BeyondTech.Extensions.Logging.Timing 1.0.0

dotnet add package BeyondTech.Extensions.Logging.Timing --version 1.0.0
NuGet\Install-Package BeyondTech.Extensions.Logging.Timing -Version 1.0.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="BeyondTech.Extensions.Logging.Timing" Version="1.0.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add BeyondTech.Extensions.Logging.Timing --version 1.0.0
#r "nuget: BeyondTech.Extensions.Logging.Timing, 1.0.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install BeyondTech.Extensions.Logging.Timing as a Cake Addin
#addin nuget:?package=BeyondTech.Extensions.Logging.Timing&version=1.0.0

// Install BeyondTech.Extensions.Logging.Timing as a Cake Tool
#tool nuget:?package=BeyondTech.Extensions.Logging.Timing&version=1.0.0

Timing Extensions

This library is a direct port of @nblumhardt 's Serilog Timing for usage directly with Microsoft's ILogger. This library provides almost all the same benefits of the Serilog Timing, but expands the support for any implementation of ILogger.


dotnet add package BeyondTech.Extensions.Logging.Timing

Getting started

First, an ILogger must be available. Whether through a LoggerFactory, dependency injection, or registration of a logging provider. The extension methods are available in the BeyondTech.Extensions.Logging.Timing namespace.

using BeyondTech.Extensions.Logging.Timing;
using (logger.TimeOperation("Processing data for action: {ActionId}", action.Id))
    // Operation to time here

At the completion of the using block, a message will be written to the log like:

info: Processing data for action: 87654 completed in 109.4 ms

The operation description passed to TimeOperation() is a message template; the event written to the log extends it with " {Outcome} in {Elapsed} ms".

  • All messages logged have an Elapsed property in milliseconds
  • Outcome will always be "completed" when the TimeOperation() method is used

Operations that can either succeed or fail, or that produce a result, can be created with BeginOperation():

using (var op = logger.BeginOperation("Processing data for action: {ActionId}", action.Id))
	// Operation to time here


Using op.Complete() will produce the same kind of result as in the first example:

info: Processing data for action: 4506 completed in 22.1 ms

Additional methods on Operation allow more detailed results to be captured:

op.Complete("{AffectedRows} rows affected", rowCount);

This will create a logging scope around the outcome message with the log scope of "2 rows affected". For this to work effectively, be sure to enable logging scopes on your ILogger configuration.

If the operation is not completed by calling Complete(), it is assumed to have failed and a warning-level event will be written to the log instead:

warn: Processing data for action: 87654 abandoned in 109.4 ms

In this case the Outcome property will be "abandoned".

To suppress this message, for example when an operation turns out to be inapplicable, use op.Cancel(). Once Cancel() has been called, no event will be written by the operation on either completion or abandonment.


Timings are most useful in production, so timing events are recorded at the Information level and higher, which should generally be collected all the time.

If you truly need Verbose- or Debug-level timings, you can trigger them with the OperationAt() extension method on ILogger:

using (logger.OperationAt(LogEventLevel.Debug).TimeOperation("Preparing zip archive"))
    // ...

When a level is specified, both completion and abandonment events will use it. To configure a different abandonment level, pass the second optional parameter to the OperationAt() method.


One important usage note: because the event is not written until the completion of the using block (or call to Complete()), arguments to BeginOperation() or TimeOperation() are not captured until then; don't pass parameters to these methods that mutate during the operation.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.0 339 11/2/2022
1.0.0-pre.1 134 5/24/2022

Initial release