Skip to content

Instantly share code, notes, and snippets.

@JunTaoLuo
Last active February 22, 2020 01:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JunTaoLuo/7fe5bb2fc01a9b8f8ce65d81ad1f5f9b to your computer and use it in GitHub Desktop.
Save JunTaoLuo/7fe5bb2fc01a9b8f8ce65d81ad1f5f9b to your computer and use it in GitHub Desktop.

Logging Formatter API

Goals

We want to add the capability to control the format of the logs produced by our console logger. Currently, there is an option to choose between Default (logging on multiple lines with colors) and Systemd. We would like to add a compact format which will log using a single line, as well as a JSON based log format. We would also like to add an extension point so that users can specify their own custom log format. The motivation for these log formats can be found at https://github.com/dotnet/extensions/issues/2479. Finally, we need to be able to control the formatter used as well as the formatters via Configuration.

Scope

We are considering limiting the scope of the formatter API to Logging.Console for now, instead of Logging.Abstractions.

Though it's possible to extend the use of the API to other logging packages such as Logging.Debug, the current lack of user interest and specific use cases makes it difficult to justify why it's needed outside of Logging.Console. We could also move the abstraction to Logging.Abstractions in the future if needed.

ILogFormatter

A new interface will be added:

    public interface ILogFormatter
    {
        string Name { get; }
        LogMessageEntry Format(LogLevel logLevel, string logName, int eventId, TState state, Exception exception, Func<TState, Exception, string> formatter);
    }

The Name property is used to resolve custom formatters from DI.

Format will be responsible for producing the formatted log message. Alternatively, we could consider encapsulating the arguments in a separate type. It would simplify the interface to:

    string Format(LogEvent logEvent);

Note that the formatter func, Exception and State are on the interface to avoid boxing on the foreground thread for performance reasons.

One benefit would be that we can more easily add additional arguments on the type in the future to support additional formatting functionalities such as Serilog's Enrichers.

LogMessageEntry's accessibility will be changed from internal to public.

The LogMessageEntry struct that will be exposed has the following fields:

    public readonly struct LogMessageEntry
    {
        public readonly string TimeStamp;
        public readonly string LevelString;
        public readonly ConsoleColor? LevelBackground;
        public readonly ConsoleColor? LevelForeground;
        public readonly ConsoleColor? MessageColor;
        public readonly string Message;
        public readonly bool LogAsError;
    }

We can choose to expose a subset of the fields such Message and LogAsError and keep the rest as internal if we do not expect custom log formatters to control the colors of logs.

We also briefly considered changing the API more dramatically, for example forgoing LogMessageEntry entirely and pass an IConsole where the formatter can explicitly call void Write(string message, ConsoleColor? background, ConsoleColor? foreground); directly. Though technically feasible it's not clear if this would bring any significant advantages, given that the internal interface IConsole will need to be made public.

There will 4 implementations of LogFormatters that we will include in-box:

  1. DefaultConsoleLogFormatter Mirroring the current implementation of ConsoleLoggerFormat.Default
  2. SystemdConsoleLogFormatter Mirroring the current implementation of ConsoleLoggerFormat.Systemd
  3. CompactConsoleLogFormatter Similar to DefaultConsoleLogFormatter but outputs to a single line
  4. JsonConsoleLogFormatter Outputs JSON logs using the AppServiceAppLog schema

ConsoleLoggerOptions

Each formatter will have its own options class, for example JsonConsoleLogFormatterOptions and most of the options on ConsoleLoggerOptions will be relocated to it. Note that we will mark existing options on ConsoleLoggerOptions as obsolete but will maintain back-compat

DisableColors: Move to relevant formatter options such as DefaultConsoleLogFormatter and CompactConsoleLogFormatter

IncludeScopes: Move to *FormatterOptions

LogToStandardErrorThreshold: Move to *FormatterOptions, though this option may make sense to remain on ConsoleLoggerOptions

UseUtcTimestamp : Move to *FormatterOptions

Format enum will be deprecated. For back compat, if a formatter is not specified, we'll use the Format to select between DefaultConsoleLogFormatter or CompactConsoleLogFormatter

Formatter This corresponds the Name property of the formatter to be used.

TimestampFormat: Move to *FormatterOptions

Each formatter options class may contain additional options. For example, JsonConsoleLogFormatterOptions will include JsonWriterOptions.

Configuring Log Formatters

We expect that formatters will be resolved from DI which will include the 4 described above as well as potential custom formatters. In turn these formatters will resolve their own options from DI. The ConsoleLogger will choose the which formatter to use via the Formatter option and fall back to the Format option if it's not set. The layout and nesting of these options should resemble how ConsoleLoggerOptions are currently specified.

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