diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.cs b/src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.cs index 122556bbbe460c..b3ff45e33d9ed1 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.cs @@ -32,10 +32,16 @@ public override void Write(in LogEntry logEntry, IExternalScopeP { return; } - LogLevel logLevel = logEntry.LogLevel; - string category = logEntry.Category; - int eventId = logEntry.EventId.Id; - Exception? exception = logEntry.Exception; + + // We extract most of the work into a non-generic method to save code size. If this was left in the generic + // method, we'd get generic specialization for all TState parameters, but that's unnecessary. + WriteInternal(scopeProvider, textWriter, message, logEntry.LogLevel, logEntry.Category, logEntry.EventId.Id, logEntry.Exception, + logEntry.State != null, logEntry.State?.ToString(), logEntry.State as IReadOnlyCollection>); + } + + private void WriteInternal(IExternalScopeProvider? scopeProvider, TextWriter textWriter, string message, LogLevel logLevel, + string category, int eventId, Exception? exception, bool hasState, string? stateMessage, IReadOnlyCollection>? stateProperties) + { const int DefaultBufferSize = 1024; using (var output = new PooledByteBufferWriter(DefaultBufferSize)) { @@ -48,9 +54,9 @@ public override void Write(in LogEntry logEntry, IExternalScopeP DateTimeOffset dateTimeOffset = FormatterOptions.UseUtcTimestamp ? DateTimeOffset.UtcNow : DateTimeOffset.Now; writer.WriteString("Timestamp", dateTimeOffset.ToString(timestampFormat)); } - writer.WriteNumber(nameof(logEntry.EventId), eventId); - writer.WriteString(nameof(logEntry.LogLevel), GetLogLevelString(logLevel)); - writer.WriteString(nameof(logEntry.Category), category); + writer.WriteNumber(nameof(LogEntry.EventId), eventId); + writer.WriteString(nameof(LogEntry.LogLevel), GetLogLevelString(logLevel)); + writer.WriteString(nameof(LogEntry.Category), category); writer.WriteString("Message", message); if (exception != null) @@ -58,11 +64,11 @@ public override void Write(in LogEntry logEntry, IExternalScopeP writer.WriteString(nameof(Exception), exception.ToString()); } - if (logEntry.State != null) + if (hasState) { - writer.WriteStartObject(nameof(logEntry.State)); - writer.WriteString("Message", logEntry.State.ToString()); - if (logEntry.State is IReadOnlyCollection> stateProperties) + writer.WriteStartObject(nameof(LogEntry.State)); + writer.WriteString("Message", stateMessage); + if (stateProperties != null) { foreach (KeyValuePair item in stateProperties) { diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/SimpleConsoleFormatter.cs b/src/libraries/Microsoft.Extensions.Logging.Console/src/SimpleConsoleFormatter.cs index 308c831dd2ea81..6aba8cb172ac8b 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/SimpleConsoleFormatter.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/SimpleConsoleFormatter.cs @@ -51,7 +51,15 @@ public override void Write(in LogEntry logEntry, IExternalScopeP { return; } - LogLevel logLevel = logEntry.LogLevel; + + // We extract most of the work into a non-generic method to save code size. If this was left in the generic + // method, we'd get generic specialization for all TState parameters, but that's unnecessary. + WriteInternal(scopeProvider, textWriter, message, logEntry.LogLevel, logEntry.EventId.Id, logEntry.Exception, logEntry.Category); + } + + private void WriteInternal(IExternalScopeProvider? scopeProvider, TextWriter textWriter, string message, LogLevel logLevel, + int eventId, Exception? exception, string category) + { ConsoleColors logLevelColors = GetLogLevelConsoleColors(logLevel); string logLevelString = GetLogLevelString(logLevel); @@ -70,14 +78,8 @@ public override void Write(in LogEntry logEntry, IExternalScopeP { textWriter.WriteColoredMessage(logLevelString, logLevelColors.Background, logLevelColors.Foreground); } - CreateDefaultLogMessage(textWriter, logEntry, message, scopeProvider); - } - private void CreateDefaultLogMessage(TextWriter textWriter, in LogEntry logEntry, string message, IExternalScopeProvider? scopeProvider) - { bool singleLine = FormatterOptions.SingleLine; - int eventId = logEntry.EventId.Id; - Exception? exception = logEntry.Exception; // Example: // info: ConsoleApp.Program[10] @@ -85,7 +87,7 @@ private void CreateDefaultLogMessage(TextWriter textWriter, in LogEntry< // category and event id textWriter.Write(LoglevelPadding); - textWriter.Write(logEntry.Category); + textWriter.Write(category); textWriter.Write('['); #if NETCOREAPP diff --git a/src/libraries/Microsoft.Extensions.Logging.Console/src/SystemdConsoleFormatter.cs b/src/libraries/Microsoft.Extensions.Logging.Console/src/SystemdConsoleFormatter.cs index 5c0626307db195..2d306fee1d0a4b 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Console/src/SystemdConsoleFormatter.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Console/src/SystemdConsoleFormatter.cs @@ -41,10 +41,15 @@ public override void Write(in LogEntry logEntry, IExternalScopeP { return; } - LogLevel logLevel = logEntry.LogLevel; - string category = logEntry.Category; - int eventId = logEntry.EventId.Id; - Exception? exception = logEntry.Exception; + + // We extract most of the work into a non-generic method to save code size. If this was left in the generic + // method, we'd get generic specialization for all TState parameters, but that's unnecessary. + WriteInternal(scopeProvider, textWriter, message, logEntry.LogLevel, logEntry.Category, logEntry.EventId.Id, logEntry.Exception); + } + + private void WriteInternal(IExternalScopeProvider? scopeProvider, TextWriter textWriter, string message, LogLevel logLevel, string category, + int eventId, Exception? exception) + { // systemd reads messages from standard out line-by-line in a '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.