Currently in preview, Serilog 4.1 adds support for falling back to a second sink when the first sink fails. This scenario shows up in a few variations, usually revolving around a network-based log collector, and a local, persistent destination such as a rolling log file that’s used when the network-based collector is unavailable. Some low-volume scenarios use email logging or a Slack channel as the fallback.

Here’s what falling back to the File() sink looks like, when a network sink like Seq() is unavailable:

Log.Logger = new LoggerConfiguration()
    .WriteTo.FallbackChain(
        wt => wt.Seq("https://seq.example"),
        wt => wt.File("log.txt", rollingInterval: RollingInterval.Day)
    )
    .CreateLogger();

Super simple! Grab the 4.1-dev-* versions of Serilog now and give it a try.

Keep in mind that most network sinks will retry failed requests for some time period before reporting a failure, so it usually takes ten minutes or so for logs to show up in the fallback sink. The 4.1 release also includes some control over the maximum retry time to tighten this up, which I’ll post about here tomorrow.

Fallback chains in Serilog 4.1 are built on another new feature, ILoggingFailureListener.

public interface ILoggingFailureListener
{
    void OnLoggingFailed(
        object sender,
        LoggingFailureKind kind,
        string message,
        IReadOnlyCollection<LogEvent>? events,
        Exception? exception);
}

public enum LoggingFailureKind
{
    Temporary,
    Permanent,
    Final
}

Failure listeners can try to record events elsewhere, or just report failures through metrics or some other mechanism. The WriteTo.FallbackChain() method uses failure listeners to move failed events from one sink to another.

You can implement an ILoggingFailureListener yourself, and hook it up directly, using WriteTo.Fallible():

// Implements `ILoggingFailureListener`
var listener = new MyFailureListener();

Log.Logger = new LoggerConfiguration()
    .WriteTo.Fallible(
        wt => wt.Seq("https://seq.example"),
        listener
    )
    .CreateLogger();

Sinks that throw exceptions from ILogEventSink.Emit() or IBatchedLogEventSink.EmitBatchAsync() get failure listener support out of the box, while other kinds of sinks can opt in by implementing ISetLoggingFailureListener.

We’ll have a stable release of Serilog 4.1 out soon. In the meantime, please share any feedback you have on fallback sinks or failure listeners here, or via the Serilog issue tracker. Thanks!