diff --git a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj index ed87018..60b1c67 100644 --- a/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj +++ b/src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj @@ -3,7 +3,7 @@ Microsoft.Extensions.Configuration (appsettings.json) support for Serilog. - 7.0.0 + 7.0.1 Serilog Contributors diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs index 2a6c317..1049b85 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/ConfigurationReader.cs @@ -17,6 +17,9 @@ class ConfigurationReader : IConfigurationReader { const string LevelSwitchNameRegex = @"^\${0,1}[A-Za-z]+[A-Za-z0-9]*$"; + // Section names that can be handled by Serilog itself (hence builtin) without requiring any additional assemblies. + static readonly string[] BuiltinSectionNames = { "LevelSwitches", "MinimumLevel", "Properties" }; + readonly IConfiguration _section; readonly IReadOnlyCollection _configurationAssemblies; readonly ResolutionContext _resolutionContext; @@ -164,7 +167,7 @@ void ApplyMinimumLevel(LoggerConfiguration loggerConfiguration) { var overridePrefix = overrideDirective.Key; var overridenLevelOrSwitch = overrideDirective.Value; - if (Enum.TryParse(overridenLevelOrSwitch, out LogEventLevel _)) + if (Enum.TryParse(overridenLevelOrSwitch, ignoreCase: true, out LogEventLevel _)) { ApplyMinimumLevelConfiguration(overrideDirective, (configuration, levelSwitch) => { @@ -221,7 +224,7 @@ void SubscribeToLoggingLevelChanges(IConfigurationSection levelSection, LoggingL levelSection.GetReloadToken, () => { - if (Enum.TryParse(levelSection.Value, out LogEventLevel minimumLevel)) + if (Enum.TryParse(levelSection.Value, ignoreCase: true, out LogEventLevel minimumLevel)) levelSwitch.MinimumLevel = minimumLevel; else SelfLog.WriteLine($"The value {levelSection.Value} is not a valid Serilog level."); @@ -383,7 +386,10 @@ static IReadOnlyCollection LoadConfigurationAssemblies(IConfiguration assemblies.Add(assumed); } - if (assemblies.Count == 1) + // We don't want to throw if the configuration contains only sections that can be handled by Serilog itself, without requiring any additional assembly. + // See https://github.com/serilog/serilog-settings-configuration/issues/389 + var requiresAdditionalAssemblies = section.GetChildren().Select(e => e.Key).Except(BuiltinSectionNames).Any(); + if (assemblies.Count == 1 && requiresAdditionalAssemblies) { var message = $""" No {usingSection.Path} configuration section is defined and no Serilog assemblies were found. @@ -591,9 +597,7 @@ internal static bool IsValidSwitchName(string input) } static LogEventLevel ParseLogEventLevel(string value) - { - if (!Enum.TryParse(value, ignoreCase: true, out LogEventLevel parsedLevel)) - throw new InvalidOperationException($"The value {value} is not a valid Serilog level."); - return parsedLevel; - } + => Enum.TryParse(value, ignoreCase: true, out LogEventLevel parsedLevel) + ? parsedLevel + : throw new InvalidOperationException($"The value {value} is not a valid Serilog level."); } diff --git a/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs b/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs index ebf3d2e..2ba8dcc 100644 --- a/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs +++ b/src/Serilog.Settings.Configuration/Settings/Configuration/StringArgumentValue.cs @@ -10,7 +10,7 @@ class StringArgumentValue : IConfigurationArgumentValue { readonly string _providedValue; - static readonly Regex StaticMemberAccessorRegex = new Regex("^(?[^:]+)::(?[A-Za-z][A-Za-z0-9]*)(?[^:]*)$"); + static readonly Regex StaticMemberAccessorRegex = new("^(?[^:]+)::(?[A-Za-z][A-Za-z0-9]*)(?[^:]*)$"); public StringArgumentValue(string providedValue) { diff --git a/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs b/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs index 2197e78..c2f5d33 100644 --- a/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs +++ b/test/Serilog.Settings.Configuration.Tests/PublishSingleFileTests.cs @@ -88,6 +88,15 @@ public async Task RunTestApp_ConsoleAndThread(PublishMode publishMode, string st stdErr.Should().BeEmpty(); } + [Theory] + [ClassData(typeof(PublishModeTheoryData))] + public async Task RunTestApp_ConfigureMinimumLevelOnly(PublishMode publishMode) + { + var (_, stdOut, stdErr) = await RunTestAppAsync(publishMode, "--minimum-level-only"); + stdOut.Should().Be("(Main thread) [Information] Expected success"); + stdErr.Should().BeEmpty(); + } + async Task<(bool IsSingleFile, string StdOut, string StdErr)> RunTestAppAsync(PublishMode publishMode, params string[] args) { // Determine whether the app is a _true_ single file, i.e. not a .NET Core 3.x version which diff --git a/test/TestApp/Program.cs b/test/TestApp/Program.cs index b376df4..033b43d 100644 --- a/test/TestApp/Program.cs +++ b/test/TestApp/Program.cs @@ -21,13 +21,20 @@ SelfLog.Enable(Console.Error); Thread.CurrentThread.Name = "Main thread"; +const string outputTemplate = "({ThreadName}) [{Level}] {Message}{NewLine}"; -var configurationValues = new Dictionary +var configurationValues = new Dictionary(); +var minimumLevelOnly = args.Contains("--minimum-level-only"); +if (minimumLevelOnly) { - ["Serilog:Enrich:0"] = "WithThreadName", - ["Serilog:WriteTo:0:Name"] = "Console", - ["Serilog:WriteTo:0:Args:outputTemplate"] = "({ThreadName}) [{Level}] {Message}{NewLine}", -}; + configurationValues["Serilog:MinimumLevel"] = "Verbose"; +} +else +{ + configurationValues["Serilog:Enrich:0"] = "WithThreadName"; + configurationValues["Serilog:WriteTo:0:Name"] = "Console"; + configurationValues["Serilog:WriteTo:0:Args:outputTemplate"] = outputTemplate; +} if (args.Contains("--using-thread")) configurationValues["Serilog:Using:Thread"] = "Serilog.Enrichers.Thread"; if (args.Contains("--using-console")) configurationValues["Serilog:Using:Console"] = "Serilog.Sinks.Console"; @@ -40,7 +47,14 @@ { var configuration = new ConfigurationBuilder().AddInMemoryCollection(configurationValues).Build(); var options = assemblies.Count > 0 ? new ConfigurationReaderOptions(assemblies.ToArray()) : null; - var logger = new LoggerConfiguration().ReadFrom.Configuration(configuration, options).CreateLogger(); + var loggerConfiguration = new LoggerConfiguration().ReadFrom.Configuration(configuration, options); + if (minimumLevelOnly) + { + loggerConfiguration + .Enrich.WithThreadName() + .WriteTo.Console(outputTemplate: outputTemplate); + } + var logger = loggerConfiguration.CreateLogger(); logger.Information("Expected success"); return 0; }