Skip to content

Failure listeners and fallback sinks#2108

Merged
nblumhardt merged 5 commits into
serilog:devfrom
nblumhardt:fallbacks
Oct 1, 2024
Merged

Failure listeners and fallback sinks#2108
nblumhardt merged 5 commits into
serilog:devfrom
nblumhardt:fallbacks

Conversation

@nblumhardt

@nblumhardt nblumhardt commented Aug 30, 2024

Copy link
Copy Markdown
Member

Implements #1791.

The core addition is a new interface, ILoggingFailureListener, through which sinks can report temporary or permanent logging failures.

The most direct way to consume this is hooked up as WriteTo.Fallible(), which installs a provided failure listener for a given sink:

    .WriteTo.Fallible(
        wt => wt.Email(...),
        new MyFailureListener())

Failure listeners get enough information about failures that monitoring/metrics use cases should be well handled.

The common batching infrastructure has been updated to support failure listeners, so existing sinks that either a) throw exceptions synchronously on failure, or b) use the common batching infrastructure, will automatically support failure listeners.

Sinks that don't fall into either category can implement ISetLoggingFailureListener and receive a failure listener at start-up.

The second, higher-level way to consume the new functionality is through fallback chains: the WriteTo.FallbackChain() method accepts an ordered sequence of sinks to try, and using failure listeners, will pass failed writes on to the next sink in the chain:

    .WriteTo.FallbackChain(
        // First try writing to Seq
        wt => wt.Seq(...),
        // If that fails, try email
        wt => wt.Email(...),
        // Finally, write events to a file
        wt => wt.File(...))

A few APIs get added and so the feature needs some careful scrutiny. It's possible to construe this as bloat, but it's also proven very hard to implement anything similar in a general/cross-cutting way outside the core, so I'm leaning towards pushing ahead with this. Keen to streamline and refine it as much as we can.

@nblumhardt

Copy link
Copy Markdown
Member Author

Making a note that Serilog.Sinks.Async will need an update to support this if/when merged.

@alsi-lawr

Copy link
Copy Markdown

@nblumhardt thanks for the effort that's gone into this, it's greatly appreciated.

If I understand correctly, would there essentially be 2 kinds of sinks that can be wrapped with a fallback chain:

  1. Sinks that explicitly implement the ISetFailureLoggingSink interface get passed whatever failure listener configured during logger configuration, and then must explicitly call the OnLoggingFailed method based on the LogFailureKind
  2. Sinks that don't implement the new ISetFailureLoggingSink interface get minimal support through exceptions only

Is there also support for state capture? Is that the goal of the object sender parameter of the OnLoggingFailed method?

LoggingFailureKind kind,
string message,
IReadOnlyCollection<LogEvent>? events,
Exception? exception);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason that Exception is preferred over ExceptionDispatchInfo to preserve the stack trace?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking a look!

If the failure is to be forwarded on to another Serilog sink, an Exception will be needed; ExceptionDispatchInfo is useful for re-throwing, but can't expose enough information to be useful for logging, unfortunately.

@ArieGato

Copy link
Copy Markdown
Contributor

Hi @nblumhardt,

Will it be possible to configure the Fallback sinks through configuration? Will it require a change in the Serilog.Settings.Configuration?

Cheers,
Arjan

@nblumhardt

Copy link
Copy Markdown
Member Author

Thanks for taking a look @ArieGato. The API used in the PR should work with the current feature set of Serilog.Settings.Configuration 👍

As an intermediate step, it should also be possible to configure fallbacks internally within your own sink configuration method, if you want to preserve configuration/API compatibility.

@nblumhardt

Copy link
Copy Markdown
Member Author

Planning to merge this after #2118 so we can try it out more broadly. It's a nice benefit of the switch to a built-in batching implementation that this should work with most of the existing ecosystem without any changes :-)

{
public void OnLoggingFailed(object sender, LoggingFailureKind kind, string message, IReadOnlyCollection<LogEvent>? events, Exception? exception)
{
if (events is not null && kind is LoggingFailureKind.Final or LoggingFailureKind.Permanent)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For batched sinks this will only occur after retries have been performed, causing a delay before the fallback sink will see the events.

By default I think this is okay, but we may wish to add a RetryCount or DisableRetries type of setting to BatchingOptions so that fallbacks are tried immediately when this behavior is required.

@ArieGato

ArieGato commented Oct 22, 2024

Copy link
Copy Markdown
Contributor

Thanks for taking a look @ArieGato. The API used in the PR should work with the current feature set of Serilog.Settings.Configuration 👍

As an intermediate step, it should also be possible to configure fallbacks internally within your own sink configuration method, if you want to preserve configuration/API compatibility.

hi @nblumhardt: I'm trying to configure the fallback/fallback chain sinks through appsettings.json, but I can't get it to work. Can you give an example how I can achieve this?

@nblumhardt

Copy link
Copy Markdown
Member Author

Hi @ArieGato; I suspect I got this wrong, and the params usage in the API isn't yet supported by Serilog.Settings.Configuration. I'll open a ticket over there.

@ArieGato

ArieGato commented Nov 4, 2024

Copy link
Copy Markdown
Contributor

@nblumhardt I did some investigating and I think the problem with configuring the FallackChain is partly due to the fact that the WriteTo.FallbackChain is not an extension method. This can be resolved in two ways:

  1. Change the method to an extension method so that it is more inline with configuring Sinks.
  2. Alter Serilog.Settings.Configuration to support normal non-extension methods.

What are your thoughts on this?

@nblumhardt

Copy link
Copy Markdown
Member Author

Thanks for taking a look @ArieGato. There's already a mechanism in Serilog.Settings.Configuration to deal with these:

https://github.com/serilog/serilog-settings-configuration/blob/dev/src/Serilog.Settings.Configuration/Settings/Configuration/SurrogateConfigurationMethods.cs

I'm a bit short on time for the next few weeks unfortunately, but it looks like it should be a fairly quick addition.

@davidkarlsson

Copy link
Copy Markdown

What would adding support for this to Serilog.Settings.Configuration look like ideally? Wouldn't it be weird to try to add FallbackChain to WriteTo somehow since that key just takes an array of sinks? Should a separate "write to with fallbacks" key be added for this instead even though it then wouldn't match the Serilog api for it?

@nblumhardt

Copy link
Copy Markdown
Member Author

@davidkarlsson I think the current mapping of C# configuration onto JSON means that the "FallbackChain" would be under "WriteTo", and then under that would be another level of "WriteTo". It's a bit awkward, really needs some tinkering with :)

@ArieGato

Copy link
Copy Markdown
Contributor

@nblumhardt I did some investigating and I think the problem with configuring the FallackChain is partly due to the fact that the WriteTo.FallbackChain is not an extension method. This can be resolved in two ways:

  1. Change the method to an extension method so that it is more inline with configuring Sinks.
  2. Alter Serilog.Settings.Configuration to support normal non-extension methods.

What are your thoughts on this?

@nblumhardt I finally got round adding support for FallbackChain and Fallable to Serilog.Settings.Configuration.

serilog/serilog-settings-configuration#474

Can you take a look at it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants