Logging
See every exception the library swallows, as a log entry with the call site attached.
Waystone.Monads.Extensions.Logging — sends the exceptions the library catches to your ILogger.
What it adds
UseLoggerFactory and UseLoggerFactoryFrom on MonadOptions. Once one of them is called, every exception Option.Try, Result.Try and their async siblings swallow becomes a log entry with the call site attached.
When to reach for it
Reach for it when the library is catching exceptions you would otherwise never see. A Try that turns a throw into a None is doing its job, and it is also the one place a real bug can hide silently.
It is the only signal that needs a package. Counters and diagnostic events work with no install at all — see Observability for those.
Install it
dotnet add package Waystone.Monads.Extensions.LoggingPoint it at your logger
Do this once, during start-up. Pick the line that matches how your application is built.
You have a host and a service provider:
var app = builder.Build();
MonadOptions.Configure(options => options.UseLoggerFactoryFrom(app.Services));You have no container — a console app, a test, a worker you built by hand:
You already hold a logger:
UseLoggerFactoryFrom asks the provider for an ILoggerFactory through IServiceProvider.GetService. It takes no dependency-injection package, so any container that can hand you a provider works — not just Microsoft's. If no factory is registered it throws and tells you to call UseLoggerFactory instead.
What you get in each entry
The exception itself, plus three properties describing the call site the compiler recorded for you:
MemberName
The member that called Try
ArgumentExpression
The source text of the delegate you passed
LineNumber
The line the Try call sits on
The exception goes in the logger's exception parameter, not into properties of its own. Your OpenTelemetry logging bridge reads exception.type, exception.message and exception.stacktrace off it, so you get those for free and you get them once.
Choose the level
The default is Debug. A Try that hands back a None or an Err did what you asked it to, so warning about it fills your logs with noise.
Pass a different level if you disagree:
Filter the library's own output
UseLoggerFactory and UseLoggerFactoryFrom create a logger in the Waystone.Monads category, so you can turn the library up or down without touching anything else:
UseLogger does not do this — the logger you pass keeps whatever category it already had.
Change the logger for one block
Both the logger and the level live on the MonadOptions scope, so BeginScope redirects them for one asynchronous flow and leaves the rest of your process alone:
This is what makes the logging usable in tests that run in parallel.
Replacing UseExceptionLogger
MonadOptions.UseExceptionLogger used to be the only way to see these exceptions. It was obsolete from 6.7.0 and 7.0.0 removes it.
Do not configure both. They both still fire in 6.x, so every handled exception is reported twice until you delete the old call.
The old hook held one delegate. A second observer replaced the first without saying so, which meant it could never support more than one integration at a time. The diagnostic event has no such limit.
What it does not do
It does not log anything you handle yourself. Only the exceptions the library catches reach it.
It does not replace
Observability. Counters and the diagnostic events are in the core package and need no install.It does not accept more than one logger factory. The last call wins, and a scope overrides it for the block.
Last updated
Was this helpful?