上一篇介绍了netcore的日志系统,接下来看下netcore里面提供了哪写日志功能:
一、 DebugLogger
/// <summary> /// A logger that writes messages in the debug output window only when a debugger is attached. /// </summary> internal partial class DebugLogger : ILogger { private readonly string _name; /// <summary> /// Initializes a new instance of the <see cref="DebugLogger"/> class. /// </summary> /// <param name="name">The name of the logger.</param> public DebugLogger(string name) { _name = name; } /// <inheritdoc /> public IDisposable BeginScope<TState>(TState state) { return NullScope.Instance; } /// <inheritdoc /> public bool IsEnabled(LogLevel logLevel) { // If the filter is null, everything is enabled // unless the debugger is not attached return Debugger.IsAttached && logLevel != LogLevel.None; } /// <inheritdoc /> public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) { if (!IsEnabled(logLevel)) { return; } if (formatter == null) { throw new ArgumentNullException(nameof(formatter)); } var message = formatter(state, exception); if (string.IsNullOrEmpty(message)) { return; } message = $"{ logLevel }: {message}"; if (exception != null) { message += Environment.NewLine + Environment.NewLine + exception; } DebugWriteLine(message, _name); } }
/// <summary> /// The provider for the <see cref="DebugLogger"/>. /// </summary> [ProviderAlias("Debug")] public class DebugLoggerProvider : ILoggerProvider { /// <inheritdoc /> public ILogger CreateLogger(string name) { return new DebugLogger(name); } public void Dispose() { } }
扩展方法:
/// <summary> /// Extension methods for the <see cref="ILoggerFactory"/> class. /// </summary> public static class DebugLoggerFactoryExtensions { /// <summary> /// Adds a debug logger named 'Debug' to the factory. /// </summary> /// <param name="builder">The extension method argument.</param> public static ILoggingBuilder AddDebug(this ILoggingBuilder builder) { builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, DebugLoggerProvider>()); return builder; } }
二、 ConsoleLogger
internal class ConsoleLogger : ILogger { private static readonly string _loglevelPadding = ": "; private static readonly string _messagePadding; private static readonly string _newLineWithMessagePadding; // ConsoleColor does not have a value to specify the 'Default' color private readonly ConsoleColor? DefaultConsoleColor = null; private readonly string _name; private readonly ConsoleLoggerProcessor _queueProcessor; [ThreadStatic] private static StringBuilder _logBuilder; static ConsoleLogger() { var logLevelString = GetLogLevelString(LogLevel.Information); _messagePadding = new string(' ', logLevelString.Length + _loglevelPadding.Length); _newLineWithMessagePadding = Environment.NewLine + _messagePadding; } internal ConsoleLogger(string name, ConsoleLoggerProcessor loggerProcessor) { if (name == null) { throw new ArgumentNullException(nameof(name)); } _name = name; _queueProcessor = loggerProcessor; } internal IExternalScopeProvider ScopeProvider { get; set; } internal ConsoleLoggerOptions Options { get; set; } public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) { if (!IsEnabled(logLevel)) { return; } if (formatter == null) { throw new ArgumentNullException(nameof(formatter)); } var message = formatter(state, exception); if (!string.IsNullOrEmpty(message) || exception != null) { WriteMessage(logLevel, _name, eventId.Id, message, exception); } } public virtual void WriteMessage(LogLevel logLevel, string logName, int eventId, string message, Exception exception) { var format = Options.Format; Debug.Assert(format >= ConsoleLoggerFormat.Default && format <= ConsoleLoggerFormat.Systemd); var logBuilder = _logBuilder; _logBuilder = null; if (logBuilder == null) { logBuilder = new StringBuilder(); } LogMessageEntry entry; if (format == ConsoleLoggerFormat.Default) { entry = CreateDefaultLogMessage(logBuilder, logLevel, logName, eventId, message, exception); } else if (format == ConsoleLoggerFormat.Systemd) { entry = CreateSystemdLogMessage(logBuilder, logLevel, logName, eventId, message, exception); } else { entry = default; } _queueProcessor.EnqueueMessage(entry); logBuilder.Clear(); if (logBuilder.Capacity > 1024) { logBuilder.Capacity = 1024; } _logBuilder = logBuilder; } private LogMessageEntry CreateDefaultLogMessage(StringBuilder logBuilder, LogLevel logLevel, string logName, int eventId, string message, Exception exception) { // Example: // INFO: ConsoleApp.Program[10] // Request received var logLevelColors = GetLogLevelConsoleColors(logLevel); var logLevelString = GetLogLevelString(logLevel); // category and event id logBuilder.Append(_loglevelPadding); logBuilder.Append(logName); logBuilder.Append("["); logBuilder.Append(eventId); logBuilder.AppendLine("]"); // scope information GetScopeInformation(logBuilder, multiLine: true); if (!string.IsNullOrEmpty(message)) { // message logBuilder.Append(_messagePadding); var len = logBuilder.Length; logBuilder.AppendLine(message); logBuilder.Replace(Environment.NewLine, _newLineWithMessagePadding, len, message.Length); } // Example: // System.InvalidOperationException // at Namespace.Class.Function() in File:line X if (exception != null) { // exception message logBuilder.AppendLine(exception.ToString()); } var timestampFormat = Options.TimestampFormat; return new LogMessageEntry( message: logBuilder.ToString(), timeStamp: timestampFormat != null ? DateTime.Now.ToString(timestampFormat) : null, levelString: logLevelString, levelBackground: logLevelColors.Background, levelForeground: logLevelColors.Foreground, messageColor: DefaultConsoleColor, logAsError: logLevel >= Options.LogToStandardErrorThreshold ); } private LogMessageEntry CreateSystemdLogMessage(StringBuilder logBuilder, LogLevel logLevel, string logName, int eventId, string message, Exception exception) { // systemd reads messages from standard out line-by-line in a '<pri>message' format. // newline characters are treated as message delimiters, so we must replace them. // Messages longer than the journal LineMax setting (default: 48KB) are cropped. // Example: // <6>ConsoleApp.Program[10] Request received // loglevel var logLevelString = GetSyslogSeverityString(logLevel); logBuilder.Append(logLevelString); // timestamp var timestampFormat = Options.TimestampFormat; if (timestampFormat != null) { logBuilder.Append(DateTime.Now.ToString(timestampFormat)); } // category and event id logBuilder.Append(logName); logBuilder.Append("["); logBuilder.Append(eventId); logBuilder.Append("]"); // scope information GetScopeInformation(logBuilder, multiLine: false); // message if (!string.IsNullOrEmpty(message)) { logBuilder.Append(' '); // message AppendAndReplaceNewLine(logBuilder, message); } // exception // System.InvalidOperationException at Namespace.Class.Function() in File:line X if (exception != null) { logBuilder.Append(' '); AppendAndReplaceNewLine(logBuilder, exception.ToString()); } // newline delimiter logBuilder.Append(Environment.NewLine); return new LogMessageEntry( message: logBuilder.ToString(), logAsError: logLevel >= Options.LogToStandardErrorThreshold ); static void AppendAndReplaceNewLine(StringBuilder sb, string message) { var len = sb.Length; sb.Append(message); sb.Replace(Environment.NewLine, " ", len, message.Length); } } public bool IsEnabled(LogLevel logLevel) { return logLevel != LogLevel.None; } public IDisposable BeginScope<TState>(TState state) => ScopeProvider?.Push(state) ?? NullScope.Instance; private static string GetLogLevelString(LogLevel logLevel) { switch (logLevel) { case LogLevel.Trace: return "trce"; case LogLevel.Debug: return "dbug"; case LogLevel.Information: return "info"; case LogLevel.Warning: return "warn"; case LogLevel.Error: return "fail"; case LogLevel.Critical: return "crit"; default: throw new ArgumentOutOfRangeException(nameof(logLevel)); } } private static string GetSyslogSeverityString(LogLevel logLevel) { // 'Syslog Message Severities' from https://tools.ietf.org/html/rfc5424. switch (logLevel) { case LogLevel.Trace: case LogLevel.Debug: return "<7>"; // debug-level messages case LogLevel.Information: return "<6>"; // informational messages case LogLevel.Warning: return "<4>"; // warning conditions case LogLevel.Error: return "<3>"; // error conditions case LogLevel.Critical: return "<2>"; // critical conditions default: throw new ArgumentOutOfRangeException(nameof(logLevel)); } } private ConsoleColors GetLogLevelConsoleColors(LogLevel logLevel) { if (Options.DisableColors) { return new ConsoleColors(null, null); } // We must explicitly set the background color if we are setting the foreground color, // since just setting one can look bad on the users console. switch (logLevel) { case LogLevel.Critical: return new ConsoleColors(ConsoleColor.White, ConsoleColor.Red); case LogLevel.Error: return new ConsoleColors(ConsoleColor.Black, ConsoleColor.Red); case LogLevel.Warning: return new ConsoleColors(ConsoleColor.Yellow, ConsoleColor.Black); case LogLevel.Information: return new ConsoleColors(ConsoleColor.DarkGreen, ConsoleColor.Black); case LogLevel.Debug: return new ConsoleColors(ConsoleColor.Gray, ConsoleColor.Black); case LogLevel.Trace: return new ConsoleColors(ConsoleColor.Gray, ConsoleColor.Black); default: return new ConsoleColors(DefaultConsoleColor, DefaultConsoleColor); } } private void GetScopeInformation(StringBuilder stringBuilder, bool multiLine) { var scopeProvider = ScopeProvider; if (Options.IncludeScopes && scopeProvider != null) { var initialLength = stringBuilder.Length; scopeProvider.ForEachScope((scope, state) => { var (builder, paddAt) = state; var padd = paddAt == builder.Length; if (padd) { builder.Append(_messagePadding); builder.Append("=> "); } else { builder.Append(" => "); } builder.Append(scope); }, (stringBuilder, multiLine ? initialLength : -1)); if (stringBuilder.Length > initialLength && multiLine) { stringBuilder.AppendLine(); } } } private readonly struct ConsoleColors { public ConsoleColors(ConsoleColor? foreground, ConsoleColor? background) { Foreground = foreground; Background = background; } public ConsoleColor? Foreground { get; } public ConsoleColor? Background { get; } } }
/// <summary> /// A provider of <see cref="ConsoleLogger"/> instances. /// </summary> [ProviderAlias("Console")] public class ConsoleLoggerProvider : ILoggerProvider, ISupportExternalScope { private readonly IOptionsMonitor<ConsoleLoggerOptions> _options; private readonly ConcurrentDictionary<string, ConsoleLogger> _loggers; private readonly ConsoleLoggerProcessor _messageQueue; private IDisposable _optionsReloadToken; private IExternalScopeProvider _scopeProvider = NullExternalScopeProvider.Instance; /// <summary> /// Creates an instance of <see cref="ConsoleLoggerProvider"/>. /// </summary> /// <param name="options">The options to create <see cref="ConsoleLogger"/> instances with.</param> public ConsoleLoggerProvider(IOptionsMonitor<ConsoleLoggerOptions> options) { _options = options; _loggers = new ConcurrentDictionary<string, ConsoleLogger>(); ReloadLoggerOptions(options.CurrentValue); _optionsReloadToken = _options.OnChange(ReloadLoggerOptions); _messageQueue = new ConsoleLoggerProcessor(); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { _messageQueue.Console = new WindowsLogConsole(); _messageQueue.ErrorConsole = new WindowsLogConsole(stdErr: true); } else { _messageQueue.Console = new AnsiLogConsole(new AnsiSystemConsole()); _messageQueue.ErrorConsole = new AnsiLogConsole(new AnsiSystemConsole(stdErr: true)); } } private void ReloadLoggerOptions(ConsoleLoggerOptions options) { foreach (var logger in _loggers) { logger.Value.Options = options; } } /// <inheritdoc /> public ILogger CreateLogger(string name) { return _loggers.GetOrAdd(name, loggerName => new ConsoleLogger(name, _messageQueue) { Options = _options.CurrentValue, ScopeProvider = _scopeProvider }); } /// <inheritdoc /> public void Dispose() { _optionsReloadToken?.Dispose(); _messageQueue.Dispose(); } /// <inheritdoc /> public void SetScopeProvider(IExternalScopeProvider scopeProvider) { _scopeProvider = scopeProvider; foreach (var logger in _loggers) { logger.Value.ScopeProvider = _scopeProvider; } } }
扩展方法:
public static class ConsoleLoggerExtensions { /// <summary> /// Adds a console logger named 'Console' to the factory. /// </summary> /// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param> public static ILoggingBuilder AddConsole(this ILoggingBuilder builder) { builder.AddConfiguration(); builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, ConsoleLoggerProvider>()); LoggerProviderOptions.RegisterProviderOptions<ConsoleLoggerOptions, ConsoleLoggerProvider>(builder.Services); return builder; } /// <summary> /// Adds a console logger named 'Console' to the factory. /// </summary> /// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param> /// <param name="configure">A delegate to configure the <see cref="ConsoleLogger"/>.</param> public static ILoggingBuilder AddConsole(this ILoggingBuilder builder, Action<ConsoleLoggerOptions> configure) { if (configure == null) { throw new ArgumentNullException(nameof(configure)); } builder.AddConsole(); builder.Services.Configure(configure); return builder; } }
三、 EventLogLogger
/// <summary> /// A logger that writes messages to Windows Event Log. /// </summary> internal class EventLogLogger : ILogger { private readonly string _name; private readonly EventLogSettings _settings; private readonly IExternalScopeProvider _externalScopeProvider; private const string ContinuationString = "..."; private readonly int _beginOrEndMessageSegmentSize; private readonly int _intermediateMessageSegmentSize; /// <summary> /// Initializes a new instance of the <see cref="EventLogLogger"/> class. /// </summary> /// <param name="name">The name of the logger.</param> /// <param name="settings">The <see cref="EventLogSettings"/>.</param> /// <param name="externalScopeProvider">The <see cref="IExternalScopeProvider"/>.</param> public EventLogLogger(string name, EventLogSettings settings, IExternalScopeProvider externalScopeProvider) { _name = name ?? throw new ArgumentNullException(nameof(name)); _settings = settings ?? throw new ArgumentNullException(nameof(settings)); _externalScopeProvider = externalScopeProvider; EventLog = settings.EventLog; // Examples: // 1. An error occu... // 2. ...esponse stream _beginOrEndMessageSegmentSize = EventLog.MaxMessageSize - ContinuationString.Length; // Example: // ...rred while writ... _intermediateMessageSegmentSize = EventLog.MaxMessageSize - 2 * ContinuationString.Length; } public IEventLog EventLog { get; } /// <inheritdoc /> public IDisposable BeginScope<TState>(TState state) { return _externalScopeProvider?.Push(state); } /// <inheritdoc /> public bool IsEnabled(LogLevel logLevel) { return logLevel != LogLevel.None && (_settings.Filter == null || _settings.Filter(_name, logLevel)); } /// <inheritdoc /> public void Log<TState>( LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) { if (!IsEnabled(logLevel)) { return; } if (formatter == null) { throw new ArgumentNullException(nameof(formatter)); } var message = formatter(state, exception); if (string.IsNullOrEmpty(message)) { return; } var builder = new StringBuilder() .Append("Category: ") .AppendLine(_name) .Append("EventId: ") .Append(eventId.Id) .AppendLine(); _externalScopeProvider?.ForEachScope((scope, sb) => { if (scope is IEnumerable<KeyValuePair<string, object>> properties) { foreach (var pair in properties) { sb.Append(pair.Key).Append(": ").AppendLine(pair.Value?.ToString()); } } else if (scope != null) { sb.AppendLine(scope.ToString()); } }, builder); builder.AppendLine() .AppendLine(message); if (exception != null) { builder.AppendLine().AppendLine("Exception: ").Append(exception).AppendLine(); } WriteMessage(builder.ToString(), GetEventLogEntryType(logLevel), EventLog.DefaultEventId ?? eventId.Id); } // category '0' translates to 'None' in event log private void WriteMessage(string message, EventLogEntryType eventLogEntryType, int eventId) { if (message.Length <= EventLog.MaxMessageSize) { EventLog.WriteEntry(message, eventLogEntryType, eventId, category: 0); return; } var startIndex = 0; string messageSegment = null; while (true) { // Begin segment // Example: An error occu... if (startIndex == 0) { messageSegment = message.Substring(startIndex, _beginOrEndMessageSegmentSize) + ContinuationString; startIndex += _beginOrEndMessageSegmentSize; } else { // Check if rest of the message can fit within the maximum message size // Example: ...esponse stream if ((message.Length - (startIndex + 1)) <= _beginOrEndMessageSegmentSize) { messageSegment = ContinuationString + message.Substring(startIndex); EventLog.WriteEntry(messageSegment, eventLogEntryType, eventId, category: 0); break; } else { // Example: ...rred while writ... messageSegment = ContinuationString + message.Substring(startIndex, _intermediateMessageSegmentSize) + ContinuationString; startIndex += _intermediateMessageSegmentSize; } } EventLog.WriteEntry(messageSegment, eventLogEntryType, eventId, category: 0); } } private EventLogEntryType GetEventLogEntryType(LogLevel level) { switch (level) { case LogLevel.Information: case LogLevel.Debug: case LogLevel.Trace: return EventLogEntryType.Information; case LogLevel.Warning: return EventLogEntryType.Warning; case LogLevel.Critical: case LogLevel.Error: return EventLogEntryType.Error; default: return EventLogEntryType.Information; } } }
/// <summary> /// The provider for the <see cref="EventLogLogger"/>. /// </summary> [ProviderAlias("EventLog")] public class EventLogLoggerProvider : ILoggerProvider, ISupportExternalScope { internal readonly EventLogSettings _settings; private IExternalScopeProvider _scopeProvider; /// <summary> /// Initializes a new instance of the <see cref="EventLogLoggerProvider"/> class. /// </summary> public EventLogLoggerProvider() : this(settings: null) { } /// <summary> /// Initializes a new instance of the <see cref="EventLogLoggerProvider"/> class. /// </summary> /// <param name="settings">The <see cref="EventLogSettings"/>.</param> public EventLogLoggerProvider(EventLogSettings settings) { _settings = settings ?? new EventLogSettings(); } /// <summary> /// Initializes a new instance of the <see cref="EventLogLoggerProvider"/> class. /// </summary> /// <param name="options">The <see cref="IOptions{EventLogSettings}"/>.</param> public EventLogLoggerProvider(IOptions<EventLogSettings> options) : this(options.Value) { } /// <inheritdoc /> public ILogger CreateLogger(string name) { return new EventLogLogger(name, _settings, _scopeProvider); } /// <inheritdoc /> public void Dispose() { if (_settings.EventLog is WindowsEventLog windowsEventLog) { windowsEventLog.DiagnosticsEventLog.Dispose(); } } /// <inheritdoc /> public void SetScopeProvider(IExternalScopeProvider scopeProvider) { _scopeProvider = scopeProvider; } }
扩展方法:
public static class EventLoggerFactoryExtensions { /// <summary> /// Adds an event logger named 'EventLog' to the factory. /// </summary> /// <param name="builder">The extension method argument.</param> /// <returns>The <see cref="ILoggingBuilder"/> so that additional calls can be chained.</returns> public static ILoggingBuilder AddEventLog(this ILoggingBuilder builder) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, EventLogLoggerProvider>()); return builder; } /// <summary> /// Adds an event logger. Use <paramref name="settings"/> to enable logging for specific <see cref="LogLevel"/>s. /// </summary> /// <param name="builder">The extension method argument.</param> /// <param name="settings">The <see cref="EventLogSettings"/>.</param> /// <returns>The <see cref="ILoggingBuilder"/> so that additional calls can be chained.</returns> public static ILoggingBuilder AddEventLog(this ILoggingBuilder builder, EventLogSettings settings) { if (builder == null) { throw new ArgumentNullException(nameof(builder)); } if (settings == null) { throw new ArgumentNullException(nameof(settings)); } builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider>(new EventLogLoggerProvider(settings))); return builder; } /// <summary> /// Adds an event logger. Use <paramref name="configure"/> to enable logging for specific <see cref="LogLevel"/>s. /// </summary> /// <param name="builder">The extension method argument.</param> /// <param name="configure">A delegate to configure the <see cref="EventLogSettings"/>.</param> /// <returns>The <see cref="ILoggingBuilder"/> so that additional calls can be chained.</returns> public static ILoggingBuilder AddEventLog(this ILoggingBuilder builder, Action<EventLogSettings> configure) { if (configure == null) { throw new ArgumentNullException(nameof(configure)); } builder.AddEventLog(); builder.Services.Configure(configure); return builder; } }
internal class ConsoleLogger : ILogger { private static readonly string _loglevelPadding = ": "; private static readonly string _messagePadding; private static readonly string _newLineWithMessagePadding;
// ConsoleColor does not have a value to specify the 'Default' color private readonly ConsoleColor? DefaultConsoleColor = null;
private readonly string _name; private readonly ConsoleLoggerProcessor _queueProcessor;
[ThreadStatic] private static StringBuilder _logBuilder;
static ConsoleLogger() { var logLevelString = GetLogLevelString(LogLevel.Information); _messagePadding = new string(' ', logLevelString.Length + _loglevelPadding.Length); _newLineWithMessagePadding = Environment.NewLine + _messagePadding; }
internal ConsoleLogger(string name, ConsoleLoggerProcessor loggerProcessor) { if (name == null) { throw new ArgumentNullException(nameof(name)); }
_name = name; _queueProcessor = loggerProcessor; }
internal IExternalScopeProvider ScopeProvider { get; set; }
internal ConsoleLoggerOptions Options { get; set; }
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) { if (!IsEnabled(logLevel)) { return; }
if (formatter == null) { throw new ArgumentNullException(nameof(formatter)); }
var message = formatter(state, exception);
if (!string.IsNullOrEmpty(message) || exception != null) { WriteMessage(logLevel, _name, eventId.Id, message, exception); } }
public virtual void WriteMessage(LogLevel logLevel, string logName, int eventId, string message, Exception exception) { var format = Options.Format; Debug.Assert(format >= ConsoleLoggerFormat.Default && format <= ConsoleLoggerFormat.Systemd);
var logBuilder = _logBuilder; _logBuilder = null;
if (logBuilder == null) { logBuilder = new StringBuilder(); }
LogMessageEntry entry; if (format == ConsoleLoggerFormat.Default) { entry = CreateDefaultLogMessage(logBuilder, logLevel, logName, eventId, message, exception); } else if (format == ConsoleLoggerFormat.Systemd) { entry = CreateSystemdLogMessage(logBuilder, logLevel, logName, eventId, message, exception); } else { entry = default; } _queueProcessor.EnqueueMessage(entry);
logBuilder.Clear(); if (logBuilder.Capacity > 1024) { logBuilder.Capacity = 1024; } _logBuilder = logBuilder; }
private LogMessageEntry CreateDefaultLogMessage(StringBuilder logBuilder, LogLevel logLevel, string logName, int eventId, string message, Exception exception) { // Example: // INFO: ConsoleApp.Program[10] // Request received
var logLevelColors = GetLogLevelConsoleColors(logLevel); var logLevelString = GetLogLevelString(logLevel); // category and event id logBuilder.Append(_loglevelPadding); logBuilder.Append(logName); logBuilder.Append("["); logBuilder.Append(eventId); logBuilder.AppendLine("]");
// scope information GetScopeInformation(logBuilder, multiLine: true);
if (!string.IsNullOrEmpty(message)) { // message logBuilder.Append(_messagePadding);
var len = logBuilder.Length; logBuilder.AppendLine(message); logBuilder.Replace(Environment.NewLine, _newLineWithMessagePadding, len, message.Length); }
// Example: // System.InvalidOperationException // at Namespace.Class.Function() in File:line X if (exception != null) { // exception message logBuilder.AppendLine(exception.ToString()); }
var timestampFormat = Options.TimestampFormat;
return new LogMessageEntry( message: logBuilder.ToString(), timeStamp: timestampFormat != null ? DateTime.Now.ToString(timestampFormat) : null, levelString: logLevelString, levelBackground: logLevelColors.Background, levelForeground: logLevelColors.Foreground, messageColor: DefaultConsoleColor, logAsError: logLevel >= Options.LogToStandardErrorThreshold ); }
private LogMessageEntry CreateSystemdLogMessage(StringBuilder logBuilder, LogLevel logLevel, string logName, int eventId, string message, Exception exception) { // systemd reads messages from standard out line-by-line in a '<pri>message' format. // newline characters are treated as message delimiters, so we must replace them. // Messages longer than the journal LineMax setting (default: 48KB) are cropped. // Example: // <6>ConsoleApp.Program[10] Request received
// loglevel var logLevelString = GetSyslogSeverityString(logLevel); logBuilder.Append(logLevelString);
// timestamp var timestampFormat = Options.TimestampFormat; if (timestampFormat != null) { logBuilder.Append(DateTime.Now.ToString(timestampFormat)); }
// category and event id logBuilder.Append(logName); logBuilder.Append("["); logBuilder.Append(eventId); logBuilder.Append("]");
// scope information GetScopeInformation(logBuilder, multiLine: false);
// message if (!string.IsNullOrEmpty(message)) { logBuilder.Append(' '); // message AppendAndReplaceNewLine(logBuilder, message); }
// exception // System.InvalidOperationException at Namespace.Class.Function() in File:line X if (exception != null) { logBuilder.Append(' '); AppendAndReplaceNewLine(logBuilder, exception.ToString()); }
// newline delimiter logBuilder.Append(Environment.NewLine);
return new LogMessageEntry( message: logBuilder.ToString(), logAsError: logLevel >= Options.LogToStandardErrorThreshold );
static void AppendAndReplaceNewLine(StringBuilder sb, string message) { var len = sb.Length; sb.Append(message); sb.Replace(Environment.NewLine, " ", len, message.Length); } }
public bool IsEnabled(LogLevel logLevel) { return logLevel != LogLevel.None; }
public IDisposable BeginScope<TState>(TState state) => ScopeProvider?.Push(state) ?? NullScope.Instance;
private static string GetLogLevelString(LogLevel logLevel) { switch (logLevel) { case LogLevel.Trace: return "trce"; case LogLevel.Debug: return "dbug"; case LogLevel.Information: return "info"; case LogLevel.Warning: return "warn"; case LogLevel.Error: return "fail"; case LogLevel.Critical: return "crit"; default: throw new ArgumentOutOfRangeException(nameof(logLevel)); } }
private static string GetSyslogSeverityString(LogLevel logLevel) { // 'Syslog Message Severities' from https://tools.ietf.org/html/rfc5424. switch (logLevel) { case LogLevel.Trace: case LogLevel.Debug: return "<7>"; // debug-level messages case LogLevel.Information: return "<6>"; // informational messages case LogLevel.Warning: return "<4>"; // warning conditions case LogLevel.Error: return "<3>"; // error conditions case LogLevel.Critical: return "<2>"; // critical conditions default: throw new ArgumentOutOfRangeException(nameof(logLevel)); } }
private ConsoleColors GetLogLevelConsoleColors(LogLevel logLevel) { if (Options.DisableColors) { return new ConsoleColors(null, null); }
// We must explicitly set the background color if we are setting the foreground color, // since just setting one can look bad on the users console. switch (logLevel) { case LogLevel.Critical: return new ConsoleColors(ConsoleColor.White, ConsoleColor.Red); case LogLevel.Error: return new ConsoleColors(ConsoleColor.Black, ConsoleColor.Red); case LogLevel.Warning: return new ConsoleColors(ConsoleColor.Yellow, ConsoleColor.Black); case LogLevel.Information: return new ConsoleColors(ConsoleColor.DarkGreen, ConsoleColor.Black); case LogLevel.Debug: return new ConsoleColors(ConsoleColor.Gray, ConsoleColor.Black); case LogLevel.Trace: return new ConsoleColors(ConsoleColor.Gray, ConsoleColor.Black); default: return new ConsoleColors(DefaultConsoleColor, DefaultConsoleColor); } }
private void GetScopeInformation(StringBuilder stringBuilder, bool multiLine) { var scopeProvider = ScopeProvider; if (Options.IncludeScopes && scopeProvider != null) { var initialLength = stringBuilder.Length;
scopeProvider.ForEachScope((scope, state) => { var (builder, paddAt) = state; var padd = paddAt == builder.Length; if (padd) { builder.Append(_messagePadding); builder.Append("=> "); } else { builder.Append(" => "); } builder.Append(scope); }, (stringBuilder, multiLine ? initialLength : -1));
if (stringBuilder.Length > initialLength && multiLine) { stringBuilder.AppendLine(); } } }
private readonly struct ConsoleColors { public ConsoleColors(ConsoleColor? foreground, ConsoleColor? background) { Foreground = foreground; Background = background; }
public ConsoleColor? Foreground { get; }
public ConsoleColor? Background { get; } } }