From 48b4757b5a945da67b6309c024d07bcbf1461edf Mon Sep 17 00:00:00 2001 From: Thibaud Desodt Date: Mon, 9 Oct 2017 07:16:13 +0200 Subject: [PATCH] Notify that sub-logger MinimumLevel.Override is not supported Write a warning message to the SelfLog when using WriteTo.Logger() with a logger that has specified MinimumLevel overrides. Related to issue #967 Related to discussion #1032 --- .../Configuration/LoggerSinkConfiguration.cs | 31 +- src/Serilog/Core/Logger.cs | 8 +- src/Serilog/LoggerConfiguration.cs | 7 +- .../Core/ChildLoggerKnownLimitationsTests.cs | 189 ++++++++++ test/Serilog.Tests/Core/ChildLoggerTests.cs | 330 ++++++++++-------- test/Serilog.Tests/Support/Some.cs | 9 + .../Serilog.Tests/Support/TemporarySelfLog.cs | 25 ++ 7 files changed, 438 insertions(+), 161 deletions(-) create mode 100644 test/Serilog.Tests/Core/ChildLoggerKnownLimitationsTests.cs create mode 100644 test/Serilog.Tests/Support/TemporarySelfLog.cs diff --git a/src/Serilog/Configuration/LoggerSinkConfiguration.cs b/src/Serilog/Configuration/LoggerSinkConfiguration.cs index 47d024e1a..8823d4b50 100644 --- a/src/Serilog/Configuration/LoggerSinkConfiguration.cs +++ b/src/Serilog/Configuration/LoggerSinkConfiguration.cs @@ -32,12 +32,9 @@ public class LoggerSinkConfiguration internal LoggerSinkConfiguration(LoggerConfiguration loggerConfiguration, Action addSink, Action applyInheritedConfiguration) { - if (loggerConfiguration == null) throw new ArgumentNullException(nameof(loggerConfiguration)); - if (addSink == null) throw new ArgumentNullException(nameof(addSink)); - if (applyInheritedConfiguration == null) throw new ArgumentNullException(nameof(applyInheritedConfiguration)); - _loggerConfiguration = loggerConfiguration; - _addSink = addSink; - _applyInheritedConfiguration = applyInheritedConfiguration; + _loggerConfiguration = loggerConfiguration ?? throw new ArgumentNullException(nameof(loggerConfiguration)); + _addSink = addSink ?? throw new ArgumentNullException(nameof(addSink)); + _applyInheritedConfiguration = applyInheritedConfiguration ?? throw new ArgumentNullException(nameof(applyInheritedConfiguration)); } /// @@ -128,9 +125,17 @@ public LoggerConfiguration Logger( var lc = new LoggerConfiguration(); _applyInheritedConfiguration(lc); - configureLogger(lc); - return Sink(new SecondaryLoggerSink(lc.CreateLogger(), attemptDispose: true), restrictedToMinimumLevel, levelSwitch); + + var subLogger = lc.CreateLogger(); + if (subLogger.HasOverrideMap) + { + SelfLog.WriteLine("Minimum level overrides are not supported on sub-loggers " + + "and may be removed completely in a future version."); + } + + var secondarySink = new SecondaryLoggerSink(subLogger, attemptDispose: true); + return Sink(secondarySink, restrictedToMinimumLevel, levelSwitch); } /// @@ -149,7 +154,15 @@ public LoggerConfiguration Logger( LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum) { if (logger == null) throw new ArgumentNullException(nameof(logger)); - return Sink(new SecondaryLoggerSink(logger, attemptDispose: false), restrictedToMinimumLevel); + + if (logger is Logger concreteLogger && concreteLogger.HasOverrideMap) + { + SelfLog.WriteLine("Minimum level overrides are not supported on sub-loggers " + + "and may be removed completely in a future version."); + } + + var secondarySink = new SecondaryLoggerSink(logger, attemptDispose: false); + return Sink(secondarySink, restrictedToMinimumLevel); } /// diff --git a/src/Serilog/Core/Logger.cs b/src/Serilog/Core/Logger.cs index b9207c31d..b423ee486 100644 --- a/src/Serilog/Core/Logger.cs +++ b/src/Serilog/Core/Logger.cs @@ -88,6 +88,8 @@ internal Logger( _enricher = enricher; } + internal bool HasOverrideMap => _overrideMap != null; + /// /// Create a logger that enriches log events via the provided enrichers. /// @@ -273,11 +275,11 @@ public void Write(LogEventLevel level, string messageTemplate, params object[] p /// True if the level is enabled; otherwise, false. public bool IsEnabled(LogEventLevel level) { - if ((int) level < (int) _minimumLevel) + if ((int)level < (int)_minimumLevel) return false; return _levelSwitch == null || - (int) level >= (int) _levelSwitch.MinimumLevel; + (int)level >= (int)_levelSwitch.MinimumLevel; } /// @@ -1359,7 +1361,7 @@ public bool BindProperty(string propertyName, object value, bool destructureObje return false; } - property =_messageTemplateProcessor.CreateProperty(propertyName, value, destructureObjects); + property = _messageTemplateProcessor.CreateProperty(propertyName, value, destructureObjects); return true; } diff --git a/src/Serilog/LoggerConfiguration.cs b/src/Serilog/LoggerConfiguration.cs index f24e2dc6c..5cff6f519 100644 --- a/src/Serilog/LoggerConfiguration.cs +++ b/src/Serilog/LoggerConfiguration.cs @@ -80,7 +80,8 @@ public LoggerMinimumLevelConfiguration MinimumLevel get { return new LoggerMinimumLevelConfiguration(this, - l => { + l => + { _minimumLevel = l; _levelSwitch = null; }, @@ -155,10 +156,10 @@ public Logger CreateLogger() } var converter = new PropertyValueConverter( - _maximumDestructuringDepth, + _maximumDestructuringDepth, _maximumStringLength, _maximumCollectionCount, - _additionalScalarTypes, + _additionalScalarTypes, _additionalDestructuringPolicies, auditing); var processor = new MessageTemplateProcessor(converter); diff --git a/test/Serilog.Tests/Core/ChildLoggerKnownLimitationsTests.cs b/test/Serilog.Tests/Core/ChildLoggerKnownLimitationsTests.cs new file mode 100644 index 000000000..b9c60e7a1 --- /dev/null +++ b/test/Serilog.Tests/Core/ChildLoggerKnownLimitationsTests.cs @@ -0,0 +1,189 @@ +using System.Collections.Generic; +using System.Linq; +using Serilog.Core; +using Serilog.Events; +using Serilog.Tests.Support; +using Xunit; +using static Serilog.Events.LogEventLevel; + +namespace Serilog.Tests.Core +{ + public class ChildLoggerKnownLimitationsTests + { + [Fact] + public void SpecifyingMinimumLevelOverridesInWriteToLoggerWithConfigCallBackWritesWarningToSelfLog() + { + var outputs = new List(); + using (TemporarySelfLog.SaveTo(outputs)) + { + var configCallBackSink = new CollectingSink(); + + var logger = new LoggerConfiguration() + .MinimumLevel.Verbose() + .MinimumLevel.Override("Foo.Bar", Warning) + .WriteTo.Logger(lc => lc + .MinimumLevel.Verbose() + .MinimumLevel.Override("Foo.Bar", Debug) + .WriteTo.Sink(configCallBackSink)) + .CreateLogger(); + + var contextLogger = logger.ForContext(Constants.SourceContextPropertyName, "Foo.Bar"); + contextLogger.Write(Some.InformationEvent()); + } + + Assert.EndsWith("Minimum level overrides are not supported on sub-loggers " + + "and may be removed completely in a future version.", + outputs.FirstOrDefault() ?? ""); + } + + [Fact] + public void SpecifyingMinimumLevelOverridesInWriteToLoggerWritesWarningToSelfLog() + { + var outputs = new List(); + using (TemporarySelfLog.SaveTo(outputs)) + { + var subSink = new CollectingSink(); + + var subLogger = new LoggerConfiguration() + .MinimumLevel.Verbose() + .MinimumLevel.Override("Foo.Bar", Debug) + .WriteTo.Sink(subSink) + .CreateLogger(); + + var logger = new LoggerConfiguration() + .MinimumLevel.Verbose() + .MinimumLevel.Override("Foo.Bar", Warning) + .WriteTo.Logger(subLogger) + .CreateLogger(); + + var contextLogger = logger.ForContext(Constants.SourceContextPropertyName, "Foo.Bar"); + contextLogger.Write(Some.InformationEvent()); + } + + Assert.EndsWith("Minimum level overrides are not supported on sub-loggers " + + "and may be removed completely in a future version.", + outputs.FirstOrDefault() ?? ""); + } + + public static IEnumerable GetMinimumLevelOverrideInheritanceTestCases() + { + // Visualizing the pipeline from left to right .... + // + // Event --> Root Logger --> Child Logger + // lvl override/lvl override/levl + // + object[] T(string rs, int? rl, string cs, int? cl) + { + return new object[] { rs, rl, cs, cl }; + } + // numbers are relative to incoming event level + // Information + 1 = Warning + // Information - 1 = Debug + // + // Incoming event is Information + // with SourceContext Root.N1.N2 + // + // - no root overrides but children has its own + yield return T(null, +0, "Root", +1); + yield return T(null, +0, "Root.N1", +1); + yield return T(null, +0, "Root.N1.N2", +1); + // - root overrides let it through but child rejects it + yield return T("Root", +0, "Root", +1); + yield return T("Root.N1", +0, "Root", +1); + yield return T("Root.N1.N2", +0, "Root", +1); + yield return T("Root", +0, "Root.N1", +1); + yield return T("Root.N1", +0, "Root.N1", +1); + yield return T("Root.N1.N2", +0, "Root.N1", +1); + yield return T("Root", +0, "Root.N1.N2", +1); + yield return T("Root.N1", +0, "Root.N1.N2", +1); + yield return T("Root.N1.N2", +0, "Root.N1.N2", +1); + } + + [Theory] + [MemberData(nameof(GetMinimumLevelOverrideInheritanceTestCases))] + public void WriteToLoggerWithConfigCallbackMinimumLevelOverrideInheritanceNotSupportedScenarios( + string rootOverrideSource, + int rootOverrideLevelIncrement, + string childOverrideSource, + int childOverrideLevelIncrement) + { + var incomingEventLevel = Information; + var rootOverrideLevel = incomingEventLevel + rootOverrideLevelIncrement; + var childOverrideLevel = incomingEventLevel + childOverrideLevelIncrement; + + LogEvent evt = null; + var sink = new DelegatingSink(e => evt = e); + + var rootLoggerConfig = new LoggerConfiguration() + .MinimumLevel.Is(LevelAlias.Minimum); + + if (rootOverrideSource != null) + { + rootLoggerConfig.MinimumLevel.Override(rootOverrideSource, rootOverrideLevel); + } + + var logger = rootLoggerConfig + .WriteTo.Logger(lc => + { + lc.MinimumLevel.Is(LevelAlias.Minimum); + if (childOverrideSource != null) + { + lc.MinimumLevel.Override(childOverrideSource, childOverrideLevel); + } + lc.WriteTo.Sink(sink); + }) + .CreateLogger(); + + logger + .ForContext(Constants.SourceContextPropertyName, "Root.N1.N2") + .Write(Some.LogEvent(level: incomingEventLevel)); + + // even though the user may expect no event + Assert.NotNull(evt); + } + + [Theory] + [MemberData(nameof(GetMinimumLevelOverrideInheritanceTestCases))] + public void WriteToLoggerMinimumLevelOverrideInheritanceNotSupportedScenarios( + string rootOverrideSource, + int rootOverrideLevelIncrement, + string childOverrideSource, + int childOverrideLevelIncrement) + { + var incomingEventLevel = Information; + var rootOverrideLevel = incomingEventLevel + rootOverrideLevelIncrement; + var childOverrideLevel = incomingEventLevel + childOverrideLevelIncrement; + + LogEvent evt = null; + var sink = new DelegatingSink(e => evt = e); + + var childLoggerConfig = new LoggerConfiguration() + .MinimumLevel.Is(LevelAlias.Minimum); + if (childOverrideSource != null) + { + childLoggerConfig.MinimumLevel.Override(childOverrideSource, childOverrideLevel); + } + childLoggerConfig.WriteTo.Sink(sink); + var childLogger = childLoggerConfig.CreateLogger(); + + var rootLoggerConfig = new LoggerConfiguration() + .MinimumLevel.Is(LevelAlias.Minimum); + + if (rootOverrideSource != null) + { + rootLoggerConfig.MinimumLevel.Override(rootOverrideSource, rootOverrideLevel); + } + + var logger = rootLoggerConfig + .WriteTo.Logger(childLogger) + .CreateLogger(); + + logger + .ForContext(Constants.SourceContextPropertyName, "Root.N1.N2") + .Write(Some.LogEvent(level: incomingEventLevel)); + + // even though the use may expect no event + Assert.NotNull(evt); + } + } +} diff --git a/test/Serilog.Tests/Core/ChildLoggerTests.cs b/test/Serilog.Tests/Core/ChildLoggerTests.cs index 6b8883c1c..51489daf4 100644 --- a/test/Serilog.Tests/Core/ChildLoggerTests.cs +++ b/test/Serilog.Tests/Core/ChildLoggerTests.cs @@ -1,5 +1,5 @@ -using Serilog.Core; -using Serilog.Core.Sinks; +using System.Collections.Generic; +using Serilog.Core; using Serilog.Events; using Serilog.Tests.Support; using Xunit; @@ -10,58 +10,67 @@ namespace Serilog.Tests.Core public class ChildLoggerTests { + public static IEnumerable GetMinimumLevelInheritanceTestCases() + { + // Visualizing the pipeline from left to right .... + // + // Event --> Root Logger --> restrictedTo --> Child Logger -> YES or + // lvl min lvl param min lvl NO ? + // + object[] T(LogEventLevel el, int? rl, int? rt, int? cl, bool r) + { + return new object[]{ el, rl, rt, cl, r }; + } + // numbers are relative to incoming event level + // Information + 1 = Warning + // Information - 1 = Debug + // Information + null = default + // + // - default case - nothing specified + // equivalent to "Information+ allowed" + yield return T(Verbose, null, null, null, false); + yield return T(Debug, null, null, null, false); + yield return T(Information, null, null, null, true); + yield return T(Warning, null, null, null, true); + yield return T(Error, null, null, null, true); + yield return T(Fatal, null, null, null, true); + + // - cases where event level is high enough all along the pipeline + // e --> --> --> = OK + yield return T(Verbose, +0, +0, +0, true); + yield return T(Debug, +0, +0, +0, true); + yield return T(Information, +0, +0, +0, true); + yield return T(Warning, +0, +0, +0, true); + yield return T(Error, +0, +0, +0, true); + yield return T(Fatal, +0, +0, +0, true); + + // - cases where event is blocked by root minimum level + // e -x> - - = NO + yield return T(Verbose, +1, +0, +0, false); + yield return T(Debug, +1, +0, +0, false); + yield return T(Information, +1, +0, +0, false); + yield return T(Warning, +1, +0, +0, false); + yield return T(Error, +1, +0, +0, false); + + // - cases where event is blocked by param restrictedToMinimumLevel + // e --> -x> - = NO + yield return T(Verbose, +0, +1, +0, false); + yield return T(Debug, +0, +1, +0, false); + yield return T(Information, +0, +1, +0, false); + yield return T(Warning, +0, +1, +0, false); + yield return T(Error, +0, +1, +0, false); + + // - cases where event is blocked by child minimum level + // e --> --> -x> = NO + yield return T(Verbose, +0, +0, +1, false); + yield return T(Debug, +0, +0, +1, false); + yield return T(Information, +0, +0, +1, false); + yield return T(Warning, +0, +0, +1, false); + yield return T(Error, +0, +0, +1, false); + } + [Theory] - // Visualizing the pipeline from left to right .... - // - // Event --> Root Logger --> restrictedTo --> Child Logger -> YES or - // lvl min lvl param min lvl NO ? - // - // numbers are relative to incoming event level - // Information + 1 = Warning - // Information - 1 = Debug - // Information + null = default - // - // - default case - nothing specified - // equivalent to "Information+ allowed" - [InlineData(Verbose, null, null, null, false)] - [InlineData(Debug, null, null, null, false)] - [InlineData(Information, null, null, null, true)] - [InlineData(Warning, null, null, null, true)] - [InlineData(Error, null, null, null, true)] - [InlineData(Fatal, null, null, null, true)] - - // - cases where event level is high enough all along the pipeline - // e --> --> --> = OK - [InlineData(Verbose, +0, +0, +0, true)] - [InlineData(Debug, +0, +0, +0, true)] - [InlineData(Information, +0, +0, +0, true)] - [InlineData(Warning, +0, +0, +0, true)] - [InlineData(Error, +0, +0, +0, true)] - [InlineData(Fatal, +0, +0, +0, true)] - - // - cases where event is blocked by root minimum level - // e -x> - - = NO - [InlineData(Verbose, +1, +0, +0, false)] - [InlineData(Debug, +1, +0, +0, false)] - [InlineData(Information, +1, +0, +0, false)] - [InlineData(Warning, +1, +0, +0, false)] - [InlineData(Error, +1, +0, +0, false)] - - // - cases where event is blocked by param restrictedToMinimumLevel - // e --> -x> - = NO - [InlineData(Verbose, +0, +1, +0, false)] - [InlineData(Debug, +0, +1, +0, false)] - [InlineData(Information, +0, +1, +0, false)] - [InlineData(Warning, +0, +1, +0, false)] - [InlineData(Error, +0, +1, +0, false)] - - // - cases where event is blocked by child minimum level - // e --> --> -x> = NO - [InlineData(Verbose, +0, +0, +1, false)] - [InlineData(Debug, +0, +0, +1, false)] - [InlineData(Information, +0, +0, +1, false)] - [InlineData(Warning, +0, +0, +1, false)] - [InlineData(Error, +0, +0, +1, false)] + [MemberData(nameof(GetMinimumLevelInheritanceTestCases))] public void WriteToLoggerWithConfigCallbackMinimumLevelInheritanceScenarios( LogEventLevel eventLevel, int? rootMinimumLevelIncrement, @@ -86,61 +95,18 @@ public void WriteToLoggerWithConfigCallbackMinimumLevelInheritanceScenarios( logger.Write(Some.LogEvent(level: eventLevel)); - Assert.Equal(eventShouldGetToChild, evt!=null); + if (eventShouldGetToChild) + { + Assert.NotNull(evt); + } + else + { + Assert.Null(evt); + } } [Theory] - // Visualizing the pipeline from left to right .... - // - // Event --> Root Logger --> restrictedTo --> Child Logger -> YES or - // lvl min lvl param min lvl NO ? - // - // numbers are relative to incoming event level - // Information + 1 = Warning - // Information - 1 = Debug - // Information + null = default - // - // - default case - nothing specified - // equivalent to "Information+ allowed" - [InlineData(Verbose, null, null, null, false)] - [InlineData(Debug, null, null, null, false)] - [InlineData(Information, null, null, null, true)] - [InlineData(Warning, null, null, null, true)] - [InlineData(Error, null, null, null, true)] - [InlineData(Fatal, null, null, null, true)] - - // - cases where event level is high enough all along the pipeline - // e --> --> --> = OK - [InlineData(Verbose, +0, +0, +0, true)] - [InlineData(Debug, +0, +0, +0, true)] - [InlineData(Information, +0, +0, +0, true)] - [InlineData(Warning, +0, +0, +0, true)] - [InlineData(Error, +0, +0, +0, true)] - [InlineData(Fatal, +0, +0, +0, true)] - - // - cases where event is blocked by root minimum level - // e -x> - - = NO - [InlineData(Verbose, +1, +0, +0, false)] - [InlineData(Debug, +1, +0, +0, false)] - [InlineData(Information, +1, +0, +0, false)] - [InlineData(Warning, +1, +0, +0, false)] - [InlineData(Error, +1, +0, +0, false)] - - // - cases where event is blocked by param restrictedToMinimumLevel - // e --> -x> - = NO - [InlineData(Verbose, +0, +1, +0, false)] - [InlineData(Debug, +0, +1, +0, false)] - [InlineData(Information, +0, +1, +0, false)] - [InlineData(Warning, +0, +1, +0, false)] - [InlineData(Error, +0, +1, +0, false)] - - // - cases where event is blocked by child minimum level - // e --> --> -x> = NO - [InlineData(Verbose, +0, +0, +1, false)] - [InlineData(Debug, +0, +0, +1, false)] - [InlineData(Information, +0, +0, +1, false)] - [InlineData(Warning, +0, +0, +1, false)] - [InlineData(Error, +0, +0, +1, false)] + [MemberData(nameof(GetMinimumLevelInheritanceTestCases))] public void WriteToLoggerMinimumLevelInheritanceScenarios( LogEventLevel eventLevel, int? rootMinimumLevelIncrement, @@ -167,50 +133,64 @@ public void WriteToLoggerMinimumLevelInheritanceScenarios( logger.Write(Some.LogEvent(level: eventLevel)); - Assert.Equal(eventShouldGetToChild, evt != null); + if (eventShouldGetToChild) + { + Assert.NotNull(evt); + } + else + { + Assert.Null(evt); + } } - + public static IEnumerable GetMinimumLevelOverrideInheritanceTestCases() + { + // Visualizing the pipeline from left to right .... + // + // Event --> Root Logger --> Child Logger -> YES or + // lvl override/lvl override/levl NO ? + // + object[] T(string rs, int? rl, string cs, int? cl, bool r) + { + return new object[] { rs, rl, cs, cl, r }; + } + // numbers are relative to incoming event level + // Information + 1 = Warning + // Information - 1 = Debug + // + // Incoming event is Information + // with SourceContext Root.N1.N2 + // + // - default case - no overrides + yield return T(null, 0, null, 0, true); + // - root overrides with level lower or equal to event + // ... and child logger is out of the way + yield return T("Root", +0, null, +0, true); + yield return T("Root", -1, null, +0, true); + yield return T("Root.N1", +0, null, +0, true); + yield return T("Root.N1", -1, null, +0, true); + yield return T("Root.N1.N2", +0, null, +0, true); + yield return T("Root.N1.N2", -1, null, +0, true); + // - root overrides on irrelevant namespaces + yield return T("xx", +1, null, +0, true); + yield return T("Root.xx", +1, null, +0, true); + yield return T("Root.N1.xx", +1, null, +0, true); + // - child overrides on irrelevant namespaces + yield return T(null, +0, "xx", +1, true); + yield return T(null, +0, "Root.xx", +1, true); + yield return T(null, +1, "Root.N1.xx", +1, true); + // - root overrides prevent all processing from children + // even though children would happily accept it + yield return T("Root", +1, null, +0, false); + yield return T("Root", +1, "Root", +0, false); + yield return T("Root.N1", +1, null, +0, false); + yield return T("Root.N1", +1, "Root.N1", +0, false); + yield return T("Root.N1.N2", +1, null, +0, false); + yield return T("Root.N1.N2", +1, "Root.N1.N2", +0, false); + } [Theory] - // Visualizing the pipeline from left to right .... - // - // Event --> Root Logger --> Child Logger -> YES or - // lvl override/lvl override/levl NO ? - // - // numbers are relative to incoming event level - // Information + 1 = Warning - // Information - 1 = Debug - // - // Incoming event is Information - // with SourceContext Root.N1.N2 - // - // - default case - no overrides - [InlineData(null, 0, null, 0, true)] - // - root overrides with level lower or equal to event - // ... and child logger is out of the way - [InlineData("Root", +0, null, +0, true)] - [InlineData("Root", -1, null, +0, true)] - [InlineData("Root.N1", +0, null, +0, true)] - [InlineData("Root.N1", -1, null, +0, true)] - [InlineData("Root.N1.N2", +0, null, +0, true)] - [InlineData("Root.N1.N2", -1, null, +0, true)] - // - root overrides on irrelevant namespaces - [InlineData("xx", +1, null, +0, true)] - [InlineData("Root.xx", +1, null, +0, true)] - [InlineData("Root.N1.xx", +1, null, +0, true)] - // - child overrides on irrelevant namespaces - [InlineData(null, +0, "xx", +1, true)] - [InlineData(null, +0, "Root.xx", +1, true)] - [InlineData(null, +1, "Root.N1.xx", +1, true)] - // - root overrides prevent all processing from children - // even though children would happily accept it - [InlineData("Root", +1, null, +0, false)] - [InlineData("Root", +1, "Root", +0, false)] - [InlineData("Root.N1", +1, null, +0, false)] - [InlineData("Root.N1", +1, "Root.N1", +0, false)] - [InlineData("Root.N1.N2", +1, null, +0, false)] - [InlineData("Root.N1.N2", +1, "Root.N1.N2", +0, false)] + [MemberData(nameof(GetMinimumLevelOverrideInheritanceTestCases))] public void WriteToLoggerWithConfigCallbackMinimumLevelOverrideInheritanceScenarios( string rootOverrideSource, int rootOverrideLevelIncrement, @@ -249,7 +229,65 @@ public void WriteToLoggerWithConfigCallbackMinimumLevelOverrideInheritanceScenar .ForContext(Constants.SourceContextPropertyName, "Root.N1.N2") .Write(Some.LogEvent(level: incomingEventLevel)); - Assert.Equal(eventShouldGetToChild, evt != null); + if (eventShouldGetToChild) + { + Assert.NotNull(evt); + } + else + { + Assert.Null(evt); + } + } + + [Theory] + [MemberData(nameof(GetMinimumLevelOverrideInheritanceTestCases))] + public void WriteToLoggerMinimumLevelOverrideInheritanceScenarios( + string rootOverrideSource, + int rootOverrideLevelIncrement, + string childOverrideSource, + int childOverrideLevelIncrement, + bool eventShouldGetToChild) + { + var incomingEventLevel = Information; + var rootOverrideLevel = incomingEventLevel + rootOverrideLevelIncrement; + var childOverrideLevel = incomingEventLevel + childOverrideLevelIncrement; + + LogEvent evt = null; + var sink = new DelegatingSink(e => evt = e); + + var childLoggerConfig = new LoggerConfiguration() + .MinimumLevel.Is(LevelAlias.Minimum); + if (childOverrideSource != null) + { + childLoggerConfig.MinimumLevel.Override(childOverrideSource, childOverrideLevel); + } + childLoggerConfig.WriteTo.Sink(sink); + var childLogger = childLoggerConfig.CreateLogger(); + + var rootLoggerConfig = new LoggerConfiguration() + .MinimumLevel.Is(LevelAlias.Minimum); + + if (rootOverrideSource != null) + { + rootLoggerConfig.MinimumLevel.Override(rootOverrideSource, rootOverrideLevel); + } + + var logger = rootLoggerConfig + .WriteTo.Logger(childLogger) + .CreateLogger(); + + logger + .ForContext(Constants.SourceContextPropertyName, "Root.N1.N2") + .Write(Some.LogEvent(level: incomingEventLevel)); + + if (eventShouldGetToChild) + { + Assert.NotNull(evt); + } + else + { + Assert.Null(evt); + } } } } diff --git a/test/Serilog.Tests/Support/Some.cs b/test/Serilog.Tests/Support/Some.cs index 0df29682a..6de916127 100644 --- a/test/Serilog.Tests/Support/Some.cs +++ b/test/Serilog.Tests/Support/Some.cs @@ -1,7 +1,9 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; +using Serilog.Core; using Serilog.Events; using Serilog.Parsing; @@ -41,6 +43,13 @@ public static DateTimeOffset OffsetInstant() return new DateTimeOffset(Instant()); } + public static LogEvent LogEvent(string sourceContext, DateTimeOffset? timestamp = null, LogEventLevel level = LogEventLevel.Information) + { + return new LogEvent(timestamp ?? OffsetInstant(), level, + null, MessageTemplate(), + new List { new LogEventProperty(Constants.SourceContextPropertyName, new ScalarValue(sourceContext)) }); + } + public static LogEvent LogEvent(DateTimeOffset? timestamp = null, LogEventLevel level = LogEventLevel.Information) { return new LogEvent(timestamp ?? OffsetInstant(), level, diff --git a/test/Serilog.Tests/Support/TemporarySelfLog.cs b/test/Serilog.Tests/Support/TemporarySelfLog.cs new file mode 100644 index 000000000..c21789358 --- /dev/null +++ b/test/Serilog.Tests/Support/TemporarySelfLog.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using Serilog.Debugging; + +namespace Serilog.Tests.Support +{ + public class TemporarySelfLog : IDisposable + { + TemporarySelfLog(Action output) + { + SelfLog.Enable(output); + } + + public void Dispose() + { + SelfLog.Disable(); + } + + public static IDisposable SaveTo(List target) + { + if (target == null) throw new ArgumentNullException(nameof(target)); + return new TemporarySelfLog(target.Add); + } + } +}