ETW を使用して例外をログに記録する標準的な方法はありますか?
私が見た限り、これを行う唯一の方法は、例外タイプの厳密に型指定されたパラメーターがないため、メッセージとおそらく内部例外メッセージをログに記録することです。
ETW を使用して例外をログに記録する標準的な方法はありますか?
私が見た限り、これを行う唯一の方法は、例外タイプの厳密に型指定されたパラメーターがないため、メッセージとおそらく内部例外メッセージをログに記録することです。
すべての 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) { }
}
}
ETW は .NET 固有のものではないため、.net 例外をログに記録するための厳密に型指定された .NET 固有の API はありません。代わりに、厳密に型指定された独自の API を作成します。これが、セマンティック ロギングとセマンティック ロギング アプリケーション ブロックの背後にある考え方です。