mirror of
https://github.com/discourse/discourse.git
synced 2025-02-17 01:32:44 +08:00
FEATURE: Attach backup log as upload (#13849)
Discourse automatically sends a private message after backup or restore finished. The private message used to contain the log inline even when it was very long. A very long log can create issues because the length of the post will be over the maximum allowed length of a post. When that happens, Discourse will try to create an upload with the logs. If that fails, it will trim the log and inline it.
This commit is contained in:
parent
e67670c1e4
commit
e2c415457c
|
@ -2984,9 +2984,7 @@ en:
|
||||||
|
|
||||||
Here's the log:
|
Here's the log:
|
||||||
|
|
||||||
``` text
|
|
||||||
%{logs}
|
%{logs}
|
||||||
```
|
|
||||||
|
|
||||||
backup_failed:
|
backup_failed:
|
||||||
title: "Backup Failed"
|
title: "Backup Failed"
|
||||||
|
@ -2996,9 +2994,7 @@ en:
|
||||||
|
|
||||||
Here's the log:
|
Here's the log:
|
||||||
|
|
||||||
``` text
|
|
||||||
%{logs}
|
%{logs}
|
||||||
```
|
|
||||||
|
|
||||||
restore_succeeded:
|
restore_succeeded:
|
||||||
title: "Restore Succeeded"
|
title: "Restore Succeeded"
|
||||||
|
@ -3008,9 +3004,7 @@ en:
|
||||||
|
|
||||||
Here's the log:
|
Here's the log:
|
||||||
|
|
||||||
``` text
|
|
||||||
%{logs}
|
%{logs}
|
||||||
```
|
|
||||||
|
|
||||||
restore_failed:
|
restore_failed:
|
||||||
title: "Restore Failed"
|
title: "Restore Failed"
|
||||||
|
@ -3020,9 +3014,7 @@ en:
|
||||||
|
|
||||||
Here's the log:
|
Here's the log:
|
||||||
|
|
||||||
``` text
|
|
||||||
%{logs}
|
%{logs}
|
||||||
```
|
|
||||||
|
|
||||||
bulk_invite_succeeded:
|
bulk_invite_succeeded:
|
||||||
title: "Bulk Invite Succeeded"
|
title: "Bulk Invite Succeeded"
|
||||||
|
|
|
@ -321,9 +321,8 @@ module BackupRestore
|
||||||
log "Notifying '#{@user.username}' of the end of the backup..."
|
log "Notifying '#{@user.username}' of the end of the backup..."
|
||||||
status = @success ? :backup_succeeded : :backup_failed
|
status = @success ? :backup_succeeded : :backup_failed
|
||||||
|
|
||||||
post = SystemMessage.create_from_system_user(
|
logs = Discourse::Utils.logs_markdown(@logs, user: @user)
|
||||||
@user, status, logs: Discourse::Utils.pretty_logs(@logs)
|
post = SystemMessage.create_from_system_user(@user, status, logs: logs)
|
||||||
)
|
|
||||||
|
|
||||||
if @user.id == Discourse::SYSTEM_USER_ID
|
if @user.id == Discourse::SYSTEM_USER_ID
|
||||||
post.topic.invite_group(@user, Group[:admins])
|
post.topic.invite_group(@user, Group[:admins])
|
||||||
|
|
|
@ -148,10 +148,8 @@ module BackupRestore
|
||||||
log "Notifying '#{user.username}' of the end of the restore..."
|
log "Notifying '#{user.username}' of the end of the restore..."
|
||||||
status = @success ? :restore_succeeded : :restore_failed
|
status = @success ? :restore_succeeded : :restore_failed
|
||||||
|
|
||||||
SystemMessage.create_from_system_user(
|
logs = Discourse::Utils.logs_markdown(@logger.logs, user: user)
|
||||||
user, status,
|
post = SystemMessage.create_from_system_user(user, status, logs: logs)
|
||||||
logs: Discourse::Utils.pretty_logs(@logger.logs)
|
|
||||||
)
|
|
||||||
else
|
else
|
||||||
log "Could not send notification to '#{@user_info[:username]}' " \
|
log "Could not send notification to '#{@user_info[:username]}' " \
|
||||||
"(#{@user_info[:email]}), because the user does not exist."
|
"(#{@user_info[:email]}), because the user does not exist."
|
||||||
|
|
|
@ -42,6 +42,49 @@ module Discourse
|
||||||
logs.join("\n")
|
logs.join("\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.logs_markdown(logs, user:, filename: 'log.txt')
|
||||||
|
# Reserve 250 characters for the rest of the text
|
||||||
|
max_logs_length = SiteSetting.max_post_length - 250
|
||||||
|
pretty_logs = Discourse::Utils.pretty_logs(logs)
|
||||||
|
|
||||||
|
# If logs are short, try to inline them
|
||||||
|
if pretty_logs.size < max_logs_length
|
||||||
|
return <<~TEXT
|
||||||
|
```text
|
||||||
|
#{pretty_logs}
|
||||||
|
```
|
||||||
|
TEXT
|
||||||
|
end
|
||||||
|
|
||||||
|
# Try to create an upload for the logs
|
||||||
|
upload = Dir.mktmpdir do |dir|
|
||||||
|
File.write(File.join(dir, filename), pretty_logs)
|
||||||
|
zipfile = Compression::Zip.new.compress(dir, filename)
|
||||||
|
File.open(zipfile) do |file|
|
||||||
|
UploadCreator.new(
|
||||||
|
file,
|
||||||
|
File.basename(zipfile),
|
||||||
|
type: 'backup_logs',
|
||||||
|
for_export: 'true'
|
||||||
|
).create_for(user.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if upload.persisted?
|
||||||
|
return UploadMarkdown.new(upload).attachment_markdown
|
||||||
|
else
|
||||||
|
Rails.logger.warn("Failed to upload the backup logs file: #{upload.errors.full_messages}")
|
||||||
|
end
|
||||||
|
|
||||||
|
# If logs are long and upload cannot be created, show trimmed logs
|
||||||
|
<<~TEXT
|
||||||
|
```text
|
||||||
|
...
|
||||||
|
#{pretty_logs.last(max_logs_length)}
|
||||||
|
```
|
||||||
|
TEXT
|
||||||
|
end
|
||||||
|
|
||||||
def self.atomic_write_file(destination, contents)
|
def self.atomic_write_file(destination, contents)
|
||||||
begin
|
begin
|
||||||
return if File.read(destination) == contents
|
return if File.read(destination) == contents
|
||||||
|
|
|
@ -16,4 +16,59 @@ describe BackupRestore::Backuper do
|
||||||
|
|
||||||
expect(backuper.send(:get_parameterized_title)).to eq("coding-horror")
|
expect(backuper.send(:get_parameterized_title)).to eq("coding-horror")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#notify_user' do
|
||||||
|
before do
|
||||||
|
freeze_time Time.zone.parse('2010-01-01 12:00')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'includes logs if short' do
|
||||||
|
SiteSetting.max_export_file_size_kb = 1
|
||||||
|
SiteSetting.export_authorized_extensions = "tar.gz"
|
||||||
|
|
||||||
|
silence_stdout do
|
||||||
|
backuper = BackupRestore::Backuper.new(Discourse.system_user.id)
|
||||||
|
|
||||||
|
expect { backuper.send(:notify_user) }
|
||||||
|
.to change { Topic.private_messages.count }.by(1)
|
||||||
|
.and change { Upload.count }.by(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(Topic.last.first_post.raw).to include("```text\n[2010-01-01 12:00:00] Notifying 'system' of the end of the backup...\n```")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'include upload if log is long' do
|
||||||
|
SiteSetting.max_post_length = 250
|
||||||
|
|
||||||
|
silence_stdout do
|
||||||
|
backuper = silence_stdout { BackupRestore::Backuper.new(Discourse.system_user.id) }
|
||||||
|
|
||||||
|
expect { backuper.send(:notify_user) }
|
||||||
|
.to change { Topic.private_messages.count }.by(1)
|
||||||
|
.and change { Upload.where(original_filename: "log.txt.zip").count }.by(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(Topic.last.first_post.raw).to include("[log.txt.zip|attachment]")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'includes trimmed logs if log is long and upload cannot be saved' do
|
||||||
|
SiteSetting.max_post_length = 348
|
||||||
|
SiteSetting.max_export_file_size_kb = 1
|
||||||
|
SiteSetting.export_authorized_extensions = "tar.gz"
|
||||||
|
|
||||||
|
silence_stdout do
|
||||||
|
backuper = BackupRestore::Backuper.new(Discourse.system_user.id)
|
||||||
|
|
||||||
|
1.upto(10).each do |i|
|
||||||
|
backuper.send(:log, "Line #{i}")
|
||||||
|
end
|
||||||
|
|
||||||
|
expect { backuper.send(:notify_user) }
|
||||||
|
.to change { Topic.private_messages.count }.by(1)
|
||||||
|
.and change { Upload.count }.by(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(Topic.last.first_post.raw).to include("```text\n...\n[2010-01-01 12:00:00] Line 10\n[2010-01-01 12:00:00] Notifying 'system' of the end of the backup...\n```")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue
Block a user