C# - ETW EventListener를 상속받았을 때 초기화 순서
지난 글에서 실습한 CLR ETW 예제를,
C# - (.NET Core 2.2부터 가능한) 프로세스 내부에서 CLR ETW 이벤트 수신
; https://www.sysnet.pe.kr/2/0/12474
약간 변경해 볼까요? ^^
예를 들어, "Microsoft-Windows-DotNETRuntime"의 특정 Keyword 영역에 해당하는,
CLR ETW Keywords and Levels
; https://learn.microsoft.com/en-us/dotnet/framework/performance/clr-etw-keywords-and-levels#clr-etw-runtime-keywords
이벤트만 처리하도록 생성자 측에 관심 있는 Keyword 영역을 지정하는 식으로 코딩을 해보겠습니다.
using System;
using System.Diagnostics.Tracing;
class Program
{
static MyEventListener _listener;
static Program()
{
_listener = new MyEventListener(DotnetRuntimeKeyword.EXCEPTIONKEYWORD);
}
// ...[생략]...
}
internal class MyEventListener : EventListener
{
int _filterKeyword;
public MyEventListener(DotnetRuntimeKeyword filterKeyword)
{
this._filterKeyword = (int)filterKeyword;
}
protected override void OnEventSourceCreated(EventSource eventSource)
{
if (eventSource.Name == "Microsoft-Windows-DotNETRuntime")
{
EnableEvents(eventSource, EventLevel.Informational, (EventKeywords)this._filterKeyword);
return;
}
}
protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
Console.WriteLine($"{DateTime.Now}: {eventData.EventName}");
}
}
// ...[생략]...
위의 코드를 직접 실행해 보면,
OnEventWritten 콜백이 단 한 번도 발생하지 않게 됩니다. 왜냐하면 OnEventSourceCreated 단계에서의 EnableEvent에 전달한 this._filterKeyword의 값이 0이기 때문입니다. 아니, 분명히 생성자를 통해 filterKeyword 값을 전달했는데 왜 0이 나오는 걸까요?
그 이유는 MyEventListener 생성자 단계에서 부모 클래스(
EventListener)의 생성자를 호출하게 되고, OnEventSourceCreated 콜백은 바로 그 EventListener의 생성자 단계에서 발생하기 때문입니다. 이로 인해 결국 MyEventListener의 OnEventSourceCreated 콜백에서는 _filterKeyword 필드가 아직 초기화되지 않아 그와 같은 현상이 나오는 것입니다.
이 문제를 우회하려면 간단하게 static 필드를 사용할 수 있겠으나,
using System;
using System.Diagnostics.Tracing;
class Program
{
static MyEventListener _listener;
static Program()
{
MyEventListener._filterKeyword = (int)DotnetRuntimeKeyword.EXCEPTIONKEYWORD;
_listener = new MyEventListener();
}
// ...[생략]...
}
internal class MyEventListener : EventListener
{
public static int _filterKeyword;
protected override void OnEventSourceCreated(EventSource eventSource)
{
if (eventSource.Name == "Microsoft-Windows-DotNETRuntime")
{
EnableEvents(eventSource, EventLevel.Informational, (EventKeywords)MyEventListener._filterKeyword);
return;
}
}
}
보다시피 별로 좋은 해결책은 아닙니다.
의도한 것인지는 알 수 없으나, EventListener는 이에 대해 별도의 이벤트를 제공하는 것으로 해결할 수 있습니다. 즉, override로 제공하는 OnEventSourceCreated를 사용하는 대신 다음과 같이 이벤트를 구독하는 방식으로 구현하면,
// ...[생략]...
internal class MyEventListener : EventListener
{
public int _filterKeyword;
public MyEventListener(DotnetRuntimeKeyword filterKeyword)
{
this._filterKeyword = (int)filterKeyword;
this.EventSourceCreated += MyEventListener_EventSourceCreated;
}
private void MyEventListener_EventSourceCreated(object sender, EventSourceCreatedEventArgs e)
{
if (e.EventSource.Name == "Microsoft-Windows-DotNETRuntime")
{
EnableEvents(e.EventSource, EventLevel.Informational, (EventKeywords)this._filterKeyword);
}
}
}
MyEventListener_EventSourceCreated 이벤트 처리기는 MyEventListener의 초기화가 완료된 이후에 호출되는 식으로 처리가 됩니다. 그래서 생성자에 전달된 값을 기반으로 한 이후의 대응이 가능한데... 어쩐지 이 대목에서 "
THE WORST API EVER MADE" 글이 다시 한번 생각나는 순간입니다. ^^;
(
첨부 파일은 이 글의 예제 코드를 포함합니다.)
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]