From 7abaafca5ded90f833153075eef9049246ce9a7a Mon Sep 17 00:00:00 2001 From: Matthew Erbs Date: Wed, 20 Jan 2016 13:43:43 +1000 Subject: [PATCH] Moved colored console sink --- ...redConsoleLoggerConfigurationExtensions.cs | 52 +++++ .../Serilog.Sinks.SystemConsole.xproj | 18 ++ .../ColoredConsole/ColoredConsoleSink.cs | 177 ++++++++++++++++++ src/Serilog.Sinks.SystemConsole/project.json | 24 +++ .../LoggerConfigurationFullNetFxExtensions.cs | 26 --- 5 files changed, 271 insertions(+), 26 deletions(-) create mode 100644 src/Serilog.Sinks.SystemConsole/ColoredConsoleLoggerConfigurationExtensions.cs create mode 100644 src/Serilog.Sinks.SystemConsole/Serilog.Sinks.SystemConsole.xproj create mode 100644 src/Serilog.Sinks.SystemConsole/Sinks/ColoredConsole/ColoredConsoleSink.cs create mode 100644 src/Serilog.Sinks.SystemConsole/project.json diff --git a/src/Serilog.Sinks.SystemConsole/ColoredConsoleLoggerConfigurationExtensions.cs b/src/Serilog.Sinks.SystemConsole/ColoredConsoleLoggerConfigurationExtensions.cs new file mode 100644 index 000000000..a7c599e20 --- /dev/null +++ b/src/Serilog.Sinks.SystemConsole/ColoredConsoleLoggerConfigurationExtensions.cs @@ -0,0 +1,52 @@ +// Copyright 2013-2016 Serilog Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using Serilog.Configuration; +using Serilog.Core; +using Serilog.Events; +using Serilog.Sinks.SystemConsole; + +namespace Serilog +{ + public static class ColoredConsoleLoggerConfigurationExtensions + { + const string DefaultConsoleOutputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}"; + + /// + /// Writes log events to , using color to differentiate + /// between levels. + /// + /// Logger sink configuration. + /// The minimum level for + /// events passed through the sink. Ignored when is specified. + /// A switch allowing the pass-through minimum level + /// to be changed at runtime. + /// A message template describing the format used to write to the sink. + /// the default is "{Timestamp} [{Level}] {Message}{NewLine}{Exception}". + /// Supplies culture-specific formatting information, or null. + /// Configuration object allowing method chaining. + public static LoggerConfiguration ColoredConsole( + this LoggerSinkConfiguration sinkConfiguration, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + string outputTemplate = DefaultConsoleOutputTemplate, + IFormatProvider formatProvider = null, + LoggingLevelSwitch levelSwitch = null) + { + if (sinkConfiguration == null) throw new ArgumentNullException(nameof(sinkConfiguration)); + if (outputTemplate == null) throw new ArgumentNullException(nameof(outputTemplate)); + return sinkConfiguration.Sink(new ColoredConsoleSink(outputTemplate, formatProvider), restrictedToMinimumLevel, levelSwitch); + } + } +} \ No newline at end of file diff --git a/src/Serilog.Sinks.SystemConsole/Serilog.Sinks.SystemConsole.xproj b/src/Serilog.Sinks.SystemConsole/Serilog.Sinks.SystemConsole.xproj new file mode 100644 index 000000000..93f396b17 --- /dev/null +++ b/src/Serilog.Sinks.SystemConsole/Serilog.Sinks.SystemConsole.xproj @@ -0,0 +1,18 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 50b24aca-d8f0-4268-a477-871b0a92a04a + Serilog + ..\..\artifacts\obj\$(MSBuildProjectName) + ..\..\artifacts\bin\$(MSBuildProjectName)\ + + + 2.0 + + + \ No newline at end of file diff --git a/src/Serilog.Sinks.SystemConsole/Sinks/ColoredConsole/ColoredConsoleSink.cs b/src/Serilog.Sinks.SystemConsole/Sinks/ColoredConsole/ColoredConsoleSink.cs new file mode 100644 index 000000000..c7d85189a --- /dev/null +++ b/src/Serilog.Sinks.SystemConsole/Sinks/ColoredConsole/ColoredConsoleSink.cs @@ -0,0 +1,177 @@ +// Copyright 2013-2016 Serilog Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.IO; +using Serilog.Core; +using Serilog.Events; +using System.Collections.Generic; +using Serilog.Formatting.Display; +using Serilog.Parsing; + +using IPropertyDictionary = System.Collections.Generic.IReadOnlyDictionary; + +namespace Serilog.Sinks.SystemConsole +{ + class ColoredConsoleSink : ILogEventSink + { + readonly IFormatProvider _formatProvider; + + class Palette + { + public ConsoleColor Base { get; set; } + public ConsoleColor BaseText { get; set; } + public ConsoleColor Highlight { get; set; } + public ConsoleColor HighlightText { get; set; } + } + + static readonly Palette DefaultPalette = new Palette + { + Base = ConsoleColor.Black, + BaseText = ConsoleColor.Gray, + Highlight = ConsoleColor.DarkGray, + HighlightText = ConsoleColor.Gray + }; + + static readonly IDictionary LevelPalettes = new Dictionary + { + { LogEventLevel.Verbose, new Palette { Base = ConsoleColor.Black, BaseText = ConsoleColor.DarkGray, + Highlight = ConsoleColor.Black, HighlightText = ConsoleColor.Gray } }, + { LogEventLevel.Debug, new Palette { Base = ConsoleColor.Black, BaseText = ConsoleColor.Gray, + Highlight = ConsoleColor.Black, HighlightText = ConsoleColor.White } }, + { LogEventLevel.Information, new Palette { Base = ConsoleColor.Black, BaseText = ConsoleColor.White, + Highlight = ConsoleColor.DarkBlue, HighlightText = ConsoleColor.White } }, + { LogEventLevel.Warning, new Palette { Base = ConsoleColor.Black, BaseText = ConsoleColor.Yellow, + Highlight = ConsoleColor.DarkYellow, HighlightText = ConsoleColor.White } }, + { LogEventLevel.Error, new Palette { Base = ConsoleColor.Black, BaseText = ConsoleColor.Red, + Highlight = ConsoleColor.Red, HighlightText = ConsoleColor.White } }, + { LogEventLevel.Fatal, new Palette { Base = ConsoleColor.DarkRed, BaseText = ConsoleColor.White, + Highlight = ConsoleColor.Red, HighlightText = ConsoleColor.White } } + }; + + readonly object _syncRoot = new object(); + readonly MessageTemplate _outputTemplate; + + public ColoredConsoleSink(string outputTemplate, IFormatProvider formatProvider) + { + if (outputTemplate == null) throw new ArgumentNullException(nameof(outputTemplate)); + _outputTemplate = new MessageTemplateParser().Parse(outputTemplate); + _formatProvider = formatProvider; + } + + const string StackFrameLinePrefix = " "; + + public void Emit(LogEvent logEvent) + { + if (logEvent == null) throw new ArgumentNullException(nameof(logEvent)); + + var outputProperties = OutputProperties.GetOutputProperties(logEvent); + var palette = GetPalette(logEvent.Level); + var output = Console.Out; + + lock (_syncRoot) + { + try + { + foreach (var outputToken in _outputTemplate.Tokens) + { + var propertyToken = outputToken as PropertyToken; + if (propertyToken == null) + { + RenderOutputToken(palette, outputToken, outputProperties, output); + } + else switch (propertyToken.PropertyName) + { + case OutputProperties.MessagePropertyName: + RenderMessageToken(logEvent, palette, output); + break; + case OutputProperties.ExceptionPropertyName: + RenderExceptionToken(palette, propertyToken, outputProperties, output); + break; + default: + RenderOutputToken(palette, outputToken, outputProperties, output); + break; + } + } + } + finally { Console.ResetColor(); } + } + } + + void RenderExceptionToken(Palette palette, MessageTemplateToken outputToken, IPropertyDictionary outputProperties, TextWriter output) + { + var sw = new StringWriter(); + outputToken.Render(outputProperties, sw, _formatProvider); + var lines = new StringReader(sw.ToString()); + string nextLine; + while ((nextLine = lines.ReadLine()) != null) + { + if (nextLine.StartsWith(StackFrameLinePrefix)) + SetBaseColors(palette); + else + SetHighlightColors(palette); + output.WriteLine(nextLine); + } + } + + void RenderMessageToken(LogEvent logEvent, Palette palette, TextWriter output) + { + foreach (var messageToken in logEvent.MessageTemplate.Tokens) + { + var messagePropertyToken = messageToken as PropertyToken; + if (messagePropertyToken != null) + { + SetHighlightColors(palette); + messageToken.Render(logEvent.Properties, output, _formatProvider); + } + else + { + SetBaseColors(palette); + messageToken.Render(logEvent.Properties, output, _formatProvider); + } + } + } + + void RenderOutputToken(Palette palette, MessageTemplateToken outputToken, IPropertyDictionary outputProperties, TextWriter output) + { + SetBaseColors(palette); + outputToken.Render(outputProperties, output, _formatProvider); + } + + static Palette GetPalette(LogEventLevel level) + { + Palette palette; + if (!LevelPalettes.TryGetValue(level, out palette)) + palette = DefaultPalette; + + return palette; + } + + static void SetBaseColors(Palette palette) + { + SetColors(palette.Base, palette.BaseText); + } + + static void SetHighlightColors(Palette palette) + { + SetColors(palette.Highlight, palette.HighlightText); + } + + static void SetColors(ConsoleColor background, ConsoleColor foreground) + { + Console.BackgroundColor = background; + Console.ForegroundColor = foreground; + } + } +} \ No newline at end of file diff --git a/src/Serilog.Sinks.SystemConsole/project.json b/src/Serilog.Sinks.SystemConsole/project.json new file mode 100644 index 000000000..1df5ab3e7 --- /dev/null +++ b/src/Serilog.Sinks.SystemConsole/project.json @@ -0,0 +1,24 @@ +{ + "version": "2.0.0-beta-*", + "description": "The colored console sink for Serilog", + "authors": [ "Serilog Contributors" ], + "tags": [ "serilog", "console", "coloredconsole" ], + "projectUrl": "http://serilog.net", + "licenseUrl": "http://www.apache.org/licenses/LICENSE-2.0", + "iconUrl": "http://serilog.net/images/serilog-sink-nuget.png", + "dependencies": { + "Serilog": { "target": "project" } + }, + "compilationOptions": { + "keyFile": "../../assets/Serilog.snk" + }, + "frameworks": { + "net45": { + }, + "dotnet5.1": { + "dependencies": { + "System.Console": "4.0.0-beta-23516" + } + } + } +} \ No newline at end of file diff --git a/src/Serilog/LoggerConfigurationFullNetFxExtensions.cs b/src/Serilog/LoggerConfigurationFullNetFxExtensions.cs index acb5a403d..0a521a095 100644 --- a/src/Serilog/LoggerConfigurationFullNetFxExtensions.cs +++ b/src/Serilog/LoggerConfigurationFullNetFxExtensions.cs @@ -44,32 +44,6 @@ public static class LoggerConfigurationFullNetFxExtensions public const string DefaultOutputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}"; public const long DefaultFileSizeLimitBytes = 1L * 1024 * 1024 * 1024; const string DefaultConsoleOutputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}"; - - /// - /// Writes log events to , using color to differentiate - /// between levels. - /// - /// Logger sink configuration. - /// The minimum level for - /// events passed through the sink. Ignored when is specified. - /// A switch allowing the pass-through minimum level - /// to be changed at runtime. - /// A message template describing the format used to write to the sink. - /// the default is "{Timestamp} [{Level}] {Message}{NewLine}{Exception}". - /// Supplies culture-specific formatting information, or null. - /// Configuration object allowing method chaining. - public static LoggerConfiguration ColoredConsole( - this LoggerSinkConfiguration sinkConfiguration, - LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, - string outputTemplate = DefaultConsoleOutputTemplate, - IFormatProvider formatProvider = null, - LoggingLevelSwitch levelSwitch = null) - { - if (sinkConfiguration == null) throw new ArgumentNullException(nameof(sinkConfiguration)); - if (outputTemplate == null) throw new ArgumentNullException(nameof(outputTemplate)); - return sinkConfiguration.Sink(new ColoredConsoleSink(outputTemplate, formatProvider), restrictedToMinimumLevel, levelSwitch); - } - #if LOGCONTEXT ///