diff --git a/CHANGELOG.md b/CHANGELOG.md index e7af0425..ffed61f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Changelog -### Version 2.6.0-beta1 +### Version 2.6.0-beta3 +- [NetStandard Support for TraceListener](https://github.com/Microsoft/ApplicationInsights-dotnet-logging/pull/166) +- [NetStandard Support for NLog and log4net](https://github.com/Microsoft/ApplicationInsights-dotnet-logging/pull/167) +- [NLog and log4net can Flush](https://github.com/Microsoft/ApplicationInsights-dotnet-logging/pull/167) +- Update log4net reference to [2.0.6](https://www.nuget.org/packages/log4net/2.0.6) + +### Version 2.6.0-beta2 - [Include NLog GlobalDiagnosticsContext properties](https://github.com/Microsoft/ApplicationInsights-dotnet-logging/pull/152) - [Remove automatic collection of User Id](https://github.com/Microsoft/ApplicationInsights-dotnet-logging/issues/153) diff --git a/Logging.sln b/Logging.sln index b454b8a2..8abc07d7 100644 --- a/Logging.sln +++ b/Logging.sln @@ -59,6 +59,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelloWorldTest", "HelloWorl EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TraceListener.netcoreapp10.Tests", "test\TraceListener.netcoreapp10.Tests\TraceListener.netcoreapp10.Tests.csproj", "{11E4A92F-154E-450B-8508-437C28744BC2}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLogTarget.NetCoreApp10.Tests", "test\NLogTarget.NetCoreApp10.Tests\NLogTarget.NetCoreApp10.Tests.csproj", "{B24CB3C6-A3A0-4190-97E4-E847C7B1A691}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Log4NetAppender.NetCoreApp10.Tests", "test\Log4NetAppender.NetCoreApp10.Tests\Log4NetAppender.NetCoreApp10.Tests.csproj", "{F74826DB-53B1-4588-BD02-4DD25DCBF292}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution test\CommonTestShared\CommonTestShared.projitems*{3b9ab7fa-562d-4e4e-86e3-3348426bc0d9}*SharedItemsImports = 13 @@ -205,6 +209,22 @@ Global {11E4A92F-154E-450B-8508-437C28744BC2}.Release|Any CPU.Build.0 = Release|Any CPU {11E4A92F-154E-450B-8508-437C28744BC2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {11E4A92F-154E-450B-8508-437C28744BC2}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {B24CB3C6-A3A0-4190-97E4-E847C7B1A691}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B24CB3C6-A3A0-4190-97E4-E847C7B1A691}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B24CB3C6-A3A0-4190-97E4-E847C7B1A691}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {B24CB3C6-A3A0-4190-97E4-E847C7B1A691}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {B24CB3C6-A3A0-4190-97E4-E847C7B1A691}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B24CB3C6-A3A0-4190-97E4-E847C7B1A691}.Release|Any CPU.Build.0 = Release|Any CPU + {B24CB3C6-A3A0-4190-97E4-E847C7B1A691}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {B24CB3C6-A3A0-4190-97E4-E847C7B1A691}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {F74826DB-53B1-4588-BD02-4DD25DCBF292}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F74826DB-53B1-4588-BD02-4DD25DCBF292}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F74826DB-53B1-4588-BD02-4DD25DCBF292}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {F74826DB-53B1-4588-BD02-4DD25DCBF292}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {F74826DB-53B1-4588-BD02-4DD25DCBF292}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F74826DB-53B1-4588-BD02-4DD25DCBF292}.Release|Any CPU.Build.0 = Release|Any CPU + {F74826DB-53B1-4588-BD02-4DD25DCBF292}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {F74826DB-53B1-4588-BD02-4DD25DCBF292}.Release|Mixed Platforms.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -229,6 +249,8 @@ Global {625DABF6-C4AD-41A9-B2C0-04C9FEC2ADCF} = {2FCC45B3-D820-405D-87FA-467C96465BB1} {7AAF876A-43B5-4F47-B402-DC5C94948F3F} = {2FCC45B3-D820-405D-87FA-467C96465BB1} {11E4A92F-154E-450B-8508-437C28744BC2} = {2FCC45B3-D820-405D-87FA-467C96465BB1} + {B24CB3C6-A3A0-4190-97E4-E847C7B1A691} = {2FCC45B3-D820-405D-87FA-467C96465BB1} + {F74826DB-53B1-4588-BD02-4DD25DCBF292} = {2FCC45B3-D820-405D-87FA-467C96465BB1} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AC8888B1-0E98-49D7-BE15-EB97A6261C0D} diff --git a/src/Log4NetAppender/ApplicationInsightsAppender.cs b/src/Log4NetAppender/ApplicationInsightsAppender.cs index 85e4ecf1..0f3c0148 100644 --- a/src/Log4NetAppender/ApplicationInsightsAppender.cs +++ b/src/Log4NetAppender/ApplicationInsightsAppender.cs @@ -65,6 +65,17 @@ public override void ActivateOptions() this.telemetryClient.Context.GetInternalContext().SdkVersion = SdkVersionUtils.GetSdkVersion("log4net:"); } + /// + /// Flushes any buffered log data + /// + /// The maximum time to wait for logging events to be flushed + /// True if all logging events were flushed successfully, else false + public override bool Flush(int millisecondsTimeout) + { + this.telemetryClient.Flush(); + return true; + } + /// /// Append LoggingEvent Application Insights logging framework. /// diff --git a/src/Log4NetAppender/AssemblyInfo.cs b/src/Log4NetAppender/AssemblyInfo.cs index 44a3028f..1eb707b8 100644 --- a/src/Log4NetAppender/AssemblyInfo.cs +++ b/src/Log4NetAppender/AssemblyInfo.cs @@ -31,6 +31,7 @@ [assembly: Guid("599daa99-7d0b-4cae-81b8-3a73509f2efa")] [assembly: InternalsVisibleTo("Microsoft.ApplicationInsights.Log4NetAppender.Net45.Tests, PublicKey=" + AssemblyInfo.PublicKey)] +[assembly: InternalsVisibleTo("Microsoft.ApplicationInsights.Log4NetAppender.NetCoreApp10.Tests, PublicKey=" + AssemblyInfo.PublicKey)] internal static class AssemblyInfo { diff --git a/src/Log4NetAppender/Log4NetAppender.csproj b/src/Log4NetAppender/Log4NetAppender.csproj index d326ec86..180709e4 100644 --- a/src/Log4NetAppender/Log4NetAppender.csproj +++ b/src/Log4NetAppender/Log4NetAppender.csproj @@ -10,7 +10,7 @@ false false - net45 + net45;netstandard1.3 Microsoft.ApplicationInsights.Log4NetAppender Microsoft.ApplicationInsights.Log4NetAppender @@ -41,9 +41,16 @@ All - + + + + + + + + diff --git a/src/NLogTarget/ApplicationInsightsTarget.cs b/src/NLogTarget/ApplicationInsightsTarget.cs index 07b0cf66..ca71ee9f 100644 --- a/src/NLogTarget/ApplicationInsightsTarget.cs +++ b/src/NLogTarget/ApplicationInsightsTarget.cs @@ -10,15 +10,16 @@ namespace Microsoft.ApplicationInsights.NLogTarget using System; using System.Collections.Generic; using System.Globalization; - + using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility.Implementation; using Microsoft.ApplicationInsights.Implementation; using NLog; + using NLog.Common; using NLog.Targets; - + /// /// NLog Target that routes all logging output to the Application Insights logging framework. /// The messages will be uploaded to the Application Insights cloud service. @@ -49,6 +50,37 @@ internal TelemetryClient TelemetryClient get { return this.telemetryClient; } } + internal void BuildPropertyBag(LogEventInfo logEvent, ITelemetry trace) + { + trace.Timestamp = logEvent.TimeStamp; + trace.Sequence = logEvent.SequenceID.ToString(CultureInfo.InvariantCulture); + + IDictionary propertyBag; + + if (trace is ExceptionTelemetry) + { + propertyBag = ((ExceptionTelemetry)trace).Properties; + } + else + { + propertyBag = ((TraceTelemetry)trace).Properties; + } + + if (!string.IsNullOrEmpty(logEvent.LoggerName)) + { + propertyBag.Add("LoggerName", logEvent.LoggerName); + } + + if (logEvent.UserStackFrame != null) + { + propertyBag.Add("UserStackFrame", logEvent.UserStackFrame.ToString()); + propertyBag.Add("UserStackFrameNumber", logEvent.UserStackFrameNumber.ToString(CultureInfo.InvariantCulture)); + } + + this.LoadGlobalDiagnosticsContextProperties(propertyBag); + this.LoadLogEventProperties(logEvent, propertyBag); + } + /// /// Initializes the Target and perform instrumentationKey validation. /// @@ -86,6 +118,23 @@ protected override void Write(LogEventInfo logEvent) } } + /// + /// Flush any pending log messages + /// + /// The asynchronous continuation + protected override void FlushAsync(AsyncContinuation asyncContinuation) + { + try + { + this.TelemetryClient.Flush(); + asyncContinuation(null); + } + catch (Exception ex) + { + asyncContinuation(ex); + } + } + private void SendException(LogEventInfo logEvent) { var exceptionTelemetry = new ExceptionTelemetry(logEvent.Exception) @@ -113,37 +162,6 @@ private void SendTrace(LogEventInfo logEvent) this.telemetryClient.Track(trace); } - private void BuildPropertyBag(LogEventInfo logEvent, ITelemetry trace) - { - trace.Timestamp = logEvent.TimeStamp; - trace.Sequence = logEvent.SequenceID.ToString(CultureInfo.InvariantCulture); - - IDictionary propertyBag; - - if (trace is ExceptionTelemetry) - { - propertyBag = ((ExceptionTelemetry)trace).Properties; - } - else - { - propertyBag = ((TraceTelemetry)trace).Properties; - } - - if (!string.IsNullOrEmpty(logEvent.LoggerName)) - { - propertyBag.Add("LoggerName", logEvent.LoggerName); - } - - if (logEvent.UserStackFrame != null) - { - propertyBag.Add("UserStackFrame", logEvent.UserStackFrame.ToString()); - propertyBag.Add("UserStackFrameNumber", logEvent.UserStackFrameNumber.ToString(CultureInfo.InvariantCulture)); - } - - this.LoadGlobalDiagnosticsContextProperties(propertyBag); - this.LoadLogEventProperties(logEvent, propertyBag); - } - private void LoadGlobalDiagnosticsContextProperties(IDictionary propertyBag) { foreach (string key in GlobalDiagnosticsContext.GetNames()) diff --git a/src/NLogTarget/AssemblyInfo.cs b/src/NLogTarget/AssemblyInfo.cs index ad34e270..bff59a6a 100644 --- a/src/NLogTarget/AssemblyInfo.cs +++ b/src/NLogTarget/AssemblyInfo.cs @@ -31,6 +31,7 @@ [assembly: Guid("44abc834-bd19-449f-ad64-f8c83ca078ed")] [assembly: InternalsVisibleTo("Microsoft.ApplicationInsights.NLogTarget.Net45.Tests, PublicKey=" + AssemblyInfo.PublicKey)] +[assembly: InternalsVisibleTo("Microsoft.ApplicationInsights.NLogTarget.NetCoreApp10.Tests, PublicKey=" + AssemblyInfo.PublicKey)] internal static class AssemblyInfo { diff --git a/src/NLogTarget/NLogTarget.csproj b/src/NLogTarget/NLogTarget.csproj index 88b75783..45b4dadd 100644 --- a/src/NLogTarget/NLogTarget.csproj +++ b/src/NLogTarget/NLogTarget.csproj @@ -10,7 +10,7 @@ false false - net45 + net45;netstandard1.3 Microsoft.ApplicationInsights.NLogTarget Microsoft.ApplicationInsights.NLogTarget @@ -41,6 +41,13 @@ All + + + + + + + diff --git a/test/Log4NetAppender.Net45.Tests/ApplicationInsightsAppenderTests.cs b/test/Log4NetAppender.Net45.Tests/ApplicationInsightsAppenderTests.cs index 4cfbbe49..5d4e83e4 100644 --- a/test/Log4NetAppender.Net45.Tests/ApplicationInsightsAppenderTests.cs +++ b/test/Log4NetAppender.Net45.Tests/ApplicationInsightsAppenderTests.cs @@ -11,6 +11,7 @@ namespace Microsoft.ApplicationInsights.Log4NetAppender.Tests using System.Collections.Generic; using System.IO; using System.Linq; + using System.Reflection; using System.Text; using log4net; @@ -31,6 +32,7 @@ namespace Microsoft.ApplicationInsights.Log4NetAppender.Tests [TestClass] public class ApplicationInsightsAppenderTests { + private static readonly Assembly callingAssembly = typeof(ApplicationInsightsAppenderTests).GetTypeInfo().Assembly; private AdapterHelper adapterHelper; private AppendableLogger appendableLogger; @@ -92,12 +94,12 @@ public void ValidateLoggingIsWorking() ApplicationInsightsAppenderTests.InitializeLog4NetAIAdapter(string.Format(@"", instrumentationKey)); // Set up error handler to intercept exception - ApplicationInsightsAppender aiAppender = (ApplicationInsightsAppender)log4net.LogManager.GetRepository().GetAppenders()[0]; + ApplicationInsightsAppender aiAppender = (ApplicationInsightsAppender)log4net.LogManager.GetRepository(callingAssembly).GetAppenders()[0]; log4net.Util.OnlyOnceErrorHandler errorHandler = new log4net.Util.OnlyOnceErrorHandler(); aiAppender.ErrorHandler = errorHandler; // Log something - ILog logger = log4net.LogManager.GetLogger("TestAIAppender"); + ILog logger = log4net.LogManager.GetLogger(callingAssembly, "TestAIAppender"); for (int i = 0; i < 1500; i++) { logger.Debug("Trace Debug" + i + DateTime.Now); @@ -339,7 +341,7 @@ internal static void InitializeLog4NetAIAdapter(string adapterComponentIdSnippet using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xmlRawText))) { - XmlConfigurator.Configure(stream); + XmlConfigurator.Configure(log4net.LogManager.GetRepository(callingAssembly), stream); } } @@ -347,7 +349,7 @@ private void VerifyInitializationSuccess(Action testAction, string expectedInstr { this.VerifyInitializationError(testAction, 0, null); - ApplicationInsightsAppender aiAppender = (ApplicationInsightsAppender)log4net.LogManager.GetRepository().GetAppenders()[0]; + ApplicationInsightsAppender aiAppender = (ApplicationInsightsAppender)log4net.LogManager.GetRepository(callingAssembly).GetAppenders()[0]; Assert.AreEqual(expectedInstrumentationKey, aiAppender.InstrumentationKey); } @@ -401,12 +403,12 @@ private IEnumerable GetAllInnerExceptions(Exception ex) private void SendMessagesToMockChannel(string instrumentationKey) { // Set up error handler to intercept exception - ApplicationInsightsAppender aiAppender = (ApplicationInsightsAppender)log4net.LogManager.GetRepository().GetAppenders()[0]; + ApplicationInsightsAppender aiAppender = (ApplicationInsightsAppender)log4net.LogManager.GetRepository(callingAssembly).GetAppenders()[0]; log4net.Util.OnlyOnceErrorHandler errorHandler = new log4net.Util.OnlyOnceErrorHandler(); aiAppender.ErrorHandler = errorHandler; // Log something - ILog logger = log4net.LogManager.GetLogger("TestAIAppender"); + ILog logger = log4net.LogManager.GetLogger(callingAssembly, "TestAIAppender"); logger.Debug("Trace Debug"); logger.Error("Trace Error"); logger.Fatal("Trace Fatal"); @@ -422,12 +424,12 @@ private void VerifyPropertiesInTelemetry() TelemetryConfiguration.Active.TelemetryChannel = this.adapterHelper.Channel; // Set up error handler to intercept exception - ApplicationInsightsAppender aiAppender = (ApplicationInsightsAppender)LogManager.GetRepository().GetAppenders()[0]; + ApplicationInsightsAppender aiAppender = (ApplicationInsightsAppender)LogManager.GetRepository(callingAssembly).GetAppenders()[0]; OnlyOnceErrorHandler errorHandler = new OnlyOnceErrorHandler(); aiAppender.ErrorHandler = errorHandler; // Log something - ILog logger = LogManager.GetLogger("TestAIAppender"); + ILog logger = LogManager.GetLogger(callingAssembly, "TestAIAppender"); logger.Debug("Trace Debug"); ITelemetry[] sentItems = this.adapterHelper.Channel.SentItems; @@ -463,11 +465,11 @@ public AppendableLogger() ApplicationInsightsAppenderTests.InitializeLog4NetAIAdapter(string.Empty); // Set up error handler to intercept exception - var aiAppender = (ApplicationInsightsAppender)LogManager.GetRepository().GetAppenders()[0]; + var aiAppender = (ApplicationInsightsAppender)LogManager.GetRepository(callingAssembly).GetAppenders()[0]; var errorHandler = new OnlyOnceErrorHandler(); aiAppender.ErrorHandler = errorHandler; - this.Logger = LogManager.GetLogger("TestAIAppender"); + this.Logger = LogManager.GetLogger(callingAssembly, "TestAIAppender"); } public ILog Logger { get; set; } diff --git a/test/Log4NetAppender.Net45.Tests/Log4NetAppender.Net45.Tests.csproj b/test/Log4NetAppender.Net45.Tests/Log4NetAppender.Net45.Tests.csproj index 70d1adb9..620b6e5f 100644 --- a/test/Log4NetAppender.Net45.Tests/Log4NetAppender.Net45.Tests.csproj +++ b/test/Log4NetAppender.Net45.Tests/Log4NetAppender.Net45.Tests.csproj @@ -15,7 +15,7 @@ All - + diff --git a/test/Log4NetAppender.NetCoreApp10.Tests/Log4NetAppender.NetCoreApp10.Tests.csproj b/test/Log4NetAppender.NetCoreApp10.Tests/Log4NetAppender.NetCoreApp10.Tests.csproj new file mode 100644 index 00000000..37a0621c --- /dev/null +++ b/test/Log4NetAppender.NetCoreApp10.Tests/Log4NetAppender.NetCoreApp10.Tests.csproj @@ -0,0 +1,39 @@ + + + + Microsoft.ApplicationInsights.Log4NetAppender.Tests + Microsoft.ApplicationInsights.Log4NetAppender.NetCoreApp10.Tests + netcoreapp1.0 + false + false + + + + + + + + + + + + + + All + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/NLogTarget.Net45.Tests/NLogTargetTests.cs b/test/NLogTarget.Net45.Tests/NLogTargetTests.cs index a68b935c..efde69d0 100644 --- a/test/NLogTarget.Net45.Tests/NLogTargetTests.cs +++ b/test/NLogTarget.Net45.Tests/NLogTargetTests.cs @@ -382,7 +382,7 @@ public void NLogFatalIsSentAsCriticalTraceItem() [TestCategory("NLogTarget")] public void NLogPropertyDuplicateKeyDuplicateValue() { - var aiTarget = new PrivateObject(typeof(ApplicationInsightsTarget)); + var aiTarget = new ApplicationInsightsTarget(); var logEventInfo = new LogEventInfo(); var loggerNameVal = "thisisaloggername"; @@ -391,7 +391,7 @@ public void NLogPropertyDuplicateKeyDuplicateValue() var traceTelemetry = new TraceTelemetry(); - aiTarget.Invoke("BuildPropertyBag", logEventInfo, traceTelemetry); + aiTarget.BuildPropertyBag(logEventInfo, traceTelemetry); Assert.IsTrue(traceTelemetry.Properties.ContainsKey("LoggerName")); Assert.AreEqual(loggerNameVal, traceTelemetry.Properties["LoggerName"]); @@ -401,7 +401,7 @@ public void NLogPropertyDuplicateKeyDuplicateValue() [TestCategory("NLogTarget")] public void NLogPropertyDuplicateKeyDifferentValue() { - var aiTarget = new PrivateObject(typeof(ApplicationInsightsTarget)); + var aiTarget = new ApplicationInsightsTarget(); var logEventInfo = new LogEventInfo(); var loggerNameVal = "thisisaloggername"; var loggerNameVal2 = "thisisadifferentloggername"; @@ -411,7 +411,7 @@ public void NLogPropertyDuplicateKeyDifferentValue() var traceTelemetry = new TraceTelemetry(); - aiTarget.Invoke("BuildPropertyBag", logEventInfo, traceTelemetry); + aiTarget.BuildPropertyBag(logEventInfo, traceTelemetry); Assert.IsTrue(traceTelemetry.Properties.ContainsKey("LoggerName")); Assert.AreEqual(loggerNameVal, traceTelemetry.Properties["LoggerName"]); @@ -420,6 +420,22 @@ public void NLogPropertyDuplicateKeyDifferentValue() Assert.AreEqual(loggerNameVal2, traceTelemetry.Properties["LoggerName_1"]); } + + [TestMethod] + [TestCategory("NLogTarget")] + public void NLogTargetFlushesTelemetryClient() + { + var aiLogger = this.CreateTargetWithGivenInstrumentationKey(); + + var flushEvent = new System.Threading.ManualResetEvent(false); + Exception flushException = null; + NLog.Common.AsyncContinuation asyncContinuation = (ex) => { flushException = ex; flushEvent.Set(); }; + aiLogger.Factory.Flush(asyncContinuation, 5000); + Assert.IsTrue(flushEvent.WaitOne(5000)); + Assert.IsNotNull(flushException); + Assert.AreEqual("Flush called", flushException.Message); + } + private void VerifyMessagesInMockChannel(Logger aiLogger, string instrumentationKey) { aiLogger.Trace("Sample trace message"); diff --git a/test/NLogTarget.NetCoreApp10.Tests/NLogTarget.NetCoreApp10.Tests.csproj b/test/NLogTarget.NetCoreApp10.Tests/NLogTarget.NetCoreApp10.Tests.csproj new file mode 100644 index 00000000..96e5e343 --- /dev/null +++ b/test/NLogTarget.NetCoreApp10.Tests/NLogTarget.NetCoreApp10.Tests.csproj @@ -0,0 +1,38 @@ + + + + Microsoft.ApplicationInsights.NLogTarget.Tests + Microsoft.ApplicationInsights.NLogTarget.NetCoreApp10.Tests + netcoreapp1.0 + false + false + + + + + + + + + + + + + All + + + + + + + + + + + + + + + + + \ No newline at end of file