diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/Log4NetLogging/Instrumentation.xml b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/Log4NetLogging/Instrumentation.xml index c41a9a3ca2..94f798a05d 100644 --- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/Log4NetLogging/Instrumentation.xml +++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/Log4NetLogging/Instrumentation.xml @@ -11,7 +11,7 @@ SPDX-License-Identifier: Apache-2.0 - + diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/Log4NetLogging/Log4netWrapper.cs b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/Log4NetLogging/Log4netWrapper.cs index e94a73bf7b..8cf50b13dc 100644 --- a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/Log4NetLogging/Log4netWrapper.cs +++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/Log4NetLogging/Log4netWrapper.cs @@ -1,4 +1,4 @@ -// Copyright 2020 New Relic, Inc. All rights reserved. +// Copyright 2020 New Relic, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 using System; @@ -22,11 +22,6 @@ public class Log4netWrapper : IWrapper private static Func _getGetProperties; // calls GetProperties method private static Func _getProperties; // getter for Properties property - private static Func _getLegacyProperties; // getter for legacy Properties property - private static Func _getLegacyHashtable; // getter for Properties hashtable property - - private static bool _legacyVersion = false; - public bool IsTransactionRequired => false; @@ -59,17 +54,7 @@ private void RecordLogMessage(object logEvent, Type logEventType, IAgent agent) // Older versions of log4net only allow access to a timestamp in local time var getTimestampFunc = _getTimestamp ??= VisibilityBypasser.Instance.GeneratePropertyAccessor(logEventType, "TimeStamp"); - if (_getLogException == null) - { - if (!VisibilityBypasser.Instance.TryGeneratePropertyAccessor(logEventType, "ExceptionObject", out _getLogException)) - { - // Legacy property, mainly used by Sitecore - if (!VisibilityBypasser.Instance.TryGeneratePropertyAccessor(logEventType, "m_thrownException", out _getLogException)) - { - _getLogException = (x) => null; - } - } - } + _getLogException ??= VisibilityBypasser.Instance.GeneratePropertyAccessor(logEventType, "ExceptionObject"); // This will either add the log message to the transaction or directly to the aggregator var xapi = agent.GetExperimentalApi(); @@ -102,36 +87,9 @@ private void DecorateLogMessage(object logEvent, Type logEventType, IAgent agent private Dictionary GetContextData(object logEvent) { var logEventType = logEvent.GetType(); - - if (_getGetProperties == null && !VisibilityBypasser.Instance.TryGenerateParameterlessMethodCaller(logEventType.Assembly.ToString(), logEventType.FullName, "GetProperties", out _getGetProperties)) - { - // Legacy property, mainly used by Sitecore - if (VisibilityBypasser.Instance.TryGeneratePropertyAccessor(logEventType, "MappedContext", out _getGetProperties)) - _legacyVersion = true; - else - _getGetProperties = (x) => null; - } + _getGetProperties ??= VisibilityBypasser.Instance.GenerateParameterlessMethodCaller(logEventType.Assembly.ToString(), logEventType.FullName, "GetProperties"); var contextData = new Dictionary(); - // In older versions of log4net, there may be additional properties - if (_legacyVersion) - { - // Properties is a "PropertiesCollection", an internal type - var getLegacyProperties = _getLegacyProperties ??= VisibilityBypasser.Instance.GeneratePropertyAccessor(logEventType, "Properties"); - var legacyProperties = getLegacyProperties(logEvent); - - // PropertyCollection has an internal hashtable that stores the data. The only public method for - // retrieving the data is the indexer [] which is more of a pain to get via reflection. - var propertyCollectionType = legacyProperties.GetType(); - var getHashtable = _getLegacyHashtable ??= VisibilityBypasser.Instance.GenerateFieldReadAccessor(propertyCollectionType.Assembly.ToString(), propertyCollectionType.FullName, "m_ht"); - - var hashtable = getHashtable(legacyProperties); - - foreach (var key in hashtable.Keys) - { - contextData.Add(key.ToString(), hashtable[key]); - } - } var propertiesDictionary = _getGetProperties(logEvent); diff --git a/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/Log4NetLogging/SitecoreLoggingWrapper.cs b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/Log4NetLogging/SitecoreLoggingWrapper.cs new file mode 100644 index 0000000000..c0eb1c01b2 --- /dev/null +++ b/src/Agent/NewRelic/Agent/Extensions/Providers/Wrapper/Log4NetLogging/SitecoreLoggingWrapper.cs @@ -0,0 +1,127 @@ +// Copyright 2020 New Relic, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using NewRelic.Agent.Api; +using NewRelic.Agent.Api.Experimental; +using NewRelic.Agent.Extensions.Logging; +using NewRelic.Agent.Extensions.Providers.Wrapper; +using NewRelic.Reflection; + +namespace NewRelic.Providers.Wrapper.Logging +{ + public class SitecoreLoggingWrapper : IWrapper + { + private static Func _getLevel; + private static Func _getRenderedMessage; + private static Func _getTimestamp; + private static Func _getLogException; + private static Func _getGetProperties; // calls GetProperties method + private static Func _getProperties; // getter for Properties property + + private static Func _getLegacyProperties; // getter for legacy Properties property + private static Func _getLegacyHashtable; // getter for Properties hashtable property + + public bool IsTransactionRequired => false; + + + private const string WrapperName = "SitecoreLogging"; + + public CanWrapResponse CanWrap(InstrumentedMethodInfo methodInfo) + { + return new CanWrapResponse(WrapperName.Equals(methodInfo.RequestedWrapperName)); + } + + public AfterWrappedMethodDelegate BeforeWrappedMethod(InstrumentedMethodCall instrumentedMethodCall, IAgent agent, ITransaction transaction) + { + var logEvent = instrumentedMethodCall.MethodCall.MethodArguments[0]; + var logEventType = logEvent.GetType(); + + RecordLogMessage(logEvent, logEventType, agent); + + DecorateLogMessage(logEvent, logEventType, agent); + + return Delegates.NoOp; + } + + private void RecordLogMessage(object logEvent, Type logEventType, IAgent agent) + { + var getLevelFunc = _getLevel ??= VisibilityBypasser.Instance.GeneratePropertyAccessor(logEventType, "Level"); + + // RenderedMessage is get only + var getRenderedMessageFunc = _getRenderedMessage ??= VisibilityBypasser.Instance.GeneratePropertyAccessor(logEventType, "RenderedMessage"); + + // Older versions of log4net only allow access to a timestamp in local time + var getTimestampFunc = _getTimestamp ??= VisibilityBypasser.Instance.GeneratePropertyAccessor(logEventType, "TimeStamp"); + + _getLogException ??= VisibilityBypasser.Instance.GenerateFieldReadAccessor(logEventType, "m_thrownException"); + + // This will either add the log message to the transaction or directly to the aggregator + var xapi = agent.GetExperimentalApi(); + + xapi.RecordLogMessage(WrapperName, logEvent, getTimestampFunc, getLevelFunc, getRenderedMessageFunc, _getLogException, GetContextData, agent.TraceMetadata.SpanId, agent.TraceMetadata.TraceId); + } + + private void DecorateLogMessage(object logEvent, Type logEventType, IAgent agent) + { + if (!agent.Configuration.LogDecoratorEnabled) + { + return; + } + + var getProperties = _getProperties ??= VisibilityBypasser.Instance.GeneratePropertyAccessor(logEventType, "Properties"); + var propertiesDictionary = getProperties(logEvent); + + if (propertiesDictionary == null) + { + return; + } + + // uses the foratted metadata to make a single entry + var formattedMetadata = LoggingHelpers.GetFormattedLinkingMetadata(agent); + + // uses underscores to support other frameworks that do not allow hyphens (Serilog) + propertiesDictionary["NR_LINKING"] = formattedMetadata; + } + + private Dictionary GetContextData(object logEvent) + { + var logEventType = logEvent.GetType(); + _getGetProperties ??= VisibilityBypasser.Instance.GeneratePropertyAccessor(logEventType, "MappedContext"); + + var contextData = new Dictionary(); + // In older versions of log4net, there may be additional properties + + // Properties is a "PropertiesCollection", an internal type + var getLegacyProperties = _getLegacyProperties ??= VisibilityBypasser.Instance.GeneratePropertyAccessor(logEventType, "Properties"); + var legacyProperties = getLegacyProperties(logEvent); + + // PropertyCollection has an internal hashtable that stores the data. The only public method for + // retrieving the data is the indexer [] which is more of a pain to get via reflection. + var propertyCollectionType = legacyProperties.GetType(); + var getHashtable = _getLegacyHashtable ??= VisibilityBypasser.Instance.GenerateFieldReadAccessor(propertyCollectionType.Assembly.ToString(), propertyCollectionType.FullName, "m_ht"); + + var hashtable = getHashtable(legacyProperties); + + foreach (var key in hashtable.Keys) + { + contextData.Add(key.ToString(), hashtable[key]); + } + + var propertiesDictionary = _getGetProperties(logEvent); + + if (propertiesDictionary != null && propertiesDictionary.Count > 0) + { + foreach (var key in propertiesDictionary.Keys) + { + contextData.Add(key.ToString(), propertiesDictionary[key]); + } + } + + return contextData.Any() ? contextData : null; + } + } +} diff --git a/tests/Agent/IntegrationTests/IntegrationTests/Logging/ContextDataTests.cs b/tests/Agent/IntegrationTests/IntegrationTests/Logging/ContextDataTests.cs index 5ce9f7270e..a060231450 100644 --- a/tests/Agent/IntegrationTests/IntegrationTests/Logging/ContextDataTests.cs +++ b/tests/Agent/IntegrationTests/IntegrationTests/Logging/ContextDataTests.cs @@ -15,36 +15,36 @@ public abstract class ContextDataTestsBase : NewRelicIntegrationTest _loggingFrameworks; private readonly bool _testNestedContexts; private const string InfoMessage = "HelloWorld"; // There are several entries in this dictionary to allow for different methods of adding the values in the test adapter - // If you need more entries for your framework, add them - private Dictionary _expectedAttributes = new Dictionary() + + private Dictionary GetExpectedAttributes(LoggingFramework framework) => new Dictionary() { + { "framework", framework.ToString() }, { "mycontext1", "foo" }, { "mycontext2", "bar" }, { "mycontext3", "test" }, { "mycontext4", "value" }, }; + private string FlattenExpectedAttributes(Dictionary attributes) => string.Join(",", attributes.Select(x => x.Key + "=" + x.Value).ToArray()); - public ContextDataTestsBase(TFixture fixture, ITestOutputHelper output, LoggingFramework loggingFramework, bool testNestedContexts) : base(fixture) + public ContextDataTestsBase(TFixture fixture, ITestOutputHelper output, bool testNestedContexts, params LoggingFramework[] loggingFrameworks) : base(fixture) { _fixture = fixture; - _loggingFramework = loggingFramework; + _loggingFrameworks = loggingFrameworks.ToList(); _testNestedContexts = testNestedContexts; _fixture.SetTimeout(TimeSpan.FromMinutes(2)); _fixture.TestLogger = output; - _fixture.AddCommand($"LoggingTester SetFramework {_loggingFramework} {RandomPortGenerator.NextPort()}"); + _loggingFrameworks.ForEach(x => _fixture.AddCommand($"LoggingTester SetFramework {x} {RandomPortGenerator.NextPort()}")); _fixture.AddCommand($"LoggingTester Configure"); - string context = string.Join(",", _expectedAttributes.Select(x => x.Key + "=" + x.Value).ToArray()); - - _fixture.AddCommand($"LoggingTester CreateSingleLogMessage {InfoMessage} INFO {context}"); + _loggingFrameworks.ForEach(x => _fixture.AddCommand($"LoggingTester CreateSingleLogMessage {x} {InfoMessage} INFO {FlattenExpectedAttributes(GetExpectedAttributes(x))}")); if (_testNestedContexts) // on supported frameworks, ensure that we don't blow up when accumulating the context key/value pairs _fixture.AddCommand($"LoggingTester LogMessageInNestedScopes"); @@ -71,15 +71,15 @@ public ContextDataTestsBase(TFixture fixture, ITestOutputHelper output, LoggingF [Fact] public void Test() { - var expectedLogLines = new Assertions.ExpectedLogLine[] - { + List expectedLogLines = new List(); + _loggingFrameworks.ForEach(x => expectedLogLines.Add( new Assertions.ExpectedLogLine { - Level = LogUtils.GetLevelName(_loggingFramework, "INFO"), + Level = LogUtils.GetLevelName(x, "INFO"), LogMessage = InfoMessage, - Attributes = _expectedAttributes + Attributes = GetExpectedAttributes(x) } - }; + )); var logLines = _fixture.AgentLog.GetLogEventDataLogLines().ToArray(); @@ -88,31 +88,33 @@ public void Test() if (_testNestedContexts) { - var outerContextExpectedAttributes = new Dictionary(); - foreach(var kvp in _expectedAttributes) - outerContextExpectedAttributes.Add(kvp.Key, kvp.Value); - outerContextExpectedAttributes.Add("ScopeKey1", "scopeValue1"); + List contextExpectedLogLines = new List(); + _loggingFrameworks.ForEach(x => + { + var outerContextExpectedAttributes = new Dictionary(); + foreach (var kvp in GetExpectedAttributes(x)) + outerContextExpectedAttributes.Add(kvp.Key, kvp.Value); + outerContextExpectedAttributes.Add("ScopeKey1", "scopeValue1"); - var innerContextExpectedAttributes = new Dictionary(); - foreach(var kvp in _expectedAttributes) - innerContextExpectedAttributes.Add(kvp.Key, kvp.Value); - innerContextExpectedAttributes.Add("ScopeKey1", "scopeValue2"); + var innerContextExpectedAttributes = new Dictionary(); + foreach (var kvp in GetExpectedAttributes(x)) + innerContextExpectedAttributes.Add(kvp.Key, kvp.Value); + innerContextExpectedAttributes.Add("ScopeKey1", "scopeValue2"); - var contextExpectedLogLines = new [] - { - new Assertions.ExpectedLogLine + + contextExpectedLogLines.Add(new Assertions.ExpectedLogLine { - Level = LogUtils.GetLevelName(_loggingFramework, "INFO"), + Level = LogUtils.GetLevelName(x, "INFO"), LogMessage = "Outer Scope", Attributes = outerContextExpectedAttributes - }, - new Assertions.ExpectedLogLine + }); + contextExpectedLogLines.Add(new Assertions.ExpectedLogLine { - Level = LogUtils.GetLevelName(_loggingFramework, "INFO"), + Level = LogUtils.GetLevelName(x, "INFO"), LogMessage = "Inner Scope", Attributes = innerContextExpectedAttributes - } - }; + }); + }); Assertions.LogLinesExist(contextExpectedLogLines, logLines, ignoreAttributeCount: true); } @@ -125,7 +127,7 @@ public void Test() public class Log4NetContextDataFWLatestTests : ContextDataTestsBase { public Log4NetContextDataFWLatestTests(ConsoleDynamicMethodFixtureFWLatest fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.Log4net, false) + : base(fixture, output, false, LoggingFramework.Log4net) { } } @@ -134,7 +136,7 @@ public Log4NetContextDataFWLatestTests(ConsoleDynamicMethodFixtureFWLatest fixtu public class Log4NetContextDataFW471Tests : ContextDataTestsBase { public Log4NetContextDataFW471Tests(ConsoleDynamicMethodFixtureFW471 fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.Log4net, false) + : base(fixture, output, false, LoggingFramework.Log4net) { } } @@ -143,7 +145,7 @@ public Log4NetContextDataFW471Tests(ConsoleDynamicMethodFixtureFW471 fixture, IT public class Log4NetContextDataFW462Tests : ContextDataTestsBase { public Log4NetContextDataFW462Tests(ConsoleDynamicMethodFixtureFW462 fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.Log4net, false) + : base(fixture, output, false, LoggingFramework.Log4net) { } } @@ -152,7 +154,7 @@ public Log4NetContextDataFW462Tests(ConsoleDynamicMethodFixtureFW462 fixture, IT public class Log4NetContextDataNetCoreLatestTests : ContextDataTestsBase { public Log4NetContextDataNetCoreLatestTests(ConsoleDynamicMethodFixtureCoreLatest fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.Log4net, false) + : base(fixture, output, false, LoggingFramework.Log4net) { } } @@ -161,7 +163,7 @@ public Log4NetContextDataNetCoreLatestTests(ConsoleDynamicMethodFixtureCoreLates public class Log4NetContextDataNetCoreOldestTests : ContextDataTestsBase { public Log4NetContextDataNetCoreOldestTests(ConsoleDynamicMethodFixtureCoreOldest fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.Log4net, false) + : base(fixture, output, false, LoggingFramework.Log4net) { } } @@ -174,7 +176,7 @@ public Log4NetContextDataNetCoreOldestTests(ConsoleDynamicMethodFixtureCoreOldes public class NLogContextDataFWLatestTests : ContextDataTestsBase { public NLogContextDataFWLatestTests(ConsoleDynamicMethodFixtureFWLatest fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.NLog, false) + : base(fixture, output, false, LoggingFramework.NLog) { } } @@ -183,7 +185,7 @@ public NLogContextDataFWLatestTests(ConsoleDynamicMethodFixtureFWLatest fixture, public class NLogContextDataFW471Tests : ContextDataTestsBase { public NLogContextDataFW471Tests(ConsoleDynamicMethodFixtureFW471 fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.NLog, false) + : base(fixture, output, false, LoggingFramework.NLog) { } } @@ -192,7 +194,7 @@ public NLogContextDataFW471Tests(ConsoleDynamicMethodFixtureFW471 fixture, ITest public class NLogContextDataFW462Tests : ContextDataTestsBase { public NLogContextDataFW462Tests(ConsoleDynamicMethodFixtureFW462 fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.NLog, false) + : base(fixture, output, false, LoggingFramework.NLog) { } } @@ -201,7 +203,7 @@ public NLogContextDataFW462Tests(ConsoleDynamicMethodFixtureFW462 fixture, ITest public class NLogContextDataNetCoreLatestTests : ContextDataTestsBase { public NLogContextDataNetCoreLatestTests(ConsoleDynamicMethodFixtureCoreLatest fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.NLog, false) + : base(fixture, output, false, LoggingFramework.NLog) { } } @@ -210,7 +212,7 @@ public NLogContextDataNetCoreLatestTests(ConsoleDynamicMethodFixtureCoreLatest f public class NLogContextDataNetCoreOldestTests : ContextDataTestsBase { public NLogContextDataNetCoreOldestTests(ConsoleDynamicMethodFixtureCoreOldest fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.NLog, false) + : base(fixture, output, false, LoggingFramework.NLog) { } } @@ -223,7 +225,7 @@ public NLogContextDataNetCoreOldestTests(ConsoleDynamicMethodFixtureCoreOldest f public class SerilogContextDataFWLatestTests : ContextDataTestsBase { public SerilogContextDataFWLatestTests(ConsoleDynamicMethodFixtureFWLatest fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.Serilog, false) + : base(fixture, output, false, LoggingFramework.Serilog) { } } @@ -232,7 +234,7 @@ public SerilogContextDataFWLatestTests(ConsoleDynamicMethodFixtureFWLatest fixtu public class SerilogContextDataFW471Tests : ContextDataTestsBase { public SerilogContextDataFW471Tests(ConsoleDynamicMethodFixtureFW471 fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.Serilog, false) + : base(fixture, output, false, LoggingFramework.Serilog) { } } @@ -241,7 +243,7 @@ public SerilogContextDataFW471Tests(ConsoleDynamicMethodFixtureFW471 fixture, IT public class SerilogContextDataFW462Tests : ContextDataTestsBase { public SerilogContextDataFW462Tests(ConsoleDynamicMethodFixtureFW462 fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.Serilog, false) + : base(fixture, output, false, LoggingFramework.Serilog) { } } @@ -250,7 +252,7 @@ public SerilogContextDataFW462Tests(ConsoleDynamicMethodFixtureFW462 fixture, IT public class SerilogContextDataNetCoreLatestTests : ContextDataTestsBase { public SerilogContextDataNetCoreLatestTests(ConsoleDynamicMethodFixtureCoreLatest fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.Serilog, false) + : base(fixture, output, false, LoggingFramework.Serilog) { } } @@ -259,7 +261,7 @@ public SerilogContextDataNetCoreLatestTests(ConsoleDynamicMethodFixtureCoreLates public class SerilogContextDataNetCoreOldestTests : ContextDataTestsBase { public SerilogContextDataNetCoreOldestTests(ConsoleDynamicMethodFixtureCoreOldest fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.Serilog, false) + : base(fixture, output, false, LoggingFramework.Serilog) { } } @@ -272,7 +274,7 @@ public SerilogContextDataNetCoreOldestTests(ConsoleDynamicMethodFixtureCoreOldes public class MELContextDataFWLatestTests : ContextDataTestsBase { public MELContextDataFWLatestTests(ConsoleDynamicMethodFixtureFWLatest fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.MicrosoftLogging, true) + : base(fixture, output, true, LoggingFramework.MicrosoftLogging) { } } @@ -281,7 +283,7 @@ public MELContextDataFWLatestTests(ConsoleDynamicMethodFixtureFWLatest fixture, public class MELContextDataNetCoreLatestTests : ContextDataTestsBase { public MELContextDataNetCoreLatestTests(ConsoleDynamicMethodFixtureCoreLatest fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.MicrosoftLogging, true) + : base(fixture, output, true, LoggingFramework.MicrosoftLogging) { } } @@ -290,7 +292,7 @@ public MELContextDataNetCoreLatestTests(ConsoleDynamicMethodFixtureCoreLatest fi public class MELContextDataNetCoreOldestTests : ContextDataTestsBase { public MELContextDataNetCoreOldestTests(ConsoleDynamicMethodFixtureCoreOldest fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.MicrosoftLogging, true) + : base(fixture, output, true, LoggingFramework.MicrosoftLogging) { } } @@ -301,7 +303,7 @@ public MELContextDataNetCoreOldestTests(ConsoleDynamicMethodFixtureCoreOldest fi public class SitecoreContextDataFWLatestTests : ContextDataTestsBase { public SitecoreContextDataFWLatestTests(ConsoleDynamicMethodFixtureFWLatest fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.Sitecore, false) + : base(fixture, output, false, LoggingFramework.Sitecore) { } } @@ -309,18 +311,35 @@ public SitecoreContextDataFWLatestTests(ConsoleDynamicMethodFixtureFWLatest fixt public class SitecoreContextDataFW48Tests : ContextDataTestsBase { public SitecoreContextDataFW48Tests(ConsoleDynamicMethodFixtureFW48 fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.Sitecore, false) + : base(fixture, output, false, LoggingFramework.Sitecore) + { + } + } + + public class SitecorePlusLog4NetContextDataFWLatestTests : ContextDataTestsBase + { + public SitecorePlusLog4NetContextDataFWLatestTests(ConsoleDynamicMethodFixtureFWLatest fixture, ITestOutputHelper output) + : base(fixture, output, false, LoggingFramework.Sitecore, LoggingFramework.Log4net) { } } + public class SitecorePlusLog4NetContextDataFW48Tests : ContextDataTestsBase + { + public SitecorePlusLog4NetContextDataFW48Tests(ConsoleDynamicMethodFixtureFW48 fixture, ITestOutputHelper output) + : base(fixture, output, false, LoggingFramework.Sitecore, LoggingFramework.Log4net) + { + } + } + + #endregion // Sitecore - + #region SEL public class SELContextDataFWLatestTests : ContextDataTestsBase { public SELContextDataFWLatestTests(ConsoleDynamicMethodFixtureFWLatest fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.SerilogEL, true) + : base(fixture, output, true, LoggingFramework.SerilogEL) { } } @@ -328,7 +347,7 @@ public SELContextDataFWLatestTests(ConsoleDynamicMethodFixtureFWLatest fixture, public class SELContextDataFW48Tests : ContextDataTestsBase { public SELContextDataFW48Tests(ConsoleDynamicMethodFixtureFW48 fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.SerilogEL, true) + : base(fixture, output, true, LoggingFramework.SerilogEL) { } } @@ -336,7 +355,7 @@ public SELContextDataFW48Tests(ConsoleDynamicMethodFixtureFW48 fixture, ITestOut public class SELContextDataCoreLatestTests : ContextDataTestsBase { public SELContextDataCoreLatestTests(ConsoleDynamicMethodFixtureCoreLatest fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.SerilogEL, true) + : base(fixture, output, true, LoggingFramework.SerilogEL) { } } @@ -344,7 +363,7 @@ public SELContextDataCoreLatestTests(ConsoleDynamicMethodFixtureCoreLatest fixtu public class SELContextDataCoreOldestTests : ContextDataTestsBase { public SELContextDataCoreOldestTests(ConsoleDynamicMethodFixtureCoreOldest fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.SerilogEL, true) + : base(fixture, output, true, LoggingFramework.SerilogEL) { } } @@ -355,7 +374,7 @@ public SELContextDataCoreOldestTests(ConsoleDynamicMethodFixtureCoreOldest fixtu public class NELContextDataFWLatestTests : ContextDataTestsBase { public NELContextDataFWLatestTests(ConsoleDynamicMethodFixtureFWLatest fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.NLogEL, true) + : base(fixture, output, true, LoggingFramework.NLogEL) { } } @@ -363,7 +382,7 @@ public NELContextDataFWLatestTests(ConsoleDynamicMethodFixtureFWLatest fixture, public class NELContextDataCoreLatestTests : ContextDataTestsBase { public NELContextDataCoreLatestTests(ConsoleDynamicMethodFixtureCoreLatest fixture, ITestOutputHelper output) - : base(fixture, output, LoggingFramework.NLogEL, true) + : base(fixture, output, true, LoggingFramework.NLogEL) { } } diff --git a/tests/Agent/IntegrationTests/SharedApplications/Common/MultiFunctionApplicationHelpers/NetStandardLibraries/LogInstrumentation/LoggingTester.cs b/tests/Agent/IntegrationTests/SharedApplications/Common/MultiFunctionApplicationHelpers/NetStandardLibraries/LogInstrumentation/LoggingTester.cs index ab811fe8aa..224e561371 100644 --- a/tests/Agent/IntegrationTests/SharedApplications/Common/MultiFunctionApplicationHelpers/NetStandardLibraries/LogInstrumentation/LoggingTester.cs +++ b/tests/Agent/IntegrationTests/SharedApplications/Common/MultiFunctionApplicationHelpers/NetStandardLibraries/LogInstrumentation/LoggingTester.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; +using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using NewRelic.Agent.IntegrationTests.Shared.ReflectionHelpers; @@ -13,81 +14,84 @@ namespace MultiFunctionApplicationHelpers.NetStandardLibraries.LogInstrumentatio [Library] public class LoggingTester { - private static ILoggingAdapter _log; + private static Dictionary _logs; [LibraryMethod] public static void SetFramework(string loggingFramework, string loggingPort) { + _logs ??= new Dictionary(); + ILoggingAdapter logger = null; switch (loggingFramework.ToUpper()) { case "LOG4NET": - _log = new Log4NetLoggingAdapter(); + logger = new Log4NetLoggingAdapter(); break; case "SERILOG": - _log = new SerilogLoggingAdapter(); + logger = new SerilogLoggingAdapter(); break; case "SERILOGWEB": // .NET 8.0+ ONLY #if NET8_0_OR_GREATER - _log = new SerilogLoggingWebAdapter(loggingPort); + logger = new SerilogLoggingWebAdapter(loggingPort); #endif break; case "MICROSOFTLOGGING": #if NETCOREAPP2_1_OR_GREATER || NET48_OR_GREATER - _log = new MicrosoftLoggingLoggingAdapter(); + logger = new MicrosoftLoggingLoggingAdapter(); #endif break; case "DUMMYMEL": #if NETCOREAPP2_1_OR_GREATER || NET48_OR_GREATER - _log = new DummyMELAdapter(); + logger = new DummyMELAdapter(); #endif break; case "NLOG": - _log = new NLogLoggingAdapter(); + logger = new NLogLoggingAdapter(); break; case "SITECORE": #if NET48_OR_GREATER - _log = new SitecoreLoggingAdapter(); + logger = new SitecoreLoggingAdapter(); #endif break; case "SERILOGEL": #if NETCOREAPP2_1_OR_GREATER || NET48_OR_GREATER - _log = new SerilogExtensionsLoggingAdapter(); + logger = new SerilogExtensionsLoggingAdapter(); #endif break; case "NLOGEL": #if NET8_0_OR_GREATER || NET481_OR_GREATER - _log = new NLogExtensionsLoggingAdapter(); + logger = new NLogExtensionsLoggingAdapter(); #endif break; default: throw new System.ArgumentNullException(nameof(loggingFramework)); } + _logs[loggingFramework.ToUpper()] = logger; } [LibraryMethod] public static void Configure() { - _log.Configure(); + _logs.Values.ToList().ForEach(l => l.Configure()); } [LibraryMethod] public static void ConfigureWithInfoLevelEnabled() { - _log.ConfigureWithInfoLevelEnabled(); + _logs.Values.ToList().ForEach(l => l.ConfigureWithInfoLevelEnabled()); } [LibraryMethod] public static void ConfigurePatternLayoutAppenderForDecoration() { - _log.ConfigurePatternLayoutAppenderForDecoration(); + _logs.Values.ToList().ForEach(l => l.ConfigurePatternLayoutAppenderForDecoration()); } [LibraryMethod] public static void ConfigureJsonLayoutAppenderForDecoration() { - _log.ConfigureJsonLayoutAppenderForDecoration(); + _logs.Values.ToList().ForEach(l => l.ConfigureJsonLayoutAppenderForDecoration()); } [LibraryMethod] @@ -98,6 +102,11 @@ public static void CreateSingleLogMessage(string message, string level) [LibraryMethod] public static void CreateSingleLogMessage(string message, string level, string context = null) + { + _logs.Keys.ToList().ForEach(k => CreateSingleLogMessage(k, message, level, context)); + } + [LibraryMethod] + public static void CreateSingleLogMessage(string logger, string message, string level, string context = null) { var contextDict = new Dictionary(); @@ -116,29 +125,31 @@ public static void CreateSingleLogMessage(string message, string level, string c } } + string key = logger.ToUpper(); + switch (level.ToUpper()) { case "DEBUG": - _log.Debug(message); + _logs[key].Debug(message); break; case "INFO": - _log.Info(message, contextDict); + _logs[key].Info(message, contextDict); break; case "WARN": case "WARNING": - _log.Warn(message); + _logs[key].Warn(message); break; case "ERROR": - _log.Error(ExceptionBuilder.BuildException(message)); + _logs[key].Error(ExceptionBuilder.BuildException(message)); break; case "FATAL": - _log.Fatal(message); + _logs[key].Fatal(message); break; case "NOMESSAGE": - _log.ErrorNoMessage(ExceptionBuilder.BuildException(message)); + _logs[key].ErrorNoMessage(ExceptionBuilder.BuildException(message)); break; default: - _log.Info(message); + _logs[key].Info(message); break; } } @@ -147,7 +158,7 @@ public static void CreateSingleLogMessage(string message, string level, string c public static void CreateSingleLogMessageWithParam(string message) { var param = new Person() { Id = 12345, Name = "John Smith" }; - _log.InfoWithParam(message, param); + _logs.Values.ToList().ForEach(l => l.InfoWithParam(message, param)); } [LibraryMethod] @@ -202,7 +213,7 @@ public static void CreateSingleLogMessageInTransactionWithParam(string message) [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] public static void LogMessageInNestedScopes() { - _log.LogMessageInNestedScopes(); + _logs.Values.ToList().ForEach(l => l.LogMessageInNestedScopes()); } }