Skip to content

Commit

Permalink
Improved exception logging to the console by making sure internal can…
Browse files Browse the repository at this point in the history
…cellations and timeout exceptions are logged
  • Loading branch information
daveaglick committed Jun 5, 2023
1 parent 2069085 commit 1de2117
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 10 deletions.
1 change: 1 addition & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# 1.0.0-beta.68

- Improved the `HttpClient.SendWithRetryAsync()` extension to log retries at the information level since they may indicate other problems, and to retry during internal `HttpClient` timeouts.
- Improved exception logging to the console by making sure internal cancellations and timeout exceptions are logged (previously they were silent, which could create the impression nothing went wrong).

# 1.0.0-beta.67

Expand Down
10 changes: 8 additions & 2 deletions src/core/Statiq.App/Commands/EngineManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,16 @@ public async Task<ExitCode> ExecuteAsync(CancellationTokenSource cancellationTok
}
catch (Exception ex)
{
// If this was a cancellation, check if it was due to a timeout or an actual cancellation
if (ex is OperationCanceledException)
{
// No log message for cancellation
return ExitCode.OperationCanceled;
// Was this a "true" cancellation of the engine?
if (Engine.CancellationToken.IsCancellationRequested)
{
_logger.LogInformation("Exited due to engine cancellation request");
return ExitCode.OperationCanceled;
}
_logger.LogCritical("Operation/task timeout occurred or internal cancellation was triggered, inner exception(s) follow (if available)");
}

// Log exceptions not already logged (including those thrown by the engine directly)
Expand Down
8 changes: 7 additions & 1 deletion src/core/Statiq.Common/Util/ExceptionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

namespace Statiq.Common
Expand All @@ -20,6 +21,11 @@ public static IEnumerable<Exception> Unwrap(this Exception exception, bool unwra
return invocationException.InnerException.Unwrap(unwrapLoggedExceptions);
case LoggedException loggedException when unwrapLoggedExceptions:
return loggedException.InnerException.Unwrap(true);
case TaskCanceledException taskCanceledException:
// Timeouts will throw this and may have an inner exception (I.e. https://stackoverflow.com/a/66694886)
return taskCanceledException.InnerException is object
? taskCanceledException.InnerException.Unwrap(unwrapLoggedExceptions)
: new Exception[] { taskCanceledException };
default:
return new Exception[] { exception };
}
Expand All @@ -28,4 +34,4 @@ public static IEnumerable<Exception> Unwrap(this Exception exception, bool unwra
return Array.Empty<Exception>();
}
}
}
}
30 changes: 23 additions & 7 deletions src/core/Statiq.Core/Execution/PipelinePhase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,19 +108,35 @@ public async Task ExecuteAsync(
catch (Exception ex)
{
Outputs = ImmutableArray<IDocument>.Empty;
if (!(ex is OperationCanceledException))

// Report on the exception if it's not a "true" cancellation of the engine (I.e. a timeout or internal cancellation)
if (!engine.CancellationToken.IsCancellationRequested)
{
string exceptionType = "Exception";
Exception exceptionToLog = ex;
LoggedException executeModulesException = ex as LoggedException;
if (executeModulesException is object)

// Was this a timeout (already tested that IsCancellationRequested is false)
if (ex is OperationCanceledException)
{
exceptionType = "Timeout/Cancellation";
exceptionToLog = ex.InnerException ?? ex;
}
else if (executeModulesException is object)
{
ex = executeModulesException.InnerException;
// ...or was it a logged exception
exceptionToLog = executeModulesException.InnerException;
}
_logger.LogDebug($"Exception while executing pipeline {PipelineName}/{Phase}: {ex}");
if (executeModulesException is object)

// Log the exception (or inner exception)
_logger.LogDebug($"{exceptionType} while executing pipeline {PipelineName}/{Phase}: {exceptionToLog}");
if (executeModulesException is object && exceptionToLog is object)
{
throw executeModulesException.InnerException;
throw exceptionToLog;
}
}

// Always rethrow the exception
throw;
}
finally
Expand Down Expand Up @@ -233,4 +249,4 @@ private static void DisposeModules(IEnumerable<IModule> modules)
}
}
}
}
}

0 comments on commit 1de2117

Please sign in to comment.