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.
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.
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:
DefaultConsoleLogFormatter
Mirroring the current implementation of ConsoleLoggerFormat.DefaultSystemdConsoleLogFormatter
Mirroring the current implementation of ConsoleLoggerFormat.SystemdCompactConsoleLogFormatter
Similar toDefaultConsoleLogFormatter
but outputs to a single lineJsonConsoleLogFormatter
Outputs JSON logs using the AppServiceAppLog schema
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
.
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.