EMH.Lib.Patterns.ExceptionProcessor.Logging
3.2.24
dotnet add package EMH.Lib.Patterns.ExceptionProcessor.Logging --version 3.2.24
NuGet\Install-Package EMH.Lib.Patterns.ExceptionProcessor.Logging -Version 3.2.24
<PackageReference Include="EMH.Lib.Patterns.ExceptionProcessor.Logging" Version="3.2.24" />
<PackageVersion Include="EMH.Lib.Patterns.ExceptionProcessor.Logging" Version="3.2.24" />
<PackageReference Include="EMH.Lib.Patterns.ExceptionProcessor.Logging" />
paket add EMH.Lib.Patterns.ExceptionProcessor.Logging --version 3.2.24
#r "nuget: EMH.Lib.Patterns.ExceptionProcessor.Logging, 3.2.24"
#:package EMH.Lib.Patterns.ExceptionProcessor.Logging@3.2.24
#addin nuget:?package=EMH.Lib.Patterns.ExceptionProcessor.Logging&version=3.2.24
#tool nuget:?package=EMH.Lib.Patterns.ExceptionProcessor.Logging&version=3.2.24
EMH.Lib.Patterns.ExceptionProcessor
Consistent exception handling for .NET — behind an interface your projects can depend on without pulling in a logger.
Why
Class libraries need to handle exceptions, but they shouldn't dictate how the application logs them.
This package splits the two apart. Your libraries reference .Interface only — a tiny contract with no logging
dependency. The host application decides which implementation to register at startup. Swap the implementation and
nothing downstream changes.
┌──────────────────┐ references ┌───────────────────────┐
│ Your libraries │ ────────────────▶ │ .Interface │
└──────────────────┘ contract only │ IExceptionProcessor │
└───────────────────────┘
▲
┌──────────────────┐ │ implements
│ Host / startup │ ─── picks one ──▶ .Logging · .Console · custom
└──────────────────┘
Packages
| Package | Use it for |
|---|---|
.Interface |
The IExceptionProcessor contract and ExceptionProcessorPolicies. Reference this from libraries. |
.Logging |
Production. Logs through HippoLog with structured properties. |
.Console |
Unit tests and low-configuration hosts. Writes to System.Console, no setup required. |
dotnet add package EMH.Lib.Patterns.ExceptionProcessor.Interface # in your libraries
dotnet add package EMH.Lib.Patterns.ExceptionProcessor.Logging # in your host
Quick start
Logging implementation
HippoLog must be initialised once, before the processor is used.
HippoLog.Initialize(myAdapter); // e.g. SerilogAdapterFactory.Create()
IExceptionProcessor processor = LoggingExceptionProcessorFactory.Create();
Console implementation
IExceptionProcessor processor = ConsoleExceptionProcessorFactory.Create();
Registering with a container
container.RegisterSingleton(() => processor);
Usage
Wrap a call and let the policy decide whether the exception propagates:
// Default policy is LogAndThrowPolicy — logs, then rethrows.
this.processor.Process(() => VoidFunction());
int x = this.processor.Process(() => NonVoidFunction(param));
// Log, swallow, and carry on.
this.processor.Process(() => NonCriticalFunction(), ExceptionProcessorPolicies.LogAndResumePolicy);
// Supply a fallback instead of default(T).
string name = this.processor.Process(
() => LookupName(id),
"unknown",
ExceptionProcessorPolicies.LogAndResumePolicy);
Async equivalents mirror the synchronous ones:
var result = await this.processor.ProcessAsync(() => FetchAsync(id));
await this.processor.ProcessAsync(() => SendAsync(), ExceptionProcessorPolicies.LogAndResumePolicy);
Already inside a catch? Ask the processor what to do, or just log:
try
{
Function();
}
catch (Exception e)
{
if (this.processor.HandleException(e, ExceptionProcessorPolicies.LogAndThrowPolicy))
{
throw;
}
}
try
{
Function();
}
catch (Exception e)
{
this.processor.LogException(e); // logs only, never rethrows
}
Policies
ExceptionProcessorPolicies decides whether the exception is rethrown after logging.
| Policy | Rethrows? |
|---|---|
LogAndResumePolicy |
No — logs and continues |
None |
Yes |
LogAndThrowPolicy (default) |
Yes |
LogThrowAndAlertPolicy |
Yes |
InputOutputTierPolicy |
Yes |
BusinessTierPolicy |
Yes |
MiddleWareServiceTierPolicy |
Yes |
PresentationTierPolicy |
Yes |
ServiceHostTierPolicy |
Yes |
WcfExceptionShieldingPolicy |
Yes |
Only LogAndResumePolicy suppresses the rethrow — anything else fails safe, so an unset policy never silently
swallows an exception. When resuming, Process<TResponse> returns default(TResponse) unless you passed an
explicit fallback.
Custom handling
Override the rule by supplying your own IHandlingStrategy:
public sealed class AlwaysResumeStrategy : IHandlingStrategy
{
public bool ShouldThrow(ExceptionProcessorPolicies policy) => false;
}
IExceptionProcessor processor = LoggingExceptionProcessorFactory.Create(new AlwaysResumeStrategy());
Calling Create() with no argument returns a shared default processor. Passing a strategy returns a separate
instance using it — the shared default is left untouched.
Cancellation
The logging implementation deliberately does not log an OperationCanceledException whose cancellation token
was already cancelled — a caller cancelling its own work is expected, not an error. The throw decision is still
applied as normal. Cancellations from other causes (a timeout, say) are logged like any other exception.
Building
The solution lives in src/.
cd src
dotnet build -c Release # zero warnings enforced; StyleCop violations fail the build
dotnet test -c Release
dotnet test -c Release --collect:"XPlat Code Coverage" --results-directory ./TestResults
Components target netstandard2.0; the test project targets net10.0 and requires the .NET 10 SDK.
All dependencies resolve from nuget.org, so no private feed is needed to build — CI configures one only in order
to publish.
Versioning
v3.1 made LoggingExceptionProcessorFactory.Create honour a supplied IHandlingStrategy. Before this the
processor was cached on the first call and any strategy passed later was discarded, so the result depended on call
order. Code passing a custom strategy will now see it applied where it was previously ignored.
v3 replaced ILogger with HippoLog. Earlier releases took an ILogger at construction; HippoLog.Initialize
is now called once by the host instead.
Both implementation packages share a version number. The patch component is the CI build number, so it is not contiguous between releases.
License
MIT © Email Hippo Ltd
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. 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. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.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 was computed. |
| .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. |
-
.NETStandard 2.0
- EMH.Lib.Patterns.ExceptionProcessor.Interface (>= 2.0.5)
- EMH.Lib.Patterns.HippoLog.Core (>= 1.0.6)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
LoggingExceptionProcessorFactory.Create now honours a supplied IHandlingStrategy. Previously the processor was cached on first call and any strategy passed afterwards was discarded, so behaviour depended on call order. Callers passing a custom strategy will see it applied where it was previously ignored. Also fixes a race on the cached instance.