From b3506639ac7b32d9fed62feb18082ff4f9ff8be2 Mon Sep 17 00:00:00 2001 From: xiang17 Date: Sat, 8 May 2021 18:31:42 -0700 Subject: [PATCH 01/12] Add MemoryMappedFileHandler for SelfDiagnostics --- .../MemoryMappedFileHandlerTest.cs | 42 +++++ ...Microsoft.ApplicationInsights.Tests.csproj | 1 + .../MemoryMappedFileHandler.cs | 162 ++++++++++++++++++ 3 files changed, 205 insertions(+) create mode 100644 BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Tracing/SelfDiagnosticsInternals/MemoryMappedFileHandlerTest.cs create mode 100644 BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnosticsInternals/MemoryMappedFileHandler.cs diff --git a/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Tracing/SelfDiagnosticsInternals/MemoryMappedFileHandlerTest.cs b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Tracing/SelfDiagnosticsInternals/MemoryMappedFileHandlerTest.cs new file mode 100644 index 0000000000..34b61d1b62 --- /dev/null +++ b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Tracing/SelfDiagnosticsInternals/MemoryMappedFileHandlerTest.cs @@ -0,0 +1,42 @@ +namespace Microsoft.ApplicationInsights.TestFramework.Extensibility.Implementation.Tracing.SelfDiagnosticsInternals +{ + using System.Diagnostics; + using System.IO; + using System.Text; + using Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing.SelfDiagnosticsInternals; + using Xunit; + + public class MemoryMappedFileHandlerTest + { + public static readonly byte[] MessageOnNewFile = Encoding.UTF8.GetBytes("Successfully opened file.\n"); + + [Fact] + public void MemoryMappedFileHandler_Success() + { + var fileName = Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName) + "." + + Process.GetCurrentProcess().Id + ".log"; + var fileSize = 1024; + using (var handler = new MemoryMappedFileHandler()) + { + handler.CreateLogFile(fileName, fileSize); + + var stream = handler.GetStream(); + stream.Write(MessageOnNewFile, 0, MessageOnNewFile.Length); + } + + var actualBytes = ReadFile(fileName, MessageOnNewFile.Length); + + Assert.Equal(MessageOnNewFile, actualBytes); + } + + private static byte[] ReadFile(string fileName, int byteCount) + { + byte[] actualBytes = new byte[byteCount]; + using (var file = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + file.Read(actualBytes, 0, byteCount); + } + return actualBytes; + } + } +} diff --git a/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Microsoft.ApplicationInsights.Tests.csproj b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Microsoft.ApplicationInsights.Tests.csproj index ba15b0357e..b917c27a3c 100644 --- a/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Microsoft.ApplicationInsights.Tests.csproj +++ b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Microsoft.ApplicationInsights.Tests.csproj @@ -22,6 +22,7 @@ + diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnosticsInternals/MemoryMappedFileHandler.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnosticsInternals/MemoryMappedFileHandler.cs new file mode 100644 index 0000000000..e92922cd4b --- /dev/null +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnosticsInternals/MemoryMappedFileHandler.cs @@ -0,0 +1,162 @@ +namespace Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing.SelfDiagnosticsInternals +{ + using System; + using System.IO; + using System.IO.MemoryMappedFiles; + using System.Threading; + + /// + /// MemoryMappedFileHandler open a MemoryMappedFile of a certain size at a certain file path. + /// The class provides a stream object with proper write position. + /// The stream is cached in ThreadLocal to be thread-safe. + /// + internal class MemoryMappedFileHandler : IDisposable + { + /// + /// t_memoryMappedFileCache is a handle kept in thread-local storage as a cache to indicate whether the cached + /// t_viewStream is created from the current m_memoryMappedFile. + /// + private readonly ThreadLocal memoryMappedFileCache = new ThreadLocal(true); + private readonly ThreadLocal viewStream = new ThreadLocal(true); + +#pragma warning disable CA2213 // Disposed in CloseLogFile, which is called in Dispose + private volatile FileStream underlyingFileStreamForMemoryMappedFile; + private volatile MemoryMappedFile memoryMappedFile; +#pragma warning restore CA2213 // Disposed in CloseLogFile, which is called in Dispose + + private bool disposedValue; + + /// + /// Create a file for MemoryMappedFile. If the file already exists, it will be overwritten. + /// + /// The file path the MemoryMappedFile will be created. + /// The size of the MemoryMappedFile. + /// Thrown if file creation failed. + public void CreateLogFile(string filePath, int fileSize) + { + // Because the API [MemoryMappedFile.CreateFromFile][1](the string version) behaves differently on + // .NET Framework and .NET Core, here I am using the [FileStream version][2] of it. + // Taking the last four prameter values from [.NET Framework] + // (https://referencesource.microsoft.com/#system.core/System/IO/MemoryMappedFiles/MemoryMappedFile.cs,148) + // and [.NET Core] + // (https://github.com/dotnet/runtime/blob/master/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.cs#L152) + // The parameter for FileAccess is different in type but the same in rules, both are Read and Write. + // The parameter for FileShare is different in values and in behavior. + // .NET Framework doesn't allow sharing but .NET Core allows reading by other programs. + // The last two parameters are the same values for both frameworks. + // [1]: https://docs.microsoft.com/dotnet/api/system.io.memorymappedfiles.memorymappedfile.createfromfile?view=net-5.0#System_IO_MemoryMappedFiles_MemoryMappedFile_CreateFromFile_System_String_System_IO_FileMode_System_String_System_Int64_ + // [2]: https://docs.microsoft.com/dotnet/api/system.io.memorymappedfiles.memorymappedfile.createfromfile?view=net-5.0#System_IO_MemoryMappedFiles_MemoryMappedFile_CreateFromFile_System_IO_FileStream_System_String_System_Int64_System_IO_MemoryMappedFiles_MemoryMappedFileAccess_System_IO_HandleInheritability_System_Boolean_ + this.underlyingFileStreamForMemoryMappedFile = + new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 0x1000, FileOptions.None); + + // The parameter values for MemoryMappedFileSecurity, HandleInheritability and leaveOpen are the same + // values for .NET Framework and .NET Core: + // https://referencesource.microsoft.com/#system.core/System/IO/MemoryMappedFiles/MemoryMappedFile.cs,172 + // https://github.com/dotnet/runtime/blob/master/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.cs#L168-L179 + this.memoryMappedFile = MemoryMappedFile.CreateFromFile( + this.underlyingFileStreamForMemoryMappedFile, + null, + fileSize, + MemoryMappedFileAccess.ReadWrite, +#if NET452 + // Only .NET Framework 4.5.2 among all .NET Framework versions is lacking a method omitting this + // default value for MemoryMappedFileSecurity. + // https://docs.microsoft.com/dotnet/api/system.io.memorymappedfiles.memorymappedfile.createfromfile?view=netframework-4.5.2 + // .NET Core simply doesn't support this parameter. + null, +#endif + HandleInheritability.None, + false); + } + + /// + /// Get a MemoryMappedViewStream for the MemoryMappedFile object for the current thread. + /// If no MemoryMappedFile is created yet, return null. + /// + /// A MemoryMappedViewStream for the MemoryMappedFile object. + /// Thrown when access to the memory-mapped file is unauthorized. + /// Thrown in a race condition when the memory-mapped file is closed after null check. + public MemoryMappedViewStream GetStream() + { + if (this.memoryMappedFile == null) + { + return null; + } + + var cachedViewStream = this.viewStream.Value; + + // Each thread has its own MemoryMappedViewStream created from the only one MemoryMappedFile. + // Once worker thread updates the MemoryMappedFile, all the cached ViewStream objects become + // obsolete. + // Each thread creates a new MemoryMappedViewStream the next time it tries to retrieve it. + // Whether the MemoryMappedViewStream is obsolete is determined by comparing the current + // MemoryMappedFile object with the MemoryMappedFile object cached at the creation time of the + // MemoryMappedViewStream. + if (cachedViewStream == null || this.memoryMappedFileCache.Value != this.memoryMappedFile) + { + // Race condition: The code might reach here right after the worker thread sets memoryMappedFile + // to null in CloseLogFile(). + // In this case, let the NullReferenceException be caught and fail silently. + // By design, all events captured will be dropped during a configuration file refresh if + // the file changed, regardless whether the file is deleted or updated. + cachedViewStream = this.memoryMappedFile.CreateViewStream(); + this.viewStream.Value = cachedViewStream; + this.memoryMappedFileCache.Value = this.memoryMappedFile; + } + + return cachedViewStream; + } + + /// + /// Close the all the resources related to the file created for MemoryMappedFile. + /// + public void CloseLogFile() + { + MemoryMappedFile mmf = Interlocked.CompareExchange(ref this.memoryMappedFile, null, this.memoryMappedFile); + if (mmf != null) + { + // Each thread has its own MemoryMappedViewStream created from the only one MemoryMappedFile. + // Once worker thread closes the MemoryMappedFile, all the ViewStream objects should be disposed + // properly. + foreach (MemoryMappedViewStream stream in this.viewStream.Values) + { + if (stream != null) + { + stream.Dispose(); + } + } + + mmf.Dispose(); + } + + FileStream fs = Interlocked.CompareExchange( + ref this.underlyingFileStreamForMemoryMappedFile, + null, + this.underlyingFileStreamForMemoryMappedFile); + fs?.Dispose(); + } + + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (!this.disposedValue) + { + if (disposing) + { + this.CloseLogFile(); + + this.viewStream.Dispose(); + this.memoryMappedFileCache.Dispose(); + } + + this.disposedValue = true; + } + } + } +} From 97d241bb1e51b0571203f7b7349258f408faaf01 Mon Sep 17 00:00:00 2001 From: xiang17 Date: Tue, 11 May 2021 15:32:54 -0700 Subject: [PATCH 02/12] Rename classes and namespace. --- .../MemoryMappedFileHandlerTest.cs | 11 ++++++----- .../Microsoft.ApplicationInsights.Tests.csproj | 5 ++++- .../MemoryMappedFileHandler.cs | 2 +- ...lfDiagnostics.cs => SelfDiagnosticsInitializer.cs} | 8 ++++---- .../Extensibility/TelemetryConfiguration.cs | 2 +- 5 files changed, 16 insertions(+), 12 deletions(-) rename BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Tracing/{SelfDiagnosticsInternals => SelfDiagnostics}/MemoryMappedFileHandlerTest.cs (79%) rename BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/{SelfDiagnosticsInternals => SelfDiagnostics}/MemoryMappedFileHandler.cs (99%) rename BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/{SelfDiagnostics.cs => SelfDiagnosticsInitializer.cs} (87%) diff --git a/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Tracing/SelfDiagnosticsInternals/MemoryMappedFileHandlerTest.cs b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandlerTest.cs similarity index 79% rename from BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Tracing/SelfDiagnosticsInternals/MemoryMappedFileHandlerTest.cs rename to BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandlerTest.cs index 34b61d1b62..a4026792f9 100644 --- a/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Tracing/SelfDiagnosticsInternals/MemoryMappedFileHandlerTest.cs +++ b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandlerTest.cs @@ -1,16 +1,17 @@ -namespace Microsoft.ApplicationInsights.TestFramework.Extensibility.Implementation.Tracing.SelfDiagnosticsInternals +namespace Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing.SelfDiagnostics { using System.Diagnostics; using System.IO; using System.Text; - using Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing.SelfDiagnosticsInternals; - using Xunit; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] public class MemoryMappedFileHandlerTest { public static readonly byte[] MessageOnNewFile = Encoding.UTF8.GetBytes("Successfully opened file.\n"); - [Fact] + [TestMethod] public void MemoryMappedFileHandler_Success() { var fileName = Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName) + "." @@ -26,7 +27,7 @@ public void MemoryMappedFileHandler_Success() var actualBytes = ReadFile(fileName, MessageOnNewFile.Length); - Assert.Equal(MessageOnNewFile, actualBytes); + Assert.AreEqual(MessageOnNewFile, actualBytes); } private static byte[] ReadFile(string fileName, int byteCount) diff --git a/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Microsoft.ApplicationInsights.Tests.csproj b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Microsoft.ApplicationInsights.Tests.csproj index b917c27a3c..fac7b96939 100644 --- a/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Microsoft.ApplicationInsights.Tests.csproj +++ b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Microsoft.ApplicationInsights.Tests.csproj @@ -22,7 +22,6 @@ - @@ -45,6 +44,10 @@ + + + + diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnosticsInternals/MemoryMappedFileHandler.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs similarity index 99% rename from BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnosticsInternals/MemoryMappedFileHandler.cs rename to BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs index e92922cd4b..7624065a7f 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnosticsInternals/MemoryMappedFileHandler.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs @@ -1,4 +1,4 @@ -namespace Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing.SelfDiagnosticsInternals +namespace Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing.SelfDiagnostics { using System; using System.IO; diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnosticsInitializer.cs similarity index 87% rename from BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics.cs rename to BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnosticsInitializer.cs index 5543ae3c93..c45f6c33f2 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnosticsInitializer.cs @@ -6,18 +6,18 @@ /// Self diagnostics class captures the EventSource events sent by Application Insights /// modules and writes them to local file for internal troubleshooting. /// - internal class SelfDiagnostics : IDisposable + internal class SelfDiagnosticsInitializer : IDisposable { /// /// Long-living object that hold relevant resources. /// - private static readonly SelfDiagnostics Instance = new SelfDiagnostics(); + private static readonly SelfDiagnosticsInitializer Instance = new SelfDiagnosticsInitializer(); // Long-living object that holds a refresher which checks whether the configuration file was updated // every 10 seconds. // private readonly SelfDiagnosticsConfigRefresher configRefresher; - static SelfDiagnostics() + static SelfDiagnosticsInitializer() { AppDomain.CurrentDomain.ProcessExit += (sender, eventArgs) => { @@ -25,7 +25,7 @@ static SelfDiagnostics() }; } - private SelfDiagnostics() + private SelfDiagnosticsInitializer() { // this.configRefresher = new SelfDiagnosticsConfigRefresher(); } diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs index c5973fcdc2..85449f8462 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs @@ -64,7 +64,7 @@ static TelemetryConfiguration() Activity.ForceDefaultIdFormat = true; } }); - SelfDiagnostics.EnsureInitialized(); + SelfDiagnosticsInitializer.EnsureInitialized(); } /// From 36bb2aced2905ace2f0daa0e1b6de8a9afabc40f Mon Sep 17 00:00:00 2001 From: xiang17 Date: Tue, 11 May 2021 15:35:48 -0700 Subject: [PATCH 03/12] - --- .../Microsoft.ApplicationInsights.Tests.csproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Microsoft.ApplicationInsights.Tests.csproj b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Microsoft.ApplicationInsights.Tests.csproj index fac7b96939..ba15b0357e 100644 --- a/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Microsoft.ApplicationInsights.Tests.csproj +++ b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Microsoft.ApplicationInsights.Tests.csproj @@ -44,10 +44,6 @@ - - - - From f6f0d121567b46edd15b8ba5a642447de59241db Mon Sep 17 00:00:00 2001 From: xiang17 Date: Tue, 11 May 2021 15:40:05 -0700 Subject: [PATCH 04/12] Use CollectionAssert.AreEqual to compare byte[] --- .../Tracing/SelfDiagnostics/MemoryMappedFileHandlerTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandlerTest.cs b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandlerTest.cs index a4026792f9..595a6e2837 100644 --- a/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandlerTest.cs +++ b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandlerTest.cs @@ -27,7 +27,7 @@ public void MemoryMappedFileHandler_Success() var actualBytes = ReadFile(fileName, MessageOnNewFile.Length); - Assert.AreEqual(MessageOnNewFile, actualBytes); + CollectionAssert.AreEqual(MessageOnNewFile, actualBytes); } private static byte[] ReadFile(string fileName, int byteCount) From b260188d84ab37a520efcad33d275b0a01613b18 Mon Sep 17 00:00:00 2001 From: xiang17 Date: Tue, 11 May 2021 18:55:22 -0700 Subject: [PATCH 05/12] Add circular write logic into MemoryMappedFileHandler class --- .../MemoryMappedFileHandlerTest.cs | 40 ++- .../Implementation/Tracing/CoreEventSource.cs | 9 + .../MemoryMappedFileHandler.cs | 241 +++++++++++++----- 3 files changed, 215 insertions(+), 75 deletions(-) diff --git a/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandlerTest.cs b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandlerTest.cs index 595a6e2837..702d10f857 100644 --- a/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandlerTest.cs +++ b/BASE/Test/Microsoft.ApplicationInsights.Test/Microsoft.ApplicationInsights.Tests/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandlerTest.cs @@ -1,5 +1,6 @@ namespace Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing.SelfDiagnostics { + using System; using System.Diagnostics; using System.IO; using System.Text; @@ -9,7 +10,7 @@ [TestClass] public class MemoryMappedFileHandlerTest { - public static readonly byte[] MessageOnNewFile = Encoding.UTF8.GetBytes("Successfully opened file.\n"); + public static readonly byte[] MessageOnNewFile = MemoryMappedFileHandler.MessageOnNewFile; [TestMethod] public void MemoryMappedFileHandler_Success() @@ -19,10 +20,7 @@ public void MemoryMappedFileHandler_Success() var fileSize = 1024; using (var handler = new MemoryMappedFileHandler()) { - handler.CreateLogFile(fileName, fileSize); - - var stream = handler.GetStream(); - stream.Write(MessageOnNewFile, 0, MessageOnNewFile.Length); + handler.CreateLogFile(".", fileSize); } var actualBytes = ReadFile(fileName, MessageOnNewFile.Length); @@ -30,6 +28,31 @@ public void MemoryMappedFileHandler_Success() CollectionAssert.AreEqual(MessageOnNewFile, actualBytes); } + [TestMethod] + public void MemoryMappedFileHandler_Circular_Success() + { + var fileSize = 1024; + var buffer = new byte[1024]; + var messageToOverflow = Encoding.UTF8.GetBytes("1234567"); + var expectedBytesAtEnd = Encoding.UTF8.GetBytes("1234"); + var expectedBytesAtStart = Encoding.UTF8.GetBytes("567cessfully opened file.\n"); + using (var handler = new MemoryMappedFileHandler()) + { + handler.CreateLogFile(".", fileSize); + + handler.Write(buffer, fileSize - MessageOnNewFile.Length - expectedBytesAtEnd.Length); + + handler.Write(messageToOverflow, messageToOverflow.Length); + } + + var fileName = Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName) + "." + + Process.GetCurrentProcess().Id + ".log"; + var actualBytes = ReadFile(fileName, buffer.Length); + + CollectionAssert.AreEqual(expectedBytesAtStart, SubArray(actualBytes, 0, expectedBytesAtStart.Length)); + CollectionAssert.AreEqual(expectedBytesAtEnd, SubArray(actualBytes, actualBytes.Length - expectedBytesAtEnd.Length, expectedBytesAtEnd.Length)); + } + private static byte[] ReadFile(string fileName, int byteCount) { byte[] actualBytes = new byte[byteCount]; @@ -39,5 +62,12 @@ private static byte[] ReadFile(string fileName, int byteCount) } return actualBytes; } + + private static byte[] SubArray(byte[] array, int offset, int length) + { + byte[] result = new byte[length]; + Array.Copy(array, offset, result, 0, length); + return result; + } } } diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/CoreEventSource.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/CoreEventSource.cs index 3a8e0da409..6b940c2e82 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/CoreEventSource.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/CoreEventSource.cs @@ -656,6 +656,15 @@ public void IngestionResponseTimeEventCounter(float responseDurationInMs) [Event(71, Keywords = Keywords.UserActionable, Message = "TransmissionStatusEvent has failed. Error: {0}. Monitoring will continue.", Level = EventLevel.Error)] public void TransmissionStatusEventError(string error, string appDomainName = "Incorrect") => this.WriteEvent(71, error, this.nameProvider.Name); + [Event(72, Keywords = Keywords.UserActionable, Message = "Failed to create file for self diagnostics at {0}. Error message: {1}.", Level = EventLevel.Error)] + public void SelfDiagnosticsFileCreateException(string logDirectory, string ex) + { + if (this.IsEnabled(EventLevel.Warning, (EventKeywords)(-1))) + { + this.WriteEvent(72, logDirectory, ex); + } + } + [NonEvent] public void TransmissionStatusEventFailed(Exception ex) { diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs index 7624065a7f..4d1d6feda4 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs @@ -1,8 +1,10 @@ namespace Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing.SelfDiagnostics { using System; + using System.Diagnostics; using System.IO; using System.IO.MemoryMappedFiles; + using System.Text; using System.Threading; /// @@ -12,6 +14,8 @@ /// internal class MemoryMappedFileHandler : IDisposable { + public static readonly byte[] MessageOnNewFile = Encoding.UTF8.GetBytes("Successfully opened file.\n"); + /// /// t_memoryMappedFileCache is a handle kept in thread-local storage as a cache to indicate whether the cached /// t_viewStream is created from the current m_memoryMappedFile. @@ -26,85 +30,69 @@ internal class MemoryMappedFileHandler : IDisposable private bool disposedValue; - /// - /// Create a file for MemoryMappedFile. If the file already exists, it will be overwritten. - /// - /// The file path the MemoryMappedFile will be created. - /// The size of the MemoryMappedFile. - /// Thrown if file creation failed. - public void CreateLogFile(string filePath, int fileSize) - { - // Because the API [MemoryMappedFile.CreateFromFile][1](the string version) behaves differently on - // .NET Framework and .NET Core, here I am using the [FileStream version][2] of it. - // Taking the last four prameter values from [.NET Framework] - // (https://referencesource.microsoft.com/#system.core/System/IO/MemoryMappedFiles/MemoryMappedFile.cs,148) - // and [.NET Core] - // (https://github.com/dotnet/runtime/blob/master/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.cs#L152) - // The parameter for FileAccess is different in type but the same in rules, both are Read and Write. - // The parameter for FileShare is different in values and in behavior. - // .NET Framework doesn't allow sharing but .NET Core allows reading by other programs. - // The last two parameters are the same values for both frameworks. - // [1]: https://docs.microsoft.com/dotnet/api/system.io.memorymappedfiles.memorymappedfile.createfromfile?view=net-5.0#System_IO_MemoryMappedFiles_MemoryMappedFile_CreateFromFile_System_String_System_IO_FileMode_System_String_System_Int64_ - // [2]: https://docs.microsoft.com/dotnet/api/system.io.memorymappedfiles.memorymappedfile.createfromfile?view=net-5.0#System_IO_MemoryMappedFiles_MemoryMappedFile_CreateFromFile_System_IO_FileStream_System_String_System_Int64_System_IO_MemoryMappedFiles_MemoryMappedFileAccess_System_IO_HandleInheritability_System_Boolean_ - this.underlyingFileStreamForMemoryMappedFile = - new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 0x1000, FileOptions.None); - - // The parameter values for MemoryMappedFileSecurity, HandleInheritability and leaveOpen are the same - // values for .NET Framework and .NET Core: - // https://referencesource.microsoft.com/#system.core/System/IO/MemoryMappedFiles/MemoryMappedFile.cs,172 - // https://github.com/dotnet/runtime/blob/master/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.cs#L168-L179 - this.memoryMappedFile = MemoryMappedFile.CreateFromFile( - this.underlyingFileStreamForMemoryMappedFile, - null, - fileSize, - MemoryMappedFileAccess.ReadWrite, -#if NET452 - // Only .NET Framework 4.5.2 among all .NET Framework versions is lacking a method omitting this - // default value for MemoryMappedFileSecurity. - // https://docs.microsoft.com/dotnet/api/system.io.memorymappedfiles.memorymappedfile.createfromfile?view=netframework-4.5.2 - // .NET Core simply doesn't support this parameter. - null, -#endif - HandleInheritability.None, - false); - } + private string logDirectory; // Log directory for log files + private int logFileSize; // Log file size in bytes + private long logFilePosition; // The logger will write into the byte at this position + + public string LogDirectory { get => logDirectory; set => logDirectory = value; } + public int LogFileSize { get => logFileSize; private set => logFileSize = value; } /// - /// Get a MemoryMappedViewStream for the MemoryMappedFile object for the current thread. - /// If no MemoryMappedFile is created yet, return null. + /// Create a log file. If the file already exists, it will be overwritten. /// - /// A MemoryMappedViewStream for the MemoryMappedFile object. - /// Thrown when access to the memory-mapped file is unauthorized. - /// Thrown in a race condition when the memory-mapped file is closed after null check. - public MemoryMappedViewStream GetStream() + /// The directory the log file will be created. + /// The size of the log file. + public void CreateLogFile(string logDirectory, int fileSize) { - if (this.memoryMappedFile == null) + try { - return null; - } + Directory.CreateDirectory(logDirectory); + var fileName = Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName) + "." + + Process.GetCurrentProcess().Id + ".log"; + var filePath = Path.Combine(logDirectory, fileName); - var cachedViewStream = this.viewStream.Value; + // Because the API [MemoryMappedFile.CreateFromFile][1](the string version) behaves differently on + // .NET Framework and .NET Core, here I am using the [FileStream version][2] of it. + // Taking the last four prameter values from [.NET Framework] + // (https://referencesource.microsoft.com/#system.core/System/IO/MemoryMappedFiles/MemoryMappedFile.cs,148) + // and [.NET Core] + // (https://github.com/dotnet/runtime/blob/master/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.cs#L152) + // The parameter for FileAccess is different in type but the same in rules, both are Read and Write. + // The parameter for FileShare is different in values and in behavior. + // .NET Framework doesn't allow sharing but .NET Core allows reading by other programs. + // The last two parameters are the same values for both frameworks. + // [1]: https://docs.microsoft.com/dotnet/api/system.io.memorymappedfiles.memorymappedfile.createfromfile?view=net-5.0#System_IO_MemoryMappedFiles_MemoryMappedFile_CreateFromFile_System_String_System_IO_FileMode_System_String_System_Int64_ + // [2]: https://docs.microsoft.com/dotnet/api/system.io.memorymappedfiles.memorymappedfile.createfromfile?view=net-5.0#System_IO_MemoryMappedFiles_MemoryMappedFile_CreateFromFile_System_IO_FileStream_System_String_System_Int64_System_IO_MemoryMappedFiles_MemoryMappedFileAccess_System_IO_HandleInheritability_System_Boolean_ + this.underlyingFileStreamForMemoryMappedFile = + new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.Read, 0x1000, FileOptions.None); - // Each thread has its own MemoryMappedViewStream created from the only one MemoryMappedFile. - // Once worker thread updates the MemoryMappedFile, all the cached ViewStream objects become - // obsolete. - // Each thread creates a new MemoryMappedViewStream the next time it tries to retrieve it. - // Whether the MemoryMappedViewStream is obsolete is determined by comparing the current - // MemoryMappedFile object with the MemoryMappedFile object cached at the creation time of the - // MemoryMappedViewStream. - if (cachedViewStream == null || this.memoryMappedFileCache.Value != this.memoryMappedFile) + // The parameter values for MemoryMappedFileSecurity, HandleInheritability and leaveOpen are the same + // values for .NET Framework and .NET Core: + // https://referencesource.microsoft.com/#system.core/System/IO/MemoryMappedFiles/MemoryMappedFile.cs,172 + // https://github.com/dotnet/runtime/blob/master/src/libraries/System.IO.MemoryMappedFiles/src/System/IO/MemoryMappedFiles/MemoryMappedFile.cs#L168-L179 + this.memoryMappedFile = MemoryMappedFile.CreateFromFile( + this.underlyingFileStreamForMemoryMappedFile, + null, + fileSize, + MemoryMappedFileAccess.ReadWrite, +#if NET452 + // Only .NET Framework 4.5.2 among all .NET Framework versions is lacking a method omitting this + // default value for MemoryMappedFileSecurity. + // https://docs.microsoft.com/dotnet/api/system.io.memorymappedfiles.memorymappedfile.createfromfile?view=netframework-4.5.2 + // .NET Core simply doesn't support this parameter. + null, +#endif + HandleInheritability.None, + false); + this.logDirectory = logDirectory; + this.logFileSize = fileSize; + this.logFilePosition = 0; + this.Write(MessageOnNewFile, MessageOnNewFile.Length); + } + catch (Exception ex) { - // Race condition: The code might reach here right after the worker thread sets memoryMappedFile - // to null in CloseLogFile(). - // In this case, let the NullReferenceException be caught and fail silently. - // By design, all events captured will be dropped during a configuration file refresh if - // the file changed, regardless whether the file is deleted or updated. - cachedViewStream = this.memoryMappedFile.CreateViewStream(); - this.viewStream.Value = cachedViewStream; - this.memoryMappedFileCache.Value = this.memoryMappedFile; + CoreEventSource.Log.SelfDiagnosticsFileCreateException(logDirectory, ex.ToInvariantString()); } - - return cachedViewStream; } /// @@ -143,6 +131,119 @@ public void Dispose() GC.SuppressFinalize(this); } + /// + /// Circularly write to the file. If write operation reaches the end of the file, start writing from the beginning of the file. + /// + /// The buffer which contains the data to be written. + /// The count of bytes to be written. + public void Write(byte[] buffer, int byteCount) + { + try + { + if (this.TryGetLogStream(byteCount, out Stream stream, out int availableByteCount)) + { + if (availableByteCount >= byteCount) + { + stream.Write(buffer, 0, byteCount); + } + else + { + stream.Write(buffer, 0, availableByteCount); + stream.Seek(0, SeekOrigin.Begin); + stream.Write(buffer, availableByteCount, byteCount - availableByteCount); + } + } + } + catch (Exception) + { + // A concurrent race condition: memory mapped file is disposed in another thread after TryGetLogStream() finishes. + // In this case, silently fail. + } + } + + /// + /// Try to get the log stream which is seeked to the position where the next line of log should be written. + /// + /// The number of bytes that need to be written. + /// When this method returns, contains the Stream object where `byteCount` of bytes can be written. + /// The number of bytes that is remaining until the end of the stream. + /// Whether the logger should log in the stream. + private bool TryGetLogStream(int byteCount, out Stream stream, out int availableByteCount) + { + if (this.memoryMappedFile == null) + { + stream = null; + availableByteCount = 0; + return false; + } + + try + { + var cachedViewStream = this.GetStream(); + + long beginPosition, endPosition; + do + { + beginPosition = this.logFilePosition; + endPosition = beginPosition + byteCount; + if (endPosition >= this.logFileSize) + { + endPosition %= this.logFileSize; + } + } + while (beginPosition != Interlocked.CompareExchange(ref this.logFilePosition, endPosition, beginPosition)); + availableByteCount = (int)(this.logFileSize - beginPosition); + cachedViewStream.Seek(beginPosition, SeekOrigin.Begin); + stream = cachedViewStream; + return true; + } + catch (Exception) + { + stream = null; + availableByteCount = 0; + return false; + } + } + + + /// + /// Get a MemoryMappedViewStream for the MemoryMappedFile object for the current thread. + /// If no MemoryMappedFile is created yet, return null. + /// + /// A MemoryMappedViewStream for the MemoryMappedFile object. + /// Thrown when access to the memory-mapped file is unauthorized. + /// Thrown in a race condition when the memory-mapped file is closed after null check. + private MemoryMappedViewStream GetStream() + { + if (this.memoryMappedFile == null) + { + return null; + } + + var cachedViewStream = this.viewStream.Value; + + // Each thread has its own MemoryMappedViewStream created from the only one MemoryMappedFile. + // Once worker thread updates the MemoryMappedFile, all the cached ViewStream objects become + // obsolete. + // Each thread creates a new MemoryMappedViewStream the next time it tries to retrieve it. + // Whether the MemoryMappedViewStream is obsolete is determined by comparing the current + // MemoryMappedFile object with the MemoryMappedFile object cached at the creation time of the + // MemoryMappedViewStream. + if (cachedViewStream == null || this.memoryMappedFileCache.Value != this.memoryMappedFile) + { + // Race condition: The code might reach here right after the worker thread sets memoryMappedFile + // to null in CloseLogFile(). + // In this case, let the NullReferenceException be caught and fail silently. + // By design, all events captured will be dropped during a configuration file refresh if + // the file changed, regardless whether the file is deleted or updated. + cachedViewStream = this.memoryMappedFile.CreateViewStream(); + this.viewStream.Value = cachedViewStream; + this.memoryMappedFileCache.Value = this.memoryMappedFile; + } + + return cachedViewStream; + } + private void Dispose(bool disposing) { if (!this.disposedValue) From 9777def2db7968616f4fa222852a816288beeef7 Mon Sep 17 00:00:00 2001 From: xiang17 Date: Tue, 11 May 2021 19:05:04 -0700 Subject: [PATCH 06/12] Fix trivial compile errors --- .../Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs index 4d1d6feda4..33290c5769 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs @@ -34,8 +34,9 @@ internal class MemoryMappedFileHandler : IDisposable private int logFileSize; // Log file size in bytes private long logFilePosition; // The logger will write into the byte at this position - public string LogDirectory { get => logDirectory; set => logDirectory = value; } - public int LogFileSize { get => logFileSize; private set => logFileSize = value; } + public string LogDirectory { get => this.logDirectory; set => this.logDirectory = value; } + + public int LogFileSize { get => this.logFileSize; private set => this.logFileSize = value; } /// /// Create a log file. If the file already exists, it will be overwritten. @@ -205,7 +206,6 @@ private bool TryGetLogStream(int byteCount, out Stream stream, out int available } } - /// /// Get a MemoryMappedViewStream for the MemoryMappedFile object for the current thread. /// If no MemoryMappedFile is created yet, return null. From e073b49c2dd230e9fd084476ad91c25e12ea3195 Mon Sep 17 00:00:00 2001 From: xiang17 Date: Tue, 11 May 2021 19:10:16 -0700 Subject: [PATCH 07/12] Fix trivial compile errors --- .../Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs index 33290c5769..627d868d97 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs @@ -83,7 +83,7 @@ public void CreateLogFile(string logDirectory, int fileSize) // .NET Core simply doesn't support this parameter. null, #endif - HandleInheritability.None, + HandleInheritability.None, false); this.logDirectory = logDirectory; this.logFileSize = fileSize; From 1639929e8d2e86c24236c62d5c5fd6f909357453 Mon Sep 17 00:00:00 2001 From: xiang17 Date: Tue, 11 May 2021 19:21:35 -0700 Subject: [PATCH 08/12] Update EventSource method implementation --- .../Extensibility/Implementation/Tracing/CoreEventSource.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/CoreEventSource.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/CoreEventSource.cs index 6b940c2e82..f1dba6fb20 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/CoreEventSource.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/CoreEventSource.cs @@ -659,7 +659,7 @@ public void IngestionResponseTimeEventCounter(float responseDurationInMs) [Event(72, Keywords = Keywords.UserActionable, Message = "Failed to create file for self diagnostics at {0}. Error message: {1}.", Level = EventLevel.Error)] public void SelfDiagnosticsFileCreateException(string logDirectory, string ex) { - if (this.IsEnabled(EventLevel.Warning, (EventKeywords)(-1))) + if (this.IsEnabled(EventLevel.Error, (EventKeywords)(-1))) { this.WriteEvent(72, logDirectory, ex); } From edad67035de058c812fc96101104f20f093ab785 Mon Sep 17 00:00:00 2001 From: xiang17 Date: Tue, 11 May 2021 19:42:39 -0700 Subject: [PATCH 09/12] Update EventSource method implementation --- .../Extensibility/Implementation/Tracing/CoreEventSource.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/CoreEventSource.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/CoreEventSource.cs index f1dba6fb20..2c0a41fcca 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/CoreEventSource.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/CoreEventSource.cs @@ -659,10 +659,7 @@ public void IngestionResponseTimeEventCounter(float responseDurationInMs) [Event(72, Keywords = Keywords.UserActionable, Message = "Failed to create file for self diagnostics at {0}. Error message: {1}.", Level = EventLevel.Error)] public void SelfDiagnosticsFileCreateException(string logDirectory, string ex) { - if (this.IsEnabled(EventLevel.Error, (EventKeywords)(-1))) - { - this.WriteEvent(72, logDirectory, ex); - } + this.WriteEvent(72, logDirectory, ex); } [NonEvent] From acca0b46c8fe968deb274348f34b66f0d6491438 Mon Sep 17 00:00:00 2001 From: xiang17 Date: Tue, 11 May 2021 19:52:21 -0700 Subject: [PATCH 10/12] - --- .../Extensibility/Implementation/Tracing/CoreEventSource.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/CoreEventSource.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/CoreEventSource.cs index 2c0a41fcca..6ab6eb5976 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/CoreEventSource.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/CoreEventSource.cs @@ -657,10 +657,7 @@ public void IngestionResponseTimeEventCounter(float responseDurationInMs) public void TransmissionStatusEventError(string error, string appDomainName = "Incorrect") => this.WriteEvent(71, error, this.nameProvider.Name); [Event(72, Keywords = Keywords.UserActionable, Message = "Failed to create file for self diagnostics at {0}. Error message: {1}.", Level = EventLevel.Error)] - public void SelfDiagnosticsFileCreateException(string logDirectory, string ex) - { - this.WriteEvent(72, logDirectory, ex); - } + public void SelfDiagnosticsFileCreateException(string logDirectory, string ex) => this.WriteEvent(72, logDirectory ?? string.Empty, ex ?? string.Empty); [NonEvent] public void TransmissionStatusEventFailed(Exception ex) From 7855113792263aa370ec365af12f71fe0fe9fd78 Mon Sep 17 00:00:00 2001 From: xiang17 Date: Tue, 11 May 2021 20:40:40 -0700 Subject: [PATCH 11/12] Special requirement for last argument in method definition and last parameter for WriteEvent call --- .../Extensibility/Implementation/Tracing/CoreEventSource.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/CoreEventSource.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/CoreEventSource.cs index 6ab6eb5976..6c71a54476 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/CoreEventSource.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/CoreEventSource.cs @@ -657,7 +657,7 @@ public void IngestionResponseTimeEventCounter(float responseDurationInMs) public void TransmissionStatusEventError(string error, string appDomainName = "Incorrect") => this.WriteEvent(71, error, this.nameProvider.Name); [Event(72, Keywords = Keywords.UserActionable, Message = "Failed to create file for self diagnostics at {0}. Error message: {1}.", Level = EventLevel.Error)] - public void SelfDiagnosticsFileCreateException(string logDirectory, string ex) => this.WriteEvent(72, logDirectory ?? string.Empty, ex ?? string.Empty); + public void SelfDiagnosticsFileCreateException(string logDirectory, string exception, string appDomainName = "Incorrect") => this.WriteEvent(72, logDirectory, exception, this.nameProvider.Name); [NonEvent] public void TransmissionStatusEventFailed(Exception ex) From 649d62c4c6b8b5be277c9000b08c9a999ebe4c06 Mon Sep 17 00:00:00 2001 From: xiang17 Date: Thu, 13 May 2021 12:17:42 -0700 Subject: [PATCH 12/12] update typo in comment --- .../Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs index 627d868d97..1adee6fd3d 100644 --- a/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs +++ b/BASE/src/Microsoft.ApplicationInsights/Extensibility/Implementation/Tracing/SelfDiagnostics/MemoryMappedFileHandler.cs @@ -17,8 +17,8 @@ internal class MemoryMappedFileHandler : IDisposable public static readonly byte[] MessageOnNewFile = Encoding.UTF8.GetBytes("Successfully opened file.\n"); /// - /// t_memoryMappedFileCache is a handle kept in thread-local storage as a cache to indicate whether the cached - /// t_viewStream is created from the current m_memoryMappedFile. + /// memoryMappedFileCache is a handle kept in thread-local storage as a cache to indicate whether the cached + /// viewStream is created from the current m_memoryMappedFile. /// private readonly ThreadLocal memoryMappedFileCache = new ThreadLocal(true); private readonly ThreadLocal viewStream = new ThreadLocal(true);