Dot-Net

使用 ETW 記錄異常的最佳方法是什麼?

  • October 8, 2014

是否有使用 ETW 記錄異常的標準方法?

據我所知,唯一的方法是記錄消息和可能的內部異常消息,因為異常類型沒有強類型參數。

使用額外的事件並在 catch 塊中觸發此事件並將異常消息作為參數傳遞給事件

[Event(1, Message = "Application Falure: {0}", Level = EventLevel.Error, Keywords = Keywords.Diagnostic)]
public void Failure(string message) 
{ 
   if (this.IsEnabled())
   {
       this.WriteEvent(1, message); 
   }
}

使用級別和關鍵字來控制是否要一直記錄它。

所有 CLR 異常(第一次機會,以及可能最終導致您的應用程序崩潰的異常)都由 CLR 執行時提供程序在啟用時記錄到 ETW。

這是一個帶有呼叫堆棧的完全“結構化”事件(如果你想要的話)。實際上,您可以使用 TraceEvent NuGet 包(Install-Package Microsoft.Diagnostics.Tracing.TraceEvent)編寫監控應用程序

我正在粘貼我經常使用的監控程式碼。將它放在控制台應用程序中,呼叫 Run 方法,並從任何程序中拋出一些託管異常,它將列印資訊及其呼叫堆棧。

注意:您需要引用的 NuGet 包,然後引用其程序集,然後此程式碼將編譯。

class TraceLogMonitor
{
   static TextWriter Out = AllSamples.Out;

   public static void Run()
   {
       var monitoringTimeSec = 10;
      TraceEventSession session = null;

       Console.CancelKeyPress += (object sender, ConsoleCancelEventArgs cancelArgs) =>
       {
           if (session != null)
               session.Dispose();
           cancelArgs.Cancel = true;
       };

       var exceptionGeneationTask = Task.Factory.StartNew(delegate
       {
           Thread.Sleep(3000);
           ThrowException();
       });

       Timer timer = null;

       using (session = new TraceEventSession("TraceLogSession"))
       {
           Out.WriteLine("Enabling Image load, Process and Thread events.  These are needed to look up native method names.");
           session.EnableKernelProvider(

               KernelTraceEventParser.Keywords.ImageLoad |
               KernelTraceEventParser.Keywords.Process,  
               KernelTraceEventParser.Keywords.None
               );

           Out.WriteLine("Enabling CLR Exception and Load events (and stack for those events)");

           session.EnableProvider(
               ClrTraceEventParser.ProviderGuid,
               TraceEventLevel.Informational,
               (ulong)(ClrTraceEventParser.Keywords.Jit |              
               ClrTraceEventParser.Keywords.JittedMethodILToNativeMap |
               ClrTraceEventParser.Keywords.Loader |                   
               ClrTraceEventParser.Keywords.Exception |               
               ClrTraceEventParser.Keywords.Stack));                   

           Out.WriteLine("Enabling CLR Events to 'catch up' on JIT compiled code in running processes.");
           session.EnableProvider(ClrRundownTraceEventParser.ProviderGuid, TraceEventLevel.Informational,
               (ulong)(ClrTraceEventParser.Keywords.Jit |          
               ClrTraceEventParser.Keywords.JittedMethodILToNativeMap | 
               ClrTraceEventParser.Keywords.Loader |               
               ClrTraceEventParser.Keywords.StartEnumeration));    

           TextWriter SymbolLookupMessages = new StringWriter();

           var symbolPath = new SymbolPath(SymbolPath.SymbolPathFromEnvironment).Add(SymbolPath.MicrosoftSymbolServerPath);
           SymbolReader symbolReader = new SymbolReader(SymbolLookupMessages, symbolPath.ToString());

           Out.WriteLine("Open a real time TraceLog session (which understands how to decode stacks).");
           using (TraceLogEventSource traceLogSource = TraceLog.CreateFromTraceEventSession(session)) 
           {
               Action<TraceEvent> PrintEvent = ((TraceEvent data) => Print(data, symbolReader));

               traceLogSource.Clr.ExceptionStart += PrintEvent;
               traceLogSource.Clr.LoaderModuleLoad += PrintEvent;

               traceLogSource.Kernel.PerfInfoSample += ((SampledProfileTraceData data) => Print(data, symbolReader));

               Out.WriteLine("Waiting {0} sec for Events.  Run managed code to see data. ", monitoringTimeSec);
               Out.WriteLine("Keep in mind there is a several second buffering delay");

               timer = new Timer(delegate(object state)
               {
                   Out.WriteLine("Stopped Monitoring after {0} sec", monitoringTimeSec);
                   if (session != null)
                       session.Dispose();
                   session = null;
               }, null, monitoringTimeSec * 1000, Timeout.Infinite);

               traceLogSource.Process();
           }
       }
       Out.WriteLine("Finished");
       if (timer != null)
           timer.Dispose();
   }

   static void Print(TraceEvent data, SymbolReader symbolReader)
   {
       if (data.Opcode == TraceEventOpcode.DataCollectionStart)
           return;

       if (data is ExceptionTraceData && ((ExceptionTraceData) data).ExceptionType.Length == 0)
           return;

       Out.WriteLine("EVENT: {0}", data.ToString());
       var callStack = data.CallStack();
       if (callStack != null)
       {
           ResolveNativeCode(callStack, symbolReader);
           Out.WriteLine("CALLSTACK: {0}", callStack.ToString());
       }
   }

   static private void ResolveNativeCode(TraceCallStack callStack, SymbolReader symbolReader)
   {
       while (callStack != null)
       {
           var codeAddress = callStack.CodeAddress;
           if (codeAddress.Method == null)
           {
               var moduleFile = codeAddress.ModuleFile;
               if (moduleFile == null)
                   Trace.WriteLine(string.Format("Could not find module for Address 0x{0:x}", codeAddress.Address));
               else
                   codeAddress.CodeAddresses.LookupSymbolsForModule(symbolReader, moduleFile);
           }
           callStack = callStack.Caller;
       }
   }

   [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
   private static void ThrowException()
   {
       ThrowException1();
   }

   [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
   private static void ThrowException1()
   {
       Out.WriteLine("Causing an exception to happen so a CLR Exception Start event will be generated.");
       try
       {
           throw new Exception("This is a test exception thrown to generate a CLR event");
       }
       catch (Exception) { }
   }
}

引用自:https://stackoverflow.com/questions/19053307