Location>code7788 >text

Serilog Documentation Translation Series (VIII) - Recorder Lifecycle, Reliability

Popularity:414 ℃/2024-10-11 00:23:34

01Life cycle of the logger

Serilog is for the most part "use only" and does not require much thought when creating and processing loggers. However, for the following reasons:

Some receivers (sinks) involve background processes, especially those that use the network;

Resources held by some receivers (especially file and scrolling file receivers);

As a result, certain usage patterns perform better in terms of effectiveness and reliability.

1. In all applications

The easiest way to use Serilog is through the global Log class:

 = new LoggerConfiguration()
    .(@"myapp\")
    .CreateLogger();
("Hello!");
// Your application runs, then:
();

If you do this, you only need to configure the logger once and then use it throughout the life cycle of your application.

To create a more specialized logger:

Call (...) to receive an ILogger with additional attributes attached; this does not require any special close/refresh logic, as this will be handled by the parent logger.

In a few cases, it is possible to create an additional ILogger using a separate LoggerConfiguration and pass events to the root logger using (); in this case, the following disposition logic must be followed.

2、Don't use Log

If you do not want to use a static Log class, you can create an ILogger using LoggerConfiguration.

using (var log = new LoggerConfiguration()
        .(@"myapp\")
        .CreateLogger())
{
    ("Hello again!");
    // Your app runs, then disposal of `log` flushes any buffers
}

() is not used in this case. Instead, the logger is disposed of when it is no longer needed by the application.

Only root loggers created through LoggerConfiguration need to be handled in this way. ILoggers returned from ForContext() and similar methods do not require any special handling.

3. Using IoC containers

See the Autofac Integration Example for an example of how to use injectable ILoggers with Autofac IoC containers.If you would like to update this page with instructions for other containers, please ask a question.

02Reliability

Serilog believes that, all things considered, logging has a lower priority than other application code and should never affect the operation of a running application in an avoidable way.

In practice, this mostly translates into a strategy for handling exceptions in Serilog. There are some usability compromises made in the process. This document explains what you can expect as a user of the Serilog library, and how to write code that works well with the rest of the code base if you are extending Serilog.

1. Configuration

At configuration time, i.e. when the LoggerConfiguration method is called, errors fall into two categories.

Runtime configuration error

If the receiver (sink) cannot be configured due to the runtime state of the host machine, Serilog will catch any resulting exception and write it to SelfLog (see Debugging and Diagnostics).

// X: does not exist, but this is a runtime condition
// so Serilog will not fail here.
 = new LoggerConfiguration()
    .("X:\\")
    .CreateLogger();

This strategy prevents temporary problems in the deployment environment from causing otherwise valid applications to fail.

Development time invariance violation

At configuration time, some tolerance is made for code that can never be executed efficiently (e.g., API invariant violations):

// Null is never acceptable as a filename, so
// Seriog will throw ArgumentNullException here.
 = new LoggerConfiguration()
    .(null)
    .CreateLogger();

This decision was based on two reasons:

These errors are unlikely to pass through a developer's workstation or test environment, as logging configuration occurs at startup and should always fail in the same way.

Allowing invalid values or silently ignoring them can lead to unexpected behavior, which is hard to debug and makes proper configuration of the library more difficult.

If you wish, you can wrap the logging configuration code in a try/catch structure to avoid exception propagation, but this is not recommended.

Receiver Author: The responsibility for achieving this lies with the receiver implementation itself, so it needs to be explicitly considered/handled.

2. Write log event

Events are written to the logging pipeline in stages. First the logger is called, then the event is constructed, next it is enriched, filters are applied, and finally it is passed ("emitted") to the configured receiver.

call logger

Methods on the ILogger and static Log classes silently ignore invalid arguments:

// Safely does nothing
(null);

This is done because logging statements in code paths that are executed less frequently may not be tested and therefore should not fail at runtime.

Constructing log events

Serilog may reflect the properties of any deconstructed object when constructing a log event.

If these properties throw an exception, Serilog catches the error, writes to SelfLog, and includes the error message instead of the property value in the deconstructed object.

A note on type loading

If an application is deployed without the required dependencies, the loader may fail to find/construct valid types. This is a major application configuration error that may become apparent during deconstruction or later, e.g. in the JIT phase. This is a very rare situation. In this case, Serilog will not do anything to allow the error to propagate.

decorator

The decorator adds properties to the log event. The decorator may throw exceptions: Serilog catches them and writes them to SelfLog.

Decorator Author: Serilog implements this policy itself, and the decorator should throw an exception on unexpected failure (although it is wise to avoid this for performance reasons).

(machine) filter

The filter determines which events are passed to the logging pipeline. The filter does not throw exceptions, but writes to SelfLog:

// No events will be carried through this pipeline, but no
// errors will be thrown.
 = new LoggerConfiguration()
    .(e => { throw new Exception(); })
    .()
    .CreateLogger();

Filter Author: If the filter unexpectedly throws an exception, this is considered an error - the impact on performance and functionality is significant.

Send to Receiver

Serilog catches and writes to SelfLog any exceptions raised by the receiver. typically, these are the majority of exceptions that occur during normal use of Serilog.

Receiver Author: The receiver should throw exceptions on failure.Serilog will consistently catch and handle these exceptions.

A note on non-catchable exceptions

Note that there is still a class of non-catchable exceptions that Serilog is forced to propagate, such as *Exception.

3. Asynchronous/batch network operations

Many Serilog receivers use the same underlying PeriodicBatchingSink architecture. These receivers, such as the batch Azure Table Store Receiver, CouchDB Receiver, RavenDB Receiver, and Seq Receiver (in non-persistent mode), cache log events, thereby reducing the number of network round trips required to transfer log data to a remote host.

 = new LoggerConfiguration()
    .("api/missing")
    .CreateLogger()

These receivers will not fail when writing events, but may fail when sending batches asynchronously in the background. When a batch send fails, the details are written to SelfLog.

Batches being sent are retained in memory and will be retried at progressively increasing time intervals from 5 seconds to 10 minutes. The increased time interval protects the receiver from connection flooding when it comes back online after a period of downtime.

If a batch cannot be sent after 4 attempts, the batch will be discarded and a new batch will be attempted. This prevents the logger from being clogged with "bad" events that are rejected by the receiver. Subsequent successes will allow other batches to be transmitted normally.

If two more attempts fail (6 failures in total, usually around 10 minutes), the entire buffer of waiting log events is discarded. This prevents out-of-memory errors if the log event fails to deliver for a long time.

If the connection is still broken, the buffer will be refreshed every 10 minutes until the connection is re-established.

Receiver Author: This behavior can be provided by default by deriving from PeriodicBatchingSink. If a different behavior is required, a custom ILogEventSink needs to be implemented.

classifier for sums of money: All relevant source code has been uploaded to the codebase for those who are interested./hugogoos/Planner