From 0afe3e154c732e9e6b4f21a1089d56f608650563 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 8 Apr 2015 20:37:29 +1000 Subject: [PATCH] Merge Serilog.Extras.AppSettings into the core, in line with #402 Moved in largely as-is, with some work remaining. --- Serilog.sln | 14 - ...oggerConfigurationAppSettingsExtensions.cs | 41 --- .../Properties/AssemblyInfo.cs | 12 - .../Serilog.Extras.AppSettings.csproj | 80 ------ .../Serilog.Extras.AppSettings.nuspec | 17 -- ...rConfigurationFullNetFxExtensions-net40.cs | 260 ++++++++++++++++++ .../LoggerConfigurationFullNetFxExtensions.cs | 35 ++- .../Serilog.FullNetFx-net40.csproj | 3 +- .../Serilog.FullNetFx.csproj | 3 + .../AppSettings/AppSettingsSettings.cs | 40 +++ .../KeyValuePairs/KeyValuePairSettings.cs} | 86 +++--- src/Serilog/Configuration/ILoggerSettings.cs | 28 ++ .../LoggerSettingsConfiguration.cs | 45 +++ src/Serilog/LoggerConfiguration.cs | 13 +- src/Serilog/Serilog-net40.csproj | 2 + src/Serilog/Serilog.csproj | 2 + .../Serilog.Extras.AppSettings.Tests.csproj | 81 ------ .../packages.config | 4 - test/Serilog.Tests/Serilog.Tests.csproj | 1 + .../Settings/KeyValuePairSettingsTests.cs} | 45 ++- 20 files changed, 494 insertions(+), 318 deletions(-) delete mode 100644 src/Serilog.Extras.AppSettings/LoggerConfigurationAppSettingsExtensions.cs delete mode 100644 src/Serilog.Extras.AppSettings/Properties/AssemblyInfo.cs delete mode 100644 src/Serilog.Extras.AppSettings/Serilog.Extras.AppSettings.csproj delete mode 100644 src/Serilog.Extras.AppSettings/Serilog.Extras.AppSettings.nuspec create mode 100644 src/Serilog.FullNetFx/LoggerConfigurationFullNetFxExtensions-net40.cs create mode 100644 src/Serilog.FullNetFx/Settings/AppSettings/AppSettingsSettings.cs rename src/{Serilog.Extras.AppSettings/Extras/AppSettings/PrefixedAppSettingsReader.cs => Serilog.FullNetFx/Settings/KeyValuePairs/KeyValuePairSettings.cs} (64%) create mode 100644 src/Serilog/Configuration/ILoggerSettings.cs create mode 100644 src/Serilog/Configuration/LoggerSettingsConfiguration.cs delete mode 100644 test/Serilog.Extras.AppSettings.Tests/Serilog.Extras.AppSettings.Tests.csproj delete mode 100644 test/Serilog.Extras.AppSettings.Tests/packages.config rename test/{Serilog.Extras.AppSettings.Tests/PrefixedAppSettingsReaderTests.cs => Serilog.Tests/Settings/KeyValuePairSettingsTests.cs} (52%) diff --git a/Serilog.sln b/Serilog.sln index 9242be609..5da528f9e 100644 --- a/Serilog.sln +++ b/Serilog.sln @@ -24,12 +24,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serilog.FullNetFx", "src\Se EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serilog.PerformanceTests", "test\Serilog.PerformanceTests\Serilog.PerformanceTests.csproj", "{6A6504BF-CD5B-4C9E-88EB-5BD71CE3106A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serilog.Extras.AppSettings", "src\Serilog.Extras.AppSettings\Serilog.Extras.AppSettings.csproj", "{21CAF132-BBAB-41FD-A018-EB9AE54822ED}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serilog.SmokeTest", "test\Serilog.SmokeTest\Serilog.SmokeTest.csproj", "{58563C46-B781-4799-ABD5-9745F94478FD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serilog.Extras.AppSettings.Tests", "test\Serilog.Extras.AppSettings.Tests\Serilog.Extras.AppSettings.Tests.csproj", "{67398D2A-0829-4373-ABC5-2161FEB28A05}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serilog.MsTests", "test\Serilog.MsTests\Serilog.MsTests.csproj", "{7FC9FC46-5014-4461-A448-815E6CCE21E5}" EndProject Global @@ -54,18 +50,10 @@ Global {6A6504BF-CD5B-4C9E-88EB-5BD71CE3106A}.Debug|Any CPU.Build.0 = Debug|Any CPU {6A6504BF-CD5B-4C9E-88EB-5BD71CE3106A}.Release|Any CPU.ActiveCfg = Release|Any CPU {6A6504BF-CD5B-4C9E-88EB-5BD71CE3106A}.Release|Any CPU.Build.0 = Release|Any CPU - {21CAF132-BBAB-41FD-A018-EB9AE54822ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {21CAF132-BBAB-41FD-A018-EB9AE54822ED}.Debug|Any CPU.Build.0 = Debug|Any CPU - {21CAF132-BBAB-41FD-A018-EB9AE54822ED}.Release|Any CPU.ActiveCfg = Release|Any CPU - {21CAF132-BBAB-41FD-A018-EB9AE54822ED}.Release|Any CPU.Build.0 = Release|Any CPU {58563C46-B781-4799-ABD5-9745F94478FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {58563C46-B781-4799-ABD5-9745F94478FD}.Debug|Any CPU.Build.0 = Debug|Any CPU {58563C46-B781-4799-ABD5-9745F94478FD}.Release|Any CPU.ActiveCfg = Release|Any CPU {58563C46-B781-4799-ABD5-9745F94478FD}.Release|Any CPU.Build.0 = Release|Any CPU - {67398D2A-0829-4373-ABC5-2161FEB28A05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {67398D2A-0829-4373-ABC5-2161FEB28A05}.Debug|Any CPU.Build.0 = Debug|Any CPU - {67398D2A-0829-4373-ABC5-2161FEB28A05}.Release|Any CPU.ActiveCfg = Release|Any CPU - {67398D2A-0829-4373-ABC5-2161FEB28A05}.Release|Any CPU.Build.0 = Release|Any CPU {7FC9FC46-5014-4461-A448-815E6CCE21E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7FC9FC46-5014-4461-A448-815E6CCE21E5}.Debug|Any CPU.Build.0 = Debug|Any CPU {7FC9FC46-5014-4461-A448-815E6CCE21E5}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -79,9 +67,7 @@ Global {D5648551-D19D-41E3-9FC1-E74B111EEF41} = {0D135C0C-A60B-454A-A2F4-CD74A30E04B0} {7A9E1095-167D-402A-B43D-B36B97FF183D} = {037440DE-440B-4129-9F7A-09B42D00397E} {6A6504BF-CD5B-4C9E-88EB-5BD71CE3106A} = {0D135C0C-A60B-454A-A2F4-CD74A30E04B0} - {21CAF132-BBAB-41FD-A018-EB9AE54822ED} = {037440DE-440B-4129-9F7A-09B42D00397E} {58563C46-B781-4799-ABD5-9745F94478FD} = {0D135C0C-A60B-454A-A2F4-CD74A30E04B0} - {67398D2A-0829-4373-ABC5-2161FEB28A05} = {0D135C0C-A60B-454A-A2F4-CD74A30E04B0} {7FC9FC46-5014-4461-A448-815E6CCE21E5} = {0D135C0C-A60B-454A-A2F4-CD74A30E04B0} EndGlobalSection EndGlobal diff --git a/src/Serilog.Extras.AppSettings/LoggerConfigurationAppSettingsExtensions.cs b/src/Serilog.Extras.AppSettings/LoggerConfigurationAppSettingsExtensions.cs deleted file mode 100644 index 656e1b8e8..000000000 --- a/src/Serilog.Extras.AppSettings/LoggerConfigurationAppSettingsExtensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2014 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 Serilog.Extras.AppSettings; - -namespace Serilog -{ - /// - /// Adds the ReadAppSettings() extension to . - /// - public static class LoggerConfigurationAppSettingsExtensions - { - /// - /// Reads the <appSettings> element of App.config or Web.config, searching for for keys - /// that look like: serilog:*, which are used to configure - /// the logger. To add a sink, use a key like serilog:write-to:File.path for - /// each parameter to the sink's configuration method. To add an additional assembly - /// containing sinks, use serilog:using. To set the level use - /// serilog:minimum-level. - /// - /// The logger configuration to apply configuration to. - /// An object allowing configuration to continue. - public static LoggerConfiguration ReadAppSettings(this LoggerConfiguration loggerConfiguration) - { - PrefixedAppSettingsReader.ConfigureLogger(loggerConfiguration); - return loggerConfiguration; - } - } -} - diff --git a/src/Serilog.Extras.AppSettings/Properties/AssemblyInfo.cs b/src/Serilog.Extras.AppSettings/Properties/AssemblyInfo.cs deleted file mode 100644 index a5d15a533..000000000 --- a/src/Serilog.Extras.AppSettings/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -[assembly: AssemblyTitle("Serilog.Extras.AppSettings")] -[assembly: AssemblyProduct("Serilog Support")] -[assembly: AssemblyCopyright("Copyright © Serilog Contributors 2014")] - -[assembly: InternalsVisibleTo("Serilog.Extras.AppSettings.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100fb8d13fd344a1c" + - "6fe0fe83ef33c1080bf30690765bc6eb0df26ebfdf8f21670c64265b30db09f73a0dea5b3db4c9" + - "d18dbf6d5a25af5ce9016f281014d79dc3b4201ac646c451830fc7e61a2dfd633d34c39f87b818" + - "94191652df5ac63cc40c77f3542f702bda692e6e8a9158353df189007a49da0f3cfd55eb250066" + - "b19485ec")] diff --git a/src/Serilog.Extras.AppSettings/Serilog.Extras.AppSettings.csproj b/src/Serilog.Extras.AppSettings/Serilog.Extras.AppSettings.csproj deleted file mode 100644 index 6ed1fc3c4..000000000 --- a/src/Serilog.Extras.AppSettings/Serilog.Extras.AppSettings.csproj +++ /dev/null @@ -1,80 +0,0 @@ - - - - - Debug - AnyCPU - {21CAF132-BBAB-41FD-A018-EB9AE54822ED} - Library - Properties - Serilog - Serilog.Extras.AppSettings - v4.5 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - true - bin\Debug\Serilog.Extras.AppSettings.XML - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - true - bin\Release\Serilog.Extras.AppSettings.XML - - - true - - - ..\..\assets\Serilog.snk - - - - - - - - - - - - - - - - - Properties\CommonAssemblyInfo.cs - - - - - Serilog.snk - - - Designer - - - - - {7A9E1095-167D-402A-B43D-B36B97FF183D} - Serilog.FullNetFx - - - {0915dbd9-0f7c-4439-8d9e-74c3d579b219} - Serilog - - - - - \ No newline at end of file diff --git a/src/Serilog.Extras.AppSettings/Serilog.Extras.AppSettings.nuspec b/src/Serilog.Extras.AppSettings/Serilog.Extras.AppSettings.nuspec deleted file mode 100644 index 26b3965bb..000000000 --- a/src/Serilog.Extras.AppSettings/Serilog.Extras.AppSettings.nuspec +++ /dev/null @@ -1,17 +0,0 @@ - - - - Serilog.Extras.AppSettings - $version$ - Serilog Contributors - Adds support for configuring Serilog from App.config and Web.config files - en-US - http://serilog.net - http://www.apache.org/licenses/LICENSE-2.0 - http://serilog.net/images/serilog-nuget.png - serilog xml - - - - - diff --git a/src/Serilog.FullNetFx/LoggerConfigurationFullNetFxExtensions-net40.cs b/src/Serilog.FullNetFx/LoggerConfigurationFullNetFxExtensions-net40.cs new file mode 100644 index 000000000..fb992c9c0 --- /dev/null +++ b/src/Serilog.FullNetFx/LoggerConfigurationFullNetFxExtensions-net40.cs @@ -0,0 +1,260 @@ +// Copyright 2014 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.ComponentModel; +using System.Diagnostics; +using System.Threading; +using Serilog.Configuration; +using Serilog.Debugging; +using Serilog.Enrichers; +using Serilog.Events; +using Serilog.Formatting.Display; +using Serilog.Formatting.Raw; +using Serilog.Sinks.DiagnosticTrace; +using Serilog.Sinks.IOFile; +using Serilog.Sinks.RollingFile; +using Serilog.Sinks.SystemConsole; + +namespace Serilog +{ + /// + /// Extends to add Full .NET Framework + /// capabilities. + /// + public static class LoggerConfigurationFullNetFxExtensions + { + const string DefaultOutputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}"; + const string DefaultConsoleOutputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}"; + const long DefaultFileSizeLimitBytes = 1L * 1024 * 1024 * 1024; + const int DefaultRetainedFileCountLimit = 31; // A long month of logs + + /// + /// Writes log events to . + /// + /// Logger sink configuration. + /// The minimum level for + /// events passed through the sink. + /// 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 Console( + this LoggerSinkConfiguration sinkConfiguration, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + string outputTemplate = DefaultConsoleOutputTemplate, + IFormatProvider formatProvider = null) + { + if (sinkConfiguration == null) throw new ArgumentNullException("sinkConfiguration"); + if (outputTemplate == null) throw new ArgumentNullException("outputTemplate"); + var formatter = new MessageTemplateTextFormatter(outputTemplate, formatProvider); + return sinkConfiguration.Sink(new ConsoleSink(formatter), restrictedToMinimumLevel); + } + + /// + /// Writes log events to , using color to differentiate + /// between levels. + /// + /// Logger sink configuration. + /// The minimum level for + /// events passed through the sink. + /// 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) + { + if (sinkConfiguration == null) throw new ArgumentNullException("sinkConfiguration"); + if (outputTemplate == null) throw new ArgumentNullException("outputTemplate"); + return sinkConfiguration.Sink(new ColoredConsoleSink(outputTemplate, formatProvider), restrictedToMinimumLevel); + } + + /// + /// Write log events in a simple text dump format to the specified file. + /// + /// Logger sink configuration. + /// Path to the dump file. + /// The minimum level for + /// events passed through the sink. + /// Configuration object allowing method chaining. + [Obsolete("Please use WriteTo.Sink(new FileSink(path, new RawFormatter(), null)) instead", true), EditorBrowsable(EditorBrowsableState.Never)] + public static LoggerConfiguration DumpFile( + this LoggerSinkConfiguration sinkConfiguration, + string path, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) + { + if (sinkConfiguration == null) throw new ArgumentNullException("sinkConfiguration"); + if (path == null) throw new ArgumentNullException("path"); + return sinkConfiguration.Sink(new FileSink(path, new RawFormatter(), null), restrictedToMinimumLevel); + } + + /// + /// Write log events to the specified file. + /// + /// Logger sink configuration. + /// Path to the file. + /// The minimum level for + /// events passed through the sink. + /// Supplies culture-specific formatting information, or null. + /// A message template describing the format used to write to the sink. + /// the default is "{Timestamp} [{Level}] {Message}{NewLine}{Exception}". + /// The maximum size, in bytes, to which a log file will be allowed to grow. + /// For unrestricted growth, pass null. The default is 1 GB. + /// Configuration object allowing method chaining. + /// The file will be written using the UTF-8 character set. + public static LoggerConfiguration File( + this LoggerSinkConfiguration sinkConfiguration, + string path, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + string outputTemplate = DefaultOutputTemplate, + IFormatProvider formatProvider = null, + long? fileSizeLimitBytes = DefaultFileSizeLimitBytes) + { + if (sinkConfiguration == null) throw new ArgumentNullException("sinkConfiguration"); + if (outputTemplate == null) throw new ArgumentNullException("outputTemplate"); + var formatter = new MessageTemplateTextFormatter(outputTemplate, formatProvider); + + FileSink sink; + try + { + sink = new FileSink(path, formatter, fileSizeLimitBytes); + } + catch (ArgumentException) + { + throw; + } + catch (Exception ex) + { + SelfLog.WriteLine("Unable to open file sink for {0}: {1}", path, ex); + return sinkConfiguration.Sink(new NullSink()); + } + + return sinkConfiguration.Sink(sink, restrictedToMinimumLevel); + } + + /// + /// Write log events to a series of files. Each file will be named according to + /// the date of the first log entry written to it. Only simple date-based rolling is + /// currently supported. + /// + /// Logger sink configuration. + /// String describing the location of the log files, + /// with {Date} in the place of the file date. E.g. "Logs\myapp-{Date}.log" will result in log + /// files such as "Logs\myapp-2013-10-20.log", "Logs\myapp-2013-10-21.log" and so on. + /// The minimum level for + /// events passed through the sink. + /// 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. + /// The maximum size, in bytes, to which any single log file will be allowed to grow. + /// For unrestricted growth, pass null. The default is 1 GB. + /// The maximum number of log files that will be retained, + /// including the current log file. For unlimited retention, pass null. The default is 31. + /// Configuration object allowing method chaining. + /// The file will be written using the UTF-8 character set. + public static LoggerConfiguration RollingFile( + this LoggerSinkConfiguration sinkConfiguration, + string pathFormat, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + string outputTemplate = DefaultOutputTemplate, + IFormatProvider formatProvider = null, + long? fileSizeLimitBytes = DefaultFileSizeLimitBytes, + int? retainedFileCountLimit = DefaultRetainedFileCountLimit) + { + if (sinkConfiguration == null) throw new ArgumentNullException("sinkConfiguration"); + if (outputTemplate == null) throw new ArgumentNullException("outputTemplate"); + var formatter = new MessageTemplateTextFormatter(outputTemplate, formatProvider); + var sink = new RollingFileSink(pathFormat, formatter, fileSizeLimitBytes, retainedFileCountLimit); + return sinkConfiguration.Sink(sink, restrictedToMinimumLevel); + } + + /// + /// Write log events to the . + /// + /// Logger sink configuration. + /// The minimum level for + /// events passed through the sink. + /// 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 Trace( + this LoggerSinkConfiguration sinkConfiguration, + LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, + string outputTemplate = DefaultOutputTemplate, + IFormatProvider formatProvider = null) + { + if (sinkConfiguration == null) throw new ArgumentNullException("sinkConfiguration"); + if (outputTemplate == null) throw new ArgumentNullException("outputTemplate"); + var formatter = new MessageTemplateTextFormatter(outputTemplate, formatProvider); + return sinkConfiguration.Sink(new DiagnosticTraceSink(formatter), restrictedToMinimumLevel); + } + + /// + /// Enrich log events with properties from . + /// + /// Logger enrichment configuration. + /// Configuration object allowing method chaining. + /// +#if !ASPNETCORE50 + public static LoggerConfiguration FromLogContext( + this LoggerEnrichmentConfiguration enrichmentConfiguration) + { + if (enrichmentConfiguration == null) throw new ArgumentNullException("enrichmentConfiguration"); + return enrichmentConfiguration.With(); + } +#endif + + /// + /// Enrich log events with a ThreadId property containing the current . + /// + /// Logger enrichment configuration. + /// Configuration object allowing method chaining. + /// + public static LoggerConfiguration WithThreadId( + this LoggerEnrichmentConfiguration enrichmentConfiguration) + { + if (enrichmentConfiguration == null) throw new ArgumentNullException("enrichmentConfiguration"); + return enrichmentConfiguration.With(); + } + + /// + /// Enrich log events with a ProcessId property containing the current . + /// + /// Logger enrichment configuration. + /// Configuration object allowing method chaining. + public static LoggerConfiguration WithProcessId( + this LoggerEnrichmentConfiguration enrichmentConfiguration) + { + if (enrichmentConfiguration == null) throw new ArgumentNullException("enrichmentConfiguration"); + return enrichmentConfiguration.With(); + } + + /// + /// Enrich log events with a MachineName property containing the current . + /// + /// Logger enrichment configuration. + /// Configuration object allowing method chaining. + public static LoggerConfiguration WithMachineName( + this LoggerEnrichmentConfiguration enrichmentConfiguration) + { + if (enrichmentConfiguration == null) throw new ArgumentNullException("enrichmentConfiguration"); + return enrichmentConfiguration.With(); + } + } +} diff --git a/src/Serilog.FullNetFx/LoggerConfigurationFullNetFxExtensions.cs b/src/Serilog.FullNetFx/LoggerConfigurationFullNetFxExtensions.cs index 37599b8e4..1fec93ffb 100644 --- a/src/Serilog.FullNetFx/LoggerConfigurationFullNetFxExtensions.cs +++ b/src/Serilog.FullNetFx/LoggerConfigurationFullNetFxExtensions.cs @@ -13,9 +13,9 @@ // limitations under the License. using System; +using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; -using System.IO; using System.Threading; using Serilog.Configuration; using Serilog.Debugging; @@ -23,6 +23,8 @@ using Serilog.Events; using Serilog.Formatting.Display; using Serilog.Formatting.Raw; +using Serilog.Settings.AppSettings; +using Serilog.Settings.KeyValuePairs; using Serilog.Sinks.DiagnosticTrace; using Serilog.Sinks.IOFile; using Serilog.Sinks.RollingFile; @@ -257,5 +259,36 @@ public static LoggerConfiguration WithMachineName( if (enrichmentConfiguration == null) throw new ArgumentNullException("enrichmentConfiguration"); return enrichmentConfiguration.With(); } + + /// + /// Apply settings specified in the Serilog key-value setting format to the logger configuration. + /// + /// Logger setting configuration + /// A list of key-value pairs describing logger settings. + /// Configuration object allowing method chaining. + public static LoggerConfiguration KeyValuePairs( + this LoggerSettingsConfiguration settingConfiguration, + IEnumerable> settings) + { + if (settingConfiguration == null) throw new ArgumentNullException("settingConfiguration"); + return settingConfiguration.Settings(new KeyValuePairSettings(settings)); + } + + /// + /// Reads the <appSettings> element of App.config or Web.config, searching for for keys + /// that look like: serilog:*, which are used to configure + /// the logger. To add a sink, use a key like serilog:write-to:File.path for + /// each parameter to the sink's configuration method. To add an additional assembly + /// containing sinks, use serilog:using. To set the level use + /// serilog:minimum-level. + /// + /// Logger setting configuration + /// An object allowing configuration to continue. + public static LoggerConfiguration AppSettings( + this LoggerSettingsConfiguration settingConfiguration) + { + if (settingConfiguration == null) throw new ArgumentNullException("settingConfiguration"); + return settingConfiguration.Settings(new AppSettingsSettings()); + } } } diff --git a/src/Serilog.FullNetFx/Serilog.FullNetFx-net40.csproj b/src/Serilog.FullNetFx/Serilog.FullNetFx-net40.csproj index fbf4c60f5..400279423 100644 --- a/src/Serilog.FullNetFx/Serilog.FullNetFx-net40.csproj +++ b/src/Serilog.FullNetFx/Serilog.FullNetFx-net40.csproj @@ -42,6 +42,7 @@ + @@ -52,7 +53,7 @@ - + Properties\CommonAssemblyInfo.cs diff --git a/src/Serilog.FullNetFx/Serilog.FullNetFx.csproj b/src/Serilog.FullNetFx/Serilog.FullNetFx.csproj index 6d42819af..284bd88d2 100644 --- a/src/Serilog.FullNetFx/Serilog.FullNetFx.csproj +++ b/src/Serilog.FullNetFx/Serilog.FullNetFx.csproj @@ -41,6 +41,7 @@ + @@ -56,6 +57,8 @@ Properties\CommonAssemblyInfo.cs + + diff --git a/src/Serilog.FullNetFx/Settings/AppSettings/AppSettingsSettings.cs b/src/Serilog.FullNetFx/Settings/AppSettings/AppSettingsSettings.cs new file mode 100644 index 000000000..61f706c82 --- /dev/null +++ b/src/Serilog.FullNetFx/Settings/AppSettings/AppSettingsSettings.cs @@ -0,0 +1,40 @@ +// Copyright 2014 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.Configuration; +using System.Linq; +using Serilog.Configuration; +using Serilog.Settings.KeyValuePairs; + +namespace Serilog.Settings.AppSettings +{ + class AppSettingsSettings : ILoggerSettings + { + const string SettingPrefix = "serilog:"; + + public void Configure(LoggerConfiguration loggerConfiguration) + { + if (loggerConfiguration == null) throw new ArgumentNullException("loggerConfiguration"); + + var settings = ConfigurationManager.AppSettings; + var pairs = settings.AllKeys + .Where(k => k.StartsWith(SettingPrefix)) + .ToDictionary(k => k.Substring(SettingPrefix.Length), k => settings[k]); + var keyValuePairSettings = new KeyValuePairSettings(pairs); + keyValuePairSettings.Configure(loggerConfiguration); + } + } +} diff --git a/src/Serilog.Extras.AppSettings/Extras/AppSettings/PrefixedAppSettingsReader.cs b/src/Serilog.FullNetFx/Settings/KeyValuePairs/KeyValuePairSettings.cs similarity index 64% rename from src/Serilog.Extras.AppSettings/Extras/AppSettings/PrefixedAppSettingsReader.cs rename to src/Serilog.FullNetFx/Settings/KeyValuePairs/KeyValuePairSettings.cs index ba92eeaa1..e039065d0 100644 --- a/src/Serilog.Extras.AppSettings/Extras/AppSettings/PrefixedAppSettingsReader.cs +++ b/src/Serilog.FullNetFx/Settings/KeyValuePairs/KeyValuePairSettings.cs @@ -14,8 +14,7 @@ using System; using System.Collections.Generic; -using System.Collections.Specialized; -using System.Configuration; +using System.ComponentModel; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; @@ -23,40 +22,44 @@ using Serilog.Configuration; using Serilog.Events; using Serilog.Sinks.RollingFile; -using System.ComponentModel; -namespace Serilog.Extras.AppSettings +namespace Serilog.Settings.KeyValuePairs { - class PrefixedAppSettingsReader + class KeyValuePairSettings : ILoggerSettings { - const string UsingDirective = "serilog:using"; - const string WriteToDirective = "serilog:write-to"; - const string MinimumLevelDirective = "serilog:minimum-level"; - const string EnrichWithPropertyDirective = "serilog:enrich:with-property"; + const string UsingDirective = "using"; + const string WriteToDirective = "write-to"; + const string MinimumLevelDirective = "minimum-level"; + const string EnrichWithPropertyDirective = "enrich:with-property"; + + const string UsingDirectiveFullFormPrefix = "using:"; + const string EnrichWithPropertyDirectivePrefix = "enrich:with-property:"; + + const string WriteToDirectiveRegex = @"^write-to:(?[A-Za-z0-9]*)(\.(?[A-Za-z0-9]*)){0,1}$"; - const string UsingDirectiveFullFormPrefix = "serilog:using:"; - const string EnrichWithPropertyDirectivePrefix = "serilog:enrich:with-property:"; + readonly string[] _supportedDirectives = + { + UsingDirective, + WriteToDirective, + MinimumLevelDirective, + EnrichWithPropertyDirective + }; - const string WriteToDirectiveRegex = @"^serilog:write-to:(?[A-Za-z0-9]*)(\.(?[A-Za-z0-9]*)){0,1}$"; + readonly Dictionary _settings; - public static void ConfigureLogger(LoggerConfiguration loggerConfiguration) + public KeyValuePairSettings(IEnumerable> settings) { - ConfigureLogger(loggerConfiguration, ConfigurationManager.AppSettings); + if (settings == null) throw new ArgumentNullException("settings"); + _settings = settings.ToDictionary(s => s.Key, s => s.Value); } - internal static void ConfigureLogger(LoggerConfiguration loggerConfiguration, NameValueCollection settings) + public void Configure(LoggerConfiguration loggerConfiguration) { - var supportedDirectives = new[] - { - UsingDirective, - WriteToDirective, - MinimumLevelDirective, - EnrichWithPropertyDirective - }; + if (loggerConfiguration == null) throw new ArgumentNullException("loggerConfiguration"); - var directives = settings.AllKeys - .Where(k => supportedDirectives.Any(k.StartsWith)) - .ToDictionary(k => k, k => Environment.ExpandEnvironmentVariables(settings[k])); + var directives = _settings.Keys + .Where(k => _supportedDirectives.Any(k.StartsWith)) + .ToDictionary(k => k, k => Environment.ExpandEnvironmentVariables(_settings[k])); string minimumLevelDirective; LogEventLevel minimumLevel; @@ -66,7 +69,7 @@ internal static void ConfigureLogger(LoggerConfiguration loggerConfiguration, Na loggerConfiguration.MinimumLevel.Is(minimumLevel); } - foreach (var enrichDirective in directives.Where(dir => + foreach (var enrichDirective in directives.Where(dir => dir.Key.StartsWith(EnrichWithPropertyDirectivePrefix) && dir.Key.Length > EnrichWithPropertyDirectivePrefix.Length)) { var name = enrichDirective.Key.Substring(EnrichWithPropertyDirectivePrefix.Length); @@ -76,20 +79,19 @@ internal static void ConfigureLogger(LoggerConfiguration loggerConfiguration, Na var splitWriteTo = new Regex(WriteToDirectiveRegex); var sinkDirectives = (from wt in directives - where splitWriteTo.IsMatch(wt.Key) - let match = splitWriteTo.Match(wt.Key) - let call = new { - Method = match.Groups["method"].Value, - Argument = match.Groups["argument"].Value, - wt.Value - } - group call by call.Method).ToList(); + where splitWriteTo.IsMatch(wt.Key) + let match = splitWriteTo.Match(wt.Key) + let call = new + { + Method = match.Groups["method"].Value, + Argument = match.Groups["argument"].Value, + wt.Value + } + group call by call.Method).ToList(); if (sinkDirectives.Any()) { - var extensionMethods = FindExtensionMethods(directives); - - var sinkConfigurationMethods = extensionMethods + var sinkConfigurationMethods = FindExtensionMethods(directives) .Where(m => m.GetParameters()[0].ParameterType == typeof(LoggerSinkConfiguration)) .ToList(); @@ -106,8 +108,8 @@ where splitWriteTo.IsMatch(wt.Key) var config = loggerConfiguration.WriteTo; var call = (from p in target.GetParameters().Skip(1) - let directive = sinkDirective.FirstOrDefault(s => s.Argument == p.Name) - select directive == null ? p.DefaultValue : ConvertToType(directive.Value, p.ParameterType)).ToList(); + let directive = sinkDirective.FirstOrDefault(s => s.Argument == p.Name) + select directive == null ? p.DefaultValue : ConvertToType(directive.Value, p.ParameterType)).ToList(); call.Insert(0, config); @@ -145,9 +147,9 @@ internal static object ConvertToType(string value, Type toType) return convertor == null ? Convert.ChangeType(value, toType) : convertor(value); } - static IList FindExtensionMethods(Dictionary directives) + static IEnumerable FindExtensionMethods(Dictionary directives) { - var extensionAssemblies = new List {typeof (ILogger).Assembly, typeof (RollingFileSink).Assembly}; + var extensionAssemblies = new List { typeof(ILogger).Assembly, typeof(RollingFileSink).Assembly }; foreach (var usingDirective in directives.Where(d => d.Key.Equals(UsingDirective) || d.Key.StartsWith(UsingDirectiveFullFormPrefix))) { @@ -157,7 +159,7 @@ static IList FindExtensionMethods(Dictionary directi return extensionAssemblies .SelectMany(a => a.ExportedTypes.Where(t => t.IsSealed && t.IsAbstract && !t.IsNested)) .SelectMany(t => t.GetMethods()) - .Where(m => m.IsStatic && m.IsPublic && m.IsDefined(typeof (ExtensionAttribute), false)) + .Where(m => m.IsStatic && m.IsPublic && m.IsDefined(typeof(ExtensionAttribute), false)) .ToList(); } } diff --git a/src/Serilog/Configuration/ILoggerSettings.cs b/src/Serilog/Configuration/ILoggerSettings.cs new file mode 100644 index 000000000..a5506e05d --- /dev/null +++ b/src/Serilog/Configuration/ILoggerSettings.cs @@ -0,0 +1,28 @@ +// Copyright 2014 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. + +namespace Serilog.Configuration +{ + /// + /// Implemented on types that apply settings to a logger configuration. + /// + public interface ILoggerSettings + { + /// + /// Apply the settings to the logger configuration. + /// + /// The logger configuration to apply settings to. + void Configure(LoggerConfiguration loggerConfiguration); + } +} \ No newline at end of file diff --git a/src/Serilog/Configuration/LoggerSettingsConfiguration.cs b/src/Serilog/Configuration/LoggerSettingsConfiguration.cs new file mode 100644 index 000000000..861d683a8 --- /dev/null +++ b/src/Serilog/Configuration/LoggerSettingsConfiguration.cs @@ -0,0 +1,45 @@ +// Copyright 2015 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; + +namespace Serilog.Configuration +{ + /// + /// Allows additional setting sources to drive the logger configuration. + /// + public class LoggerSettingsConfiguration + { + readonly LoggerConfiguration _loggerConfiguration; + + internal LoggerSettingsConfiguration(LoggerConfiguration loggerConfiguration) + { + if (loggerConfiguration == null) throw new ArgumentNullException("loggerConfiguration"); + _loggerConfiguration = loggerConfiguration; + } + + /// + /// Apply external settings to the logger configuration. + /// + /// Configuration object allowing method chaining. + public LoggerConfiguration Settings(ILoggerSettings settings) + { + if (settings == null) throw new ArgumentNullException("settings"); + + settings.Configure(_loggerConfiguration); + + return _loggerConfiguration; + } + } +} diff --git a/src/Serilog/LoggerConfiguration.cs b/src/Serilog/LoggerConfiguration.cs index 4c582bdba..34969e8c3 100644 --- a/src/Serilog/LoggerConfiguration.cs +++ b/src/Serilog/LoggerConfiguration.cs @@ -103,7 +103,18 @@ public LoggerDestructuringConfiguration Destructure depth => _maximumDestructuringDepth = depth); } } - + + /// + /// Apply external settings to the logger configuration. + /// + public LoggerSettingsConfiguration ReadFrom + { + get + { + return new LoggerSettingsConfiguration(this); + } + } + /// /// Create a logger using the configured sinks, enrichers and minimum level. /// diff --git a/src/Serilog/Serilog-net40.csproj b/src/Serilog/Serilog-net40.csproj index 409b43981..869b34d00 100644 --- a/src/Serilog/Serilog-net40.csproj +++ b/src/Serilog/Serilog-net40.csproj @@ -40,10 +40,12 @@ ..\..\assets\Serilog.snk + + diff --git a/src/Serilog/Serilog.csproj b/src/Serilog/Serilog.csproj index 2170385d4..545d264ac 100644 --- a/src/Serilog/Serilog.csproj +++ b/src/Serilog/Serilog.csproj @@ -46,7 +46,9 @@ + + diff --git a/test/Serilog.Extras.AppSettings.Tests/Serilog.Extras.AppSettings.Tests.csproj b/test/Serilog.Extras.AppSettings.Tests/Serilog.Extras.AppSettings.Tests.csproj deleted file mode 100644 index a283434b0..000000000 --- a/test/Serilog.Extras.AppSettings.Tests/Serilog.Extras.AppSettings.Tests.csproj +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Debug - AnyCPU - {67398D2A-0829-4373-ABC5-2161FEB28A05} - Library - Properties - Serilog.Extras.AppSettings.Tests - Serilog.Extras.AppSettings.Tests - v4.5 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - true - - - ..\..\assets\Serilog.snk - - - - ..\..\packages\NUnit.2.6.3\lib\nunit.framework.dll - - - - - - - - - - - - - - - {21caf132-bbab-41fd-a018-eb9ae54822ed} - Serilog.Extras.AppSettings - - - {7a9e1095-167d-402a-b43d-b36b97ff183d} - Serilog.FullNetFx - - - {0915dbd9-0f7c-4439-8d9e-74c3d579b219} - Serilog - - - {D5648551-D19D-41E3-9FC1-E74B111EEF41} - Serilog.Tests - - - - - Serilog.snk - - - - - - - - \ No newline at end of file diff --git a/test/Serilog.Extras.AppSettings.Tests/packages.config b/test/Serilog.Extras.AppSettings.Tests/packages.config deleted file mode 100644 index ad37a5282..000000000 --- a/test/Serilog.Extras.AppSettings.Tests/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/test/Serilog.Tests/Serilog.Tests.csproj b/test/Serilog.Tests/Serilog.Tests.csproj index 545df619f..965a935f0 100644 --- a/test/Serilog.Tests/Serilog.Tests.csproj +++ b/test/Serilog.Tests/Serilog.Tests.csproj @@ -91,6 +91,7 @@ + diff --git a/test/Serilog.Extras.AppSettings.Tests/PrefixedAppSettingsReaderTests.cs b/test/Serilog.Tests/Settings/KeyValuePairSettingsTests.cs similarity index 52% rename from test/Serilog.Extras.AppSettings.Tests/PrefixedAppSettingsReaderTests.cs rename to test/Serilog.Tests/Settings/KeyValuePairSettingsTests.cs index ad58b431b..1ac54ce77 100644 --- a/test/Serilog.Extras.AppSettings.Tests/PrefixedAppSettingsReaderTests.cs +++ b/test/Serilog.Tests/Settings/KeyValuePairSettingsTests.cs @@ -1,54 +1,53 @@ -using System.Collections.Specialized; +using System.Collections.Generic; using NUnit.Framework; using Serilog.Events; +using Serilog.Settings.KeyValuePairs; using Serilog.Tests.Support; namespace Serilog.Extras.AppSettings.Tests { [TestFixture] - public class PrefixedAppSettingsReaderTests + public class KeyValuePairSettingsTests { [Test] public void ConvertibleValuesConvertToTIfTargetIsNullable() { - var result = (int?)PrefixedAppSettingsReader.ConvertToType("3", typeof(int?)); + var result = (int?)KeyValuePairSettings.ConvertToType("3", typeof(int?)); Assert.That(result == 3); } [Test] public void NullValuesConvertToNullIfTargetIsNullable() { - var result = (int?)PrefixedAppSettingsReader.ConvertToType(null, typeof(int?)); + var result = (int?)KeyValuePairSettings.ConvertToType(null, typeof(int?)); Assert.That(result == null); } [Test] public void EmptyStringValuesConvertToNullIfTargetIsNullable() { - var result = (int?)PrefixedAppSettingsReader.ConvertToType("", typeof(int?)); + var result = (int?)KeyValuePairSettings.ConvertToType("", typeof(int?)); Assert.That(result == null); } [Test] public void ValuesConvertToEnumMembers() { - var result = (LogEventLevel)PrefixedAppSettingsReader.ConvertToType("Information", typeof(LogEventLevel)); + var result = (LogEventLevel)KeyValuePairSettings.ConvertToType("Information", typeof(LogEventLevel)); Assert.AreEqual(LogEventLevel.Information, result); } [Test] public void PropertyEnrichmentIsApplied() { - var configuration = new LoggerConfiguration(); - var settings = new NameValueCollection - { - { "serilog:enrich:with-property:App", "Test" } - }; - - PrefixedAppSettingsReader.ConfigureLogger(configuration, settings); - LogEvent evt = null; - var log = configuration.WriteTo.Sink(new DelegatingSink(e => evt = e)).CreateLogger(); + var log = new LoggerConfiguration() + .ReadFrom.KeyValuePairs(new Dictionary + { + {"enrich:with-property:App", "Test"} + }) + .WriteTo.Sink(new DelegatingSink(e => evt = e)) + .CreateLogger(); log.Information("Has a test property"); @@ -59,16 +58,14 @@ public void PropertyEnrichmentIsApplied() [Test] public void EnvironmentVariableExpansionIsApplied() { - var configuration = new LoggerConfiguration(); - var settings = new NameValueCollection - { - { "serilog:enrich:with-property:Path", "%PATH%" } - }; - - PrefixedAppSettingsReader.ConfigureLogger(configuration, settings); - LogEvent evt = null; - var log = configuration.WriteTo.Sink(new DelegatingSink(e => evt = e)).CreateLogger(); + var log = new LoggerConfiguration() + .ReadFrom.KeyValuePairs(new Dictionary + { + {"enrich:with-property:Path", "%PATH%"} + }) + .WriteTo.Sink(new DelegatingSink(e => evt = e)) + .CreateLogger(); log.Information("Has a Path property with value expanded from the environment variable");