Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve result reporting #4978

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Duplicati/Library/Interface/ResultInterfaces.cs
Expand Up @@ -39,6 +39,7 @@ public interface IBasicResults
IEnumerable<string> Warnings { get; }
IEnumerable<string> Messages { get; }
ParsedResultType ParsedResult { get; }
bool Interrupted { get; }
}

public interface IBackendStatstics
Expand Down
44 changes: 40 additions & 4 deletions Duplicati/Library/Main/Controller.cs
Expand Up @@ -434,6 +434,10 @@ private T RunAction<T>(T result, ref string[] paths, ref IFilter filter, Action<
if (result.EndTime.Ticks == 0)
result.EndTime = DateTime.UtcNow;
result.SetDatabase(null);
if(result is BasicResults r)
{
r.Interrupted = false;
}

OnOperationComplete(result);

Expand All @@ -447,18 +451,50 @@ private T RunAction<T>(T result, ref string[] paths, ref IFilter filter, Action<

if (ex is Library.Interface.OperationAbortException oae)
{
// Perform the module shutdown
OnOperationComplete(ex);

// Log this as a normal operation, as the script raising the exception,
// has already populated either warning or log messages as required
Logging.Log.WriteInformationMessage(LOGTAG, "AbortOperation", "Aborting operation by request, requested result: {0}", oae.AbortReason);

if (result is BasicResults basicResults)
{
basicResults.Interrupted = true;
try
{
// No operation was started in database, so write logs to new operation
using (var db = new LocalDatabase(m_options.Dbpath, result.MainOperation.ToString(), true))
{
basicResults.SetDatabase(db);
db.WriteResults();
}

OnOperationComplete(result);
}
catch { }
}
else
{
// Perform the module shutdown
OnOperationComplete(ex);
}

return result;
}
else
{
try { (result as BasicResults).OperationProgressUpdater.UpdatePhase(OperationPhase.Error); }
try
{
if (result is BasicResults basicResults)
{
basicResults.OperationProgressUpdater.UpdatePhase(OperationPhase.Error);
basicResults.Fatal = true;
// Write logs to previous operation
using (var db = new LocalDatabase(m_options.Dbpath, null, true))
{
basicResults.SetDatabase(db);
db.WriteResults();
}
}
}
catch { }

OnOperationComplete(ex);
Expand Down
25 changes: 21 additions & 4 deletions Duplicati/Library/Main/Database/LocalDatabase.cs
Expand Up @@ -65,7 +65,7 @@ protected static System.Data.IDbConnection CreateConnection(string path)
/// Creates a new database instance and starts a new operation
/// </summary>
/// <param name="path">The path to the database</param>
/// <param name="operation">The name of the operation</param>
/// <param name="operation">The name of the operation. If null, continues last operation</param>
public LocalDatabase(string path, string operation, bool shouldclose)
: this(CreateConnection(path), operation)
{
Expand All @@ -87,7 +87,7 @@ public LocalDatabase(LocalDatabase db)
/// <summary>
/// Creates a new database instance and starts a new operation
/// </summary>
/// <param name="operation">The name of the operation</param>
/// <param name="operation">The name of the operation. If null, continues last operation</param>
public LocalDatabase(System.Data.IDbConnection connection, string operation)
: this(connection)
{
Expand All @@ -97,8 +97,25 @@ public LocalDatabase(System.Data.IDbConnection connection, string operation)
if (m_connection.State != System.Data.ConnectionState.Open)
m_connection.Open();

using (var cmd = m_connection.CreateCommand())
m_operationid = cmd.ExecuteScalarInt64( @"INSERT INTO ""Operation"" (""Description"", ""Timestamp"") VALUES (?, ?); SELECT last_insert_rowid();", -1, operation, Library.Utility.Utility.NormalizeDateTimeToEpochSeconds(OperationTimestamp));
if (operation != null)
{
using (var cmd = m_connection.CreateCommand())
m_operationid = cmd.ExecuteScalarInt64(@"INSERT INTO ""Operation"" (""Description"", ""Timestamp"") VALUES (?, ?); SELECT last_insert_rowid();", -1, operation, Library.Utility.Utility.NormalizeDateTimeToEpochSeconds(OperationTimestamp));
}
else
{
// Get last operation
using (var cmd = m_connection.CreateCommand())
using (var rd = cmd.ExecuteReader(@"SELECT ""ID"", ""Timestamp"" FROM ""Operation"" ORDER BY ""Timestamp"" DESC LIMIT 1"))
{
if(!rd.Read())
{
throw new Exception("LocalDatabase does not contain a previous operation.");
}
m_operationid = rd.GetInt64(0);
OperationTimestamp = ParseFromEpochSeconds(rd.GetInt64(1));
}
}
}

private LocalDatabase(System.Data.IDbConnection connection)
Expand Down
30 changes: 27 additions & 3 deletions Duplicati/Library/Main/ResultClasses.cs
Expand Up @@ -190,6 +190,8 @@ public virtual ParsedResultType ParsedResult
{
get
{
if (Fatal)
return ParsedResultType.Fatal;
if (Errors != null && Errors.Any())
return ParsedResultType.Error;
else if (Warnings != null && Warnings.Any())
Expand All @@ -198,6 +200,9 @@ public virtual ParsedResultType ParsedResult
return ParsedResultType.Success;
}
}
public bool Interrupted { get; set; }
[JsonIgnore]
public bool Fatal { get; set; }

// ReSharper disable once UnusedMember.Global
// This is referenced in the logs.
Expand Down Expand Up @@ -593,7 +598,16 @@ public override ParsedResultType ParsedResult
{
get
{
if ((CompactResults != null && CompactResults.ParsedResult == ParsedResultType.Error) ||
if ((CompactResults != null && CompactResults.ParsedResult == ParsedResultType.Fatal) ||
(VacuumResults != null && VacuumResults.ParsedResult == ParsedResultType.Fatal) ||
(DeleteResults != null && DeleteResults.ParsedResult == ParsedResultType.Fatal) ||
(RepairResults != null && RepairResults.ParsedResult == ParsedResultType.Fatal) ||
(TestResults != null && TestResults.ParsedResult == ParsedResultType.Fatal) ||
Fatal)
{
return ParsedResultType.Fatal;
}
else if ((CompactResults != null && CompactResults.ParsedResult == ParsedResultType.Error) ||
(VacuumResults != null && VacuumResults.ParsedResult == ParsedResultType.Error) ||
(DeleteResults != null && DeleteResults.ParsedResult == ParsedResultType.Error) ||
(RepairResults != null && RepairResults.ParsedResult == ParsedResultType.Error) ||
Expand Down Expand Up @@ -638,7 +652,12 @@ public override ParsedResultType ParsedResult
{
get
{
if ((RecreateDatabaseResults != null && RecreateDatabaseResults.ParsedResult == ParsedResultType.Error) ||
if ((RecreateDatabaseResults != null && RecreateDatabaseResults.ParsedResult == ParsedResultType.Fatal) ||
Fatal)
{
return ParsedResultType.Fatal;
}
else if ((RecreateDatabaseResults != null && RecreateDatabaseResults.ParsedResult == ParsedResultType.Error) ||
(Errors != null && Errors.Any()))
{
return ParsedResultType.Error;
Expand Down Expand Up @@ -814,7 +833,12 @@ public override ParsedResultType ParsedResult
{
get
{
if ((RecreateDatabaseResults != null && RecreateDatabaseResults.ParsedResult == ParsedResultType.Error) ||
if ((RecreateDatabaseResults != null && RecreateDatabaseResults.ParsedResult == ParsedResultType.Fatal) ||
Fatal)
{
return ParsedResultType.Fatal;
}
else if ((RecreateDatabaseResults != null && RecreateDatabaseResults.ParsedResult == ParsedResultType.Error) ||
(Errors != null && Errors.Any()))
{
return ParsedResultType.Error;
Expand Down
16 changes: 16 additions & 0 deletions Duplicati/Server/README.md
@@ -0,0 +1,16 @@
## Update style sheets

Install [less CSS](https://lesscss.org) and minify plugin:
```
npm install less -g
npm install -g less-plugin-clean-css
```

Compile styles:
```
cd webroot/ngax
lessc less/dark.less styles/dark.css --clean-css -m=always
lessc less/default.less styles/default.css --clean-css -m=always
```

There are warnings about math=always, but to fix those all divisions in `.less` need to be wrapped in parens.
43 changes: 23 additions & 20 deletions Duplicati/Server/Runner.cs
Expand Up @@ -754,44 +754,47 @@ private static void UpdateMetadata(Duplicati.Server.Serialization.Interface.IBac
backup.Metadata["LastRestoreFinished"] = Library.Utility.Utility.SerializeDateTime(result.EndTime.ToUniversalTime());
}

if (result is IParsedBackendStatistics r2)
if (result is IParsedBackendStatistics r2 && !result.Interrupted)
{
UpdateMetadata(backup, r2);
}

if (result is IBackendStatsticsReporter r3)
if (result is IBackendStatsticsReporter r3 && !result.Interrupted)
{
if (r3.BackendStatistics is IParsedBackendStatistics statistics)
UpdateMetadata(backup, statistics);
}

if (result is ICompactResults r4)
if (result is ICompactResults r4 && !result.Interrupted)
{
UpdateMetadataLastCompact(backup, r4);

if (r4.VacuumResults != null)
UpdateMetadataLastVacuum(backup, r4.VacuumResults);
}

if (result is IVacuumResults r5)
if (result is IVacuumResults r5 && !result.Interrupted)
{
UpdateMetadataLastVacuum(backup, r5);
}

if (result is IBackupResults r)
{
backup.Metadata["SourceFilesSize"] = r.SizeOfExaminedFiles.ToString();
backup.Metadata["SourceFilesCount"] = r.ExaminedFiles.ToString();
backup.Metadata["SourceSizeString"] = Duplicati.Library.Utility.Utility.FormatSizeString(r.SizeOfExaminedFiles);
backup.Metadata["LastBackupStarted"] = Library.Utility.Utility.SerializeDateTime(r.BeginTime.ToUniversalTime());
backup.Metadata["LastBackupFinished"] = Library.Utility.Utility.SerializeDateTime(r.EndTime.ToUniversalTime());
backup.Metadata["LastBackupDuration"] = r.Duration.ToString();

if (r.CompactResults != null)
UpdateMetadataLastCompact(backup, r.CompactResults);

if (r.VacuumResults != null)
UpdateMetadataLastVacuum(backup, r.VacuumResults);
if (!result.Interrupted)
{
backup.Metadata["SourceFilesSize"] = r.SizeOfExaminedFiles.ToString();
backup.Metadata["SourceFilesCount"] = r.ExaminedFiles.ToString();
backup.Metadata["SourceSizeString"] = Duplicati.Library.Utility.Utility.FormatSizeString(r.SizeOfExaminedFiles);
backup.Metadata["LastBackupStarted"] = Library.Utility.Utility.SerializeDateTime(r.BeginTime.ToUniversalTime());
backup.Metadata["LastBackupFinished"] = Library.Utility.Utility.SerializeDateTime(r.EndTime.ToUniversalTime());
backup.Metadata["LastBackupDuration"] = r.Duration.ToString();

if (r.CompactResults != null)
UpdateMetadataLastCompact(backup, r.CompactResults);

if (r.VacuumResults != null)
UpdateMetadataLastVacuum(backup, r.VacuumResults);
}

if (r.FilesWithError > 0 || r.Warnings.Any() || r.Errors.Any())
{
Expand Down Expand Up @@ -837,19 +840,19 @@ private static void UpdateMetadata(Duplicati.Server.Serialization.Interface.IBac
);
}
}
else if (result.ParsedResult != Library.Interface.ParsedResultType.Success)
else if (result.ParsedResult != ParsedResultType.Success)
{
var type = result.ParsedResult == Library.Interface.ParsedResultType.Warning
var type = result.ParsedResult != ParsedResultType.Warning
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Jojo-1000

Err, I know I'm bit late but I suddenly wonder about this change. Why did you replace == by != exactly (same for the 2 following changes) ?
It seems wrong. And when running a repair from the Web UI with a warning, it displays a red window saying '0 errors'

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I didn't catch that. The intention was to handle fatal the same as error, so I wanted to reverse the ternary operator. Apparently I forgot to reverse the order of the condition. But now that I look at this it would have worked in the original version anyways.

? NotificationType.Warning
: NotificationType.Error;

var title = result.ParsedResult == Library.Interface.ParsedResultType.Warning
var title = result.ParsedResult != ParsedResultType.Warning
? (backup.IsTemporary ?
"Warning" : string.Format("Warning while running {0}", backup.Name))
: (backup.IsTemporary ?
"Error" : string.Format("Error while running {0}", backup.Name));

var message = result.ParsedResult == Library.Interface.ParsedResultType.Warning
var message = result.ParsedResult != ParsedResultType.Warning
? string.Format("Got {0} warning(s)", result.Warnings.Count())
: string.Format("Got {0} error(s)", result.Errors.Count());

Expand Down
4 changes: 4 additions & 0 deletions Duplicati/Server/webroot/ngax/less/base.less
Expand Up @@ -269,6 +269,10 @@ wait-area {
color: #ffcc00;
}

.fatal-color {
color: #990000;
}

ul.tabs {
margin-bottom: 10px;

Expand Down
Expand Up @@ -57,17 +57,19 @@ backupApp.controller('BackupLogController', function($scope, $routeParams, AppUt
});
};

$scope.ResultIcon = function(parsedResult) {
$scope.ResultIcon = function (parsedResult) {
if (parsedResult == 'Success') {
return 'fa fa-check-circle success-color';
} else if (parsedResult == 'Warning') {
return 'fa fa-exclamation-circle warning-color';
} else if (parsedResult == 'Error') {
return 'fa fa-times-circle error-color';
} else if (parsedResult == 'Fatal') {
return 'fa fa-exclamation-triangle fatal-color';
} else {
return 'fa fa-question-circle';
}
}
};

$scope.LoadMoreGeneralData();
$scope.Backup = BackupList.lookup[$scope.BackupID];
Expand Down