수정 이력(6건, 최종 수정 2026년 09월 03일)
이 글에 적용한 변경 사항의 기록입니다. 보관해 둔 수정 전 버전은 DOI가 부여된 고정 URL에서 읽을 수 있습니다.
- Codex 리뷰에 따라 상담·문의 링크에 /ko/ 로케일 접두를 붙였습니다. 본문의 기술적인 주장은 바꾸지 않았습니다.
- permalink·저자 표기·지식 맵 래퍼·깨진 내부 링크 등 CI가 지적한 표시용 수정을 반영했습니다. 본문의 기술적인 주장은 바꾸지 않았습니다.
- 기사 서두에 「이 기사의 지식 맵」 절을 추가했습니다. 본문에서 다루는 개념과 그 관계를 요약·그림·상세 페이지 링크로 정리한 것입니다. 본문의 주장은 바꾸지 않았습니다.
- 외부 리뷰(1283건)에 대응하여 본문을 갱신했습니다. 개별 변경 내용은 아래 이력을 참조하세요.
- 기록되었음을 확인하는 절과 정리를 새로 만들고, 이벤트 뷰어에서의 확인 절차와 코드 지정 간의 대응표를 추가했습니다. 아울러 이벤트 로그와 ETW의 차이를 그림으로 나타내고, 대상 버전과 EventPipe를 경유한다는 점의 설명을 더했습니다.
- 출처가 본문 어디에서도 참조되지 않아 「참고 자료」에 표시되지 않던 문제를 수정했습니다. 본문 내용은 바꾸지 않았습니다.
- 최초 공개
이 글을 인용하기(DOI(등록된 아카이브): 10.5281/zenodo.21635392)
아래 DOI는 이전에 등록된 아카이브를 가리키며 현재 본문과 다를 수 있습니다. 현재 본문을 참조할 때는 이 페이지의 URL을 사용하세요.
Go Komura (2026). 「Windows 이벤트 로그·ETW 입문 ── 업무 앱 로그를 OS 표준 메커니즘에 올리기」. 합동회사 코무라소프트. https://comcomponent.com/ko/blog/windows-eventlog-etw-structured-logging/
- DOI(등록된 아카이브)
- 10.5281/zenodo.21635392
- DOI(마지막 등록 버전)
- 10.5281/zenodo.21635393
C#으로 업무 앱이나 Windows 서비스를 개발하다 보면, 대개 자체 파일 로거나 Serilog·NLog 같은 라이브러리로 일상 로그를 남깁니다. 그렇다면 Windows 이벤트 로그나 ETW(Event Tracing for Windows)는 이제 필요 없느냐 하면, 그렇지 않습니다. 이 둘은 「파일 로그의 대체」가 아니라, 운영 담당자와 OS 표준 도구에서 보이는 별도 계층의 기록입니다.
이 기사에서는 파일 로그·이벤트 로그·ETW의 역할 분담부터 .NET에서의 구현 방법, ETW의 최소한의 핵심, 수집·조사의 실무, 그리고 현장에서 빠지기 쉬운 함정까지 정리합니다.
대상 버전: 이 기사의 샘플 코드는 .NET 8(Windows)을 전제로 작성했습니다. .NET Framework 4.8을 쓰는 경우와의 주요 차이는 다음 두 가지입니다. 첫째, System.Diagnostics.EventLog가 .NET Framework에서는 BCL에 포함되지만, .NET(Core 계열)에서는 같은 이름의 NuGet 패키지를 명시적으로 참조해야 한다는 점입니다. 둘째는 EventSource의 전달처입니다. .NET Framework에서는 목적지가 ETW뿐이지만, .NET(Core 계열)에서는 ETW에 더해 EventPipe(런타임 내장 트레이스 메커니즘)로도 흐르므로, dotnet-trace 같은 도구로 관리자 권한 없이 수집할 수 있습니다. 12
1. 먼저 결론
- 세 수단은 대체 관계가 아니라 역할 분담입니다. 파일 로그는 개발자가 나중에 상세를 추적하기 위한 기록, 이벤트 로그는 운영 담당자나 OS 표준 이벤트 뷰어·모니터링 도구가 이상을 알아차리기 위한 기록, ETW는 성능 조사나 재현이 어려운 장애 분석을 위해 온디맨드로 켜는 고빈도 트레이스입니다. 기본은 병용하고, 각각에 쓰는 정보량을 바꿉니다.
- 이벤트 로그에는 「기동·정지·치명적 오류」만 기록합니다. 모든 로그를 이벤트 로그에도 흘리면, 운영 담당자가 정말로 봐야 할 이상이 묻힙니다. 파일 로그는 지금까지처럼 상세하게 남기되, 이벤트 로그는 좁힙니다.
- 이벤트 소스 등록에는 관리자 권한이 필요합니다.
EventLog.CreateEventSource는 Windows Vista 이후, 관리자 권한이 없으면 호출할 수 없습니다. 실행 시 최초 접근에서 등록하려는 설계는 피하고, 설치 프로그램에서 미리 등록해 둡니다. 3 - ETW는 「상시 수집」이 아닙니다.
EventSource로 이벤트를 정의해 두어도, 구독자(dotnet-trace, PerfView, ETW 기반 도구)가 없으면 아무것도 기록되지 않습니다. 성능 분석이나 특정 장애 조사 때만 대상 프로바이더를 지정해 수집하는 것이 기본 사용법입니다. 4 - 메시지는 문자열 연결이나 문자열 보간이 아니라 템플릿 형식으로 씁니다.
logger.LogInformation("Order {OrderId} failed", orderId)처럼 쓰면, 구조화 로그로서 OrderId를 플레이스홀더 이름과 함께 검색할 수 있습니다. 문자열 연결·보간으로 조립하면 이 구조 정보가 사라집니다. 5 - 로그 비대화는 기본값의 작음이 원인인 경우가 많습니다. 이벤트 로그의 기본 최대 크기는 512KB에 불과합니다. 업무 앱이 이벤트 로그를 일상적으로 쓴다면 명시적으로 확장하고, 한도에 도달했을 때의 동작(덮어쓸지, 버릴지)도 설계 시점에 정합니다. 6
아래는 판단표입니다.
| 관점 | 파일 로그 | 이벤트 로그 | ETW |
|---|---|---|---|
| 주요 독자 | 개발자·유지보수 담당 | 운영 담당자, 모니터링 도구, OS 표준 이벤트 뷰어 | 성능 조사 담당, 지원 엔지니어 |
| 출력량 기준 | 상세(Trace/Debug 포함해도 됨) | 적음(기동·정지·치명적 오류만) | 활성화 시에만 대량. 기본에서는 아무것도 기록되지 않음 |
| 수명·보관 | 로테이션 설정에 따라 장기 보관이 쉬움 | 로그 크기 한도에 도달하면 오래된 것부터 덮어쓰기·폐기됨 | 세션 단위. 수집이 끝나면 .etl/.nettrace 파일로 저장 |
| OS 표준 도구와의 통합 | 없음(자체 뷰어가 필요) | 이벤트 뷰어, wevtutil, Get-WinEvent로 표준적으로 볼 수 있음 |
dotnet-trace, PerfView, WPR/WPA로 봄 |
| 권한 | 앱 실행 사용자의 쓰기 권한만 | 소스 등록에 관리자 권한 필요(쓰기 자체는 등록된 소스라면 표준 사용자도 가능) | 수집 시작에 관리자 권한이 필요한 경우가 많음 |
그림의 실선은 항상 성립하는 관계, 점선은 조건이 붙는 관계입니다(성립 조건은 상세 페이지의 관계별 설명에 적혀 있습니다). 관계 전체 목록(총 18건, 근거와 확신도 포함)과 주요 개념의 정의는 지식 맵 상세 페이지에 정리되어 있습니다(일본어). 데이터: JSON-LD / Turtle
2. Windows 이벤트 로그의 기초
Windows에는 기본으로 Application·System·Security 세 가지 로그가 있으며, Security는 읽기 전용입니다. 업무 앱이나 서비스는 Application 로그, 또는 앱 고유의 커스텀 로그(채널)에 기록합니다. 디바이스 드라이버는 System 로그에 쓰는 것이 관례입니다. 7
쓰기의 단위는 「이벤트 소스」입니다. 소스는 애플리케이션을 식별하는 이름이며, EventLog.CreateEventSource로 미리 등록합니다. Windows Vista 이후, 소스 등록에는 관리자 권한이 필요합니다. 소스 이름의 유일성을 확인하려면 Security 로그를 포함한 모든 이벤트 로그를 검색해야 하고, 표준 사용자는 Security 로그 접근 권한이 없기 때문입니다. 3
using System.Diagnostics;
// 설치 프로그램이나 셋업 처리 안에서, 관리자 권한이 있는 동안 실행한다
const string SourceName = "KomuraSoft.OrderService";
const string LogName = "Application";
if (!EventLog.SourceExists(SourceName))
{
EventLog.CreateEventSource(SourceName, LogName);
}
소스를 실행 시 최초 접근에서 만들려고 하면, 앱이 표준 사용자로 동작하는 운영에서는 이 호출이 실패합니다. 관리자 권한이 언제 필요해지는지에 대한 일반론은 「Windows에서 관리자 권한이 필요한 때는 언제인가」에 정리되어 있습니다. 이벤트 소스 등록도 바로 이 패턴이며, 설치 프로그램에서 등록하고, 앱 본체는 등록된 소스에 쓰기만 하는 편이 안전한 설계입니다.
이벤트에는 「레벨」과 「이벤트 ID」 두 가지 식별 정보가 붙습니다. 레벨은 EventLogEntryType(Information, Warning, Error, SuccessAudit, FailureAudit)이며, Event Viewer의 아이콘이나 기본 필터에 쓰입니다. 이벤트 ID는 애플리케이션이 정의하는 정수로, 같은 종류의 현상을 나중에 검색·집계하기 위한 키가 됩니다.
3. .NET에서 이벤트 로그에 기록하기
.NET에서 이벤트 로그에 쓰는 경로는 크게 두 가지입니다. 먼저 전제 조건의 차이를 나열합니다.
| 관점 | 3.1 System.Diagnostics.EventLog를 직접 사용 |
3.2 ILogger + AddEventLog |
|---|---|---|
| 이벤트 소스 등록 필요 여부 | 필요. WriteEntry에 넘기는 소스 이름을 설치 프로그램 쪽에서 CreateEventSource해 둔다 3 |
필요. SourceName에 지정하는 이름을 마찬가지로 설치 프로그램에서 등록해 둔다. 생략하면 .NET Runtime이라는 범용 이름이 된다 8 |
| 등록에 필요한 권한 | 관리자 권한(등록 시에만. 등록된 소스로의 쓰기는 표준 사용자로 가능) | 같음 |
| 추가로 필요한 참조 | .NET(Core 계열)에서는 System.Diagnostics.EventLog 패키지 |
Microsoft.Extensions.Logging.EventLog 패키지(Generic Host에서는 Windows에서 기본 사용) 9 |
| 지정할 수 있는 항목 | 레벨·이벤트 ID에 더해, 카테고리나 바이너리 데이터 첨부까지 세밀하게 지정할 수 있음 | 레벨·이벤트 ID·메시지 템플릿. 카테고리나 바이너리 데이터 첨부는 다루지 않음 |
| 출력량 좁히기 | 호출 지점마다 스스로 판단 | settings.Filter로 이벤트 로그용만 레벨을 올릴 수 있음 |
| 적합한 장면 | 로그 기반이 없는 작은 서비스, 기존 Win32적인 구조에서의 이식 | 이미 ILogger로 구조화 로그를 구성한 앱 |
어느 경로든 지정한 소스 이름이 미리 등록되어 있는 것이 전제입니다. 미등록 소스 이름으로 쓰면 이벤트 자체는 Application 로그에 들어가지만, 대응하는 메시지 리소스가 없어 이벤트 뷰어가 설명문을 표시하지 못합니다. 10
3.1 System.Diagnostics.EventLog를 직접 사용하기
저수준 API로, 세밀한 제어(카테고리, 바이너리 데이터 첨부 등)가 필요할 때 적합합니다. .NET(.NET Framework 이외)에서는 System.Diagnostics.EventLog NuGet 패키지 참조가 필요합니다.
using System.Diagnostics;
public sealed class OrderServiceEventLog
{
private const string SourceName = "KomuraSoft.OrderService";
public void WriteServiceStarted()
{
EventLog.WriteEntry(SourceName, "OrderService를 기동했습니다.",
EventLogEntryType.Information, eventID: 1000);
}
public void WriteFatalError(Exception ex)
{
EventLog.WriteEntry(SourceName,
$"치명적 오류가 발생하여 처리를 계속할 수 없습니다. 상세는 파일 로그를 확인하세요. {ex.GetType().Name}: {ex.Message}",
EventLogEntryType.Error, eventID: 1999);
}
}
3.2 ILogger + AddEventLog 사용하기
이미 ILogger로 구조화 로그를 구성했다면, Microsoft.Extensions.Logging.EventLog 패키지의 AddEventLog를 쓰는 편이 기존 로깅 파이프라인에 자연스럽게 올라갑니다. ASP.NET Core나 Generic Host 기반 Worker Service에서는 Windows에서 동작할 때 기본적으로 EventLog 프로바이더가 켜져 있습니다. 9
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.EventLog;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddEventLog(settings =>
{
settings.SourceName = "KomuraSoft.OrderService";
settings.LogName = "Application";
// 기본 카테고리 필터에 더해, 이벤트 로그에는
// Warning 이상만 흘린다(상세는 파일 로그 쪽에 맡긴다)
settings.Filter = (_, level) => level >= LogLevel.Warning;
});
using IHost host = builder.Build();
await host.RunAsync();
EventLogSettings를 생략하면 LogName은 기본값 "Application", SourceName은 기본값 ".NET Runtime"이 됩니다. 8 .NET Runtime이라는 범용 소스 이름 그대로면, 자기 앱 로그인지 다른 .NET 앱 로그인지 이벤트 뷰어에서 구별하기 어려워지므로, SourceName은 반드시 자사 앱용으로 명시합니다.
Windows 서비스나 작업 스케줄러에서 도는 배치 처리에서는 「기동」「정지」「치명적 오류」만 이벤트 로그에 쓰고, 그 밖의 상세는 파일 로그에 맡기는 것이 실무상의 균형입니다. 서비스로서의 구성·운영은 「Windows서비스 만드는 법과 운영」, 작업 스케줄러를 통한 실행 문제는 「작업 스케줄러 작업이 실행되지 않거나 0x1로 끝나는 경우」에서 다룹니다. 어느 쪽이든 이상 시 운영 담당자가 처음 보는 곳이 이벤트 로그인 경우가 많고, 여기에 요점이 남아 있는지가 초기 조사 속도를 바꿉니다.
3.3 기록되었음을 확인하기
코드를 작성했다면, 실제로 이벤트 뷰어에서 보이는 지점까지 한 번은 통과해 둡니다. 절차는 다음과 같습니다.
Win + R에서eventvwr.msc를 실행해 이벤트 뷰어를 엽니다.- 왼쪽 창에서 이벤트 뷰어(로컬) > Windows 로그 > Application을 엽니다(일본어 환경에서는 「アプリケーション」으로 표시되는 경우가 있습니다). 커스텀 로그를 만든 경우 애플리케이션 및 서비스 로그 아래에 자신의 로그 이름이 나열됩니다.
- 건수가 많아 묻히는 경우에는 오른쪽 창의 현재 로그 필터를 열고, 「이벤트 원본」에 자신의 소스 이름을, 「모든 이벤트 ID」에 확인할 ID를 넣어 좁힙니다.
코드에서 지정한 값이 이벤트 뷰어의 어느 칸에 나오는지는 다음 대응과 같습니다. 여기를 알아 두면, 「썼을 텐데 찾을 수 없다」일 때 어디를 보면 되는지 알 수 있습니다.
| 코드상의 지정 | 이벤트 뷰어에서의 표시 |
|---|---|
WriteEntry의 제1인수 / settings.SourceName |
「원본」 열 |
eventID 인수 / ILogger의 EventId |
「이벤트 ID」 열 |
EventLogEntryType(Information·Warning·Error) / LogLevel |
「수준」 열(정보·경고·오류) |
WriteEntry에 넘긴 문자열 / 템플릿 전개 후의 메시지 |
목록 하단, 「일반」 탭의 설명문 |
LogName(기본값은 Application) |
어느 로그 아래에 나오는가 |
명령줄에서 확인하고 싶을 때는 PowerShell의 Get-WinEvent가 다루기 쉽습니다. -FilterHashtable에 로그 이름과 프로바이더 이름(=소스 이름)을 넘기면, 그 소스가 쓴 이벤트만 가져올 수 있습니다. 11
# 자기 소스가 쓴 최근 10건을, 일시·ID·레벨·본문만으로 좁혀 표시한다
Get-WinEvent -FilterHashtable @{ LogName = 'Application'; ProviderName = 'KomuraSoft.OrderService' } -MaxEvents 10 |
Select-Object TimeCreated, Id, LevelDisplayName, Message
여기서 한 건도 반환되지 않으면, 원인은 대개 「소스 이름 철자 차이」「소스가 미등록」「애초에 쓰기에 실패하고 예외를 삼키고 있다」 중 하나입니다. 소스가 등록되어 있는지는 [System.Diagnostics.EventLog]::SourceExists('KomuraSoft.OrderService')로 확인할 수 있습니다.
4. ETW란 무엇인가 ── OS 전체를 관통하는 트레이스 기반
Event Tracing for Windows(ETW)는 커널부터 사용자 모드 애플리케이션까지 공통 기반으로 계측할 수 있는 트레이스 메커니즘입니다. 커널 이벤트(디스크 I/O, 프로세스 생성 등)와 애플리케이션의 고유 이벤트를 같은 타임라인 위에서 한꺼번에 기록·분석할 수 있는 점이 가장 큰 특징입니다. 12
.NET에서는 System.Diagnostics.Tracing.EventSource를 상속한 클래스를 만들어 고유 ETW 프로바이더를 정의할 수 있습니다. 1 ETW는 Publish-Subscribe 방식으로 동작하며, 구독자가 없으면 이벤트는 기록되지 않습니다. 즉 EventSource를 구현해 두기만 해서는 아무 일도 일어나지 않고, 수집 도구에서 명시적으로 활성화해야 비로소 이벤트가 기록됩니다. 4
1장의 판단표에서 「이벤트 로그=상시·운영용」「ETW=온디맨드·조사용」이라고 쓴 차이는, 이 구독 유무에서 옵니다. 그림으로 나타내면 다음과 같습니다.
flowchart LR
APP["업무 앱"]
APP -->|"기동·정지·치명적 오류만"| EL["이벤트 로그<br/>상시 기록되며, OS가 영속화한다"]
EL --> EV["이벤트 뷰어<br/>wevtutil / Get-WinEvent<br/>운영 담당자가 언제든 볼 수 있다"]
APP -->|"EventSource 로 계측"| SRC["프로바이더<br/>고빈도 이벤트를 정의"]
SRC -->|"구독자가 없을 때"| NONE["어디에도 기록되지 않는다<br/>비용도 거의 제로"]
SRC -->|"수집을 시작했을 때"| SES["세션<br/>조사 중에만 활성화한다"]
SES --> TOOL["컨슈머<br/>dotnet-trace / PerfView / WPA<br/>기간을 나눠 분석한다"]
그림 1: 이벤트 로그는 상시 기록·영속, ETW는 프로바이더 → 세션 → 컨슈머가 갖춰졌을 때만 기록된다
using System.Diagnostics.Tracing;
[EventSource(Name = "KomuraSoft-OrderService")]
internal sealed class OrderServiceEventSource : EventSource
{
public static readonly OrderServiceEventSource Log = new();
[Event(1, Level = EventLevel.Informational, Message = "주문 처리를 시작했습니다. OrderId={0}")]
public void OrderProcessingStart(string orderId)
{
if (IsEnabled())
{
WriteEvent(1, orderId);
}
}
[Event(2, Level = EventLevel.Informational, Message = "주문 처리가 완료되었습니다. OrderId={0}")]
public void OrderProcessingStop(string orderId)
{
if (IsEnabled())
{
WriteEvent(2, orderId);
}
}
}
호출 측은 다음과 같이 씁니다. 이벤트를 실제로 쓰기 전에 IsEnabled()로 활성화 여부를 확인해 두면, 수집하지 않을 때의 불필요한 비용을 피할 수 있습니다.
OrderServiceEventSource.Log.OrderProcessingStart(order.Id);
try
{
ProcessOrder(order);
}
finally
{
OrderServiceEventSource.Log.OrderProcessingStop(order.Id);
}
이 이벤트를 수집하려면 크로스 플랫폼인 dotnet-trace 도구를 쓰는 것이 가장 간편합니다. 13
dotnet-trace collect --providers KomuraSoft-OrderService -- OrderService.exe
여기서 알아 둘 점은, dotnet-trace가 쓰는 것은 ETW 자체가 아니라 EventPipe라는 다른 경로라는 것입니다. EventPipe는 .NET 런타임에 내장된 트레이스 메커니즘으로, ETW와 마찬가지로 EventSource 이벤트를 모을 수 있습니다. 차이는 세 가지입니다. EventPipe는 .NET이 지원하는 모든 플랫폼에서 같게 동작하는 반면 ETW는 Windows 전용입니다. ETW 수집 시작에는 관리자 권한이 필요한 반면, EventPipe는 대상 앱과 같은 사용자로 실행 중이면 관리자 권한이 필요 없습니다. 그리고 EventPipe의 범위는 매니지드 코드와 런타임에 한정되며, OS·커널 이벤트나 네이티브 콜 스택은 가져올 수 없습니다. 2 커널까지 포함해 추적하고 싶을 때 ETW(PerfView, WPR/WPA)가 필요해지는 것은 이 세 번째 제약 때문입니다.
수집한 .nettrace 파일은 Visual Studio나 PerfView로 열어 내용을 확인합니다. 더 본격적인 성능 조사, 예를 들어 커널 이벤트까지 포함한 분석을 하려면 Windows Assessment and Deployment Kit(ADK)에 포함된 Windows Performance Recorder(WPR)를 쓰고, Windows Performance Analyzer(WPA)로 보는 구성이 됩니다. 14 다만 이 기사의 범위는 「ETW로 무엇을 할 수 있고, 언제 쓰는가」까지이며, PerfView나 WPA에서의 상세 분석 절차에는 들어가지 않습니다. 업무 앱의 개발·유지보수라는 관점에서는, 먼저 자신의 프로바이더를 EventSource로 정의하고 dotnet-trace로 수집할 수 있는 정도까지 익히면 충분합니다.
5. 구조화 로그의 실무
ILogger의 메시지 템플릿은 {PlaceHolder}라는 이름 있는 플레이스홀더를 포함한 고정 문자열로 씁니다. 이는 단순한 겉모습의 관례가 아니라, 템플릿 자체는 바꾸지 않고 인수만 넘김으로써 로그 기반 쪽이 플레이스홀더 이름과 값의 대응 관계(구조화 데이터)를 유지할 수 있다는 의미입니다. 15
// 권장: 템플릿은 고정, 인수는 따로 넘긴다
logger.LogWarning("Order {OrderId} 의 재고 확보에 실패했습니다. 창고={WarehouseId}", orderId, warehouseId);
// 비권장: 문자열 연결·보간은 템플릿과 값의 대응 관계를 깨뜨린다
logger.LogWarning("Order " + orderId + " 의 재고 확보에 실패했습니다. 창고=" + warehouseId);
logger.LogWarning($"Order {orderId} 의 재고 확보에 실패했습니다. 창고={warehouseId}");
코드 분석기 규칙 CA2254는 이 비권장 패턴(호출마다 템플릿이 바뀌는 쓰기 방식)을 검출해 줍니다. 5 호출 빈도가 높은 핫 패스에서는 나아가 LoggerMessageAttribute에 의한 소스 생성을 쓰면 박싱이나 템플릿 분석 비용을 피할 수 있습니다. 16
using Microsoft.Extensions.Logging;
internal static partial class Log
{
[LoggerMessage(
EventId = 2001,
Level = LogLevel.Warning,
Message = "Order {OrderId} 의 재고 확보에 실패했습니다. 창고={WarehouseId}")]
public static partial void StockReservationFailed(
this ILogger logger, string orderId, string warehouseId);
}
// 호출 측
logger.StockReservationFailed(orderId, warehouseId);
이 구성이면 같은 로그 호출에서, 파일 로그(Serilog/NLog 등의 싱크)에는 상세를, 이벤트 로그에는 좁힌 내용을, ETW에는 EventSourceLoggerProvider를 통해 저비용 트레이스를 각각 나눌 수 있습니다. ILoggerProvider를 여러 개 등록하고, Filter로 프로바이더마다 출력 레벨을 바꾸는 것이 기본 형태입니다. 자체 로거의 최소 요건은 「자작 logger의 최소 요건과 결합 테스트 체크리스트」, 크래시 시 로그와 덤프를 어떻게 묶어 남길지는 「Windows 앱 크래시 시 로그와 덤프를 남기는 설계」를 참조합니다.
6. 수집과 조사 ── 쓴 로그를 읽는 쪽 이야기
이벤트 뷰어에서는 로그 목록의 「필터」 또는 「사용자 지정 보기」로 특정 소스·레벨·이벤트 ID만 추출할 수 있습니다. 자주 쓰는 조건은 사용자 지정 보기로 저장해 두면 다음 조사가 빨라집니다. 사용자 지정 보기는 XPath 쿼리로 정의되어 있으며, 예를 들어 특정 이벤트 이름만 추출하는 쿼리는 다음 형태입니다. 17
<QueryList>
<Query Id="0" Path="Application">
<Select Path="Application">
*[System[Provider[@Name='KomuraSoft.OrderService'] and (Level=2 or Level=3)]]
</Select>
</Query>
</QueryList>
이 XML을 붙이는 위치는 이벤트 뷰어 오른쪽 창 「사용자 지정 보기 만들기」 > 「XML」 탭 > 「쿼리를 수동으로 편집」에 체크입니다. 저장하면 왼쪽 창의 「사용자 지정 보기」 아래에 나열되고, 이후에는 클릭만으로 KomuraSoft.OrderService가 쓴 오류(Level=2)와 경고(Level=3)만의 목록이 열립니다. 이벤트 스키마의 Level은 이벤트 뷰어 「수준」 열과 같은 값이므로, 정보 레벨까지 포함하려면 Level=4를 조건에 더합니다. 필터 대화 상자에서 만든 좁히기도 같은 대화 상자의 「XML」 탭을 열면 XPath로 확인할 수 있으므로, 먼저 GUI로 좁힌 뒤 XML을 복사하는 진행이 지름길입니다.
명령줄에서는 wevtutil로 로그 내보내기와 설정 변경을 할 수 있습니다. 장애 조사의 일환으로, 발생 시점 전후의 이벤트 로그를 내보내 지원에 보내는 운영에도 쓸 수 있습니다. 18
:: Application 로그를 통째로 evtx 파일로 내보낸다
wevtutil epl Application C:\logs\application_20260707.evtx
:: 특정 소스의 이벤트만 표시한다(최근 10건)
wevtutil qe Application /q:"*[System[Provider[@Name='KomuraSoft.OrderService']]]" /c:10 /rd:true /f:text
/q:에 넘기는 것은 위의 사용자 지정 보기에서 쓴 것과 같은 XPath의 Select 요소 내용입니다. /f:는 출력 형식 지정으로, 사람이 눈으로 따라가려면 text, 스크립트에서 기계적으로 처리하려면 xml을 고릅니다. xml을 고르면 사용자 지정 보기의 XML과 같은 스키마가 그대로 나오므로, Provider나 EventID를 요소 이름으로 집을 수 있습니다. 내보낸 .evtx 파일은 이벤트 뷰어의 「저장된 로그 열기」에서 다른 PC에서도 그대로 열 수 있습니다.
크래시 조사에서는 이벤트 로그·파일 로그·크래시 덤프 세 가지를 시각으로 맞추는 것이 기본입니다. 이벤트 로그에 남은 「치명적 오류」의 타임스탬프를 기점으로, 같은 시각의 파일 로그 상세와 WER/ProcDump로 채취한 덤프를 대조합니다. 덤프 채취 방법은 「Windows 크래시 덤프 수집 입문 - WER/ProcDump/WinDbg」, 채취한 덤프의 실제 분석 절차는 「WinDbg + SOS로 크래시 덤프 읽기」에 정리되어 있습니다.
7. 함정
- 소스가 미등록인 채로 쓰려다 실패한다. 등록되지 않은 소스 이름으로
RegisterEventSource에 해당하는 호출을 하면, 이벤트 자체는 Application 로그에 쓰이지만 대응하는 메시지 리소스 DLL이 없어 이벤트 뷰어는 설명문을 표시하지 못하고 오류 표시가 됩니다. 10 게다가 실행 시CreateEventSource로 자동 등록하려는 설계에서는, 비관리자 사용자로 도는 프로세스의 최초 기동에서 예외로 죽는 사고도 잘 납니다. 소스 등록은 반드시 설치 프로그램 쪽에서 끝내 둡니다. - 「다른 소스 이름으로 쓰여 버린다」는 혼선. 이벤트 로그는 쓰기 권한만 있으면, 앱이 등록한 적 없는 다른 소스 이름으로도 쓸 수 있습니다. 소스 이름은 컴퓨터 상에서 유일해야 하지만, 이를 지키는 메커니즘은 OS 쪽에 없고 앱 쪽 규율에 맡겨져 있습니다. 7 너무 범용적인 소스 이름(회사명이나 제품명을 포함하지 않는 짧은 이름)을 고르면, 다른 벤더 앱과 충돌하거나 의도치 않게 다른 앱의 소스 이름을 그대로 쓰게 되는 원인이 됩니다.
- 로그 비대화와 보관 정책. 이벤트 로그의 기본 최대 크기는 겨우 512KB입니다. 6 업무 앱이 일상적으로 이벤트 로그를 쓴다면
wevtutil sl이나 설치 프로그램으로 크기를 명시적으로 확장하고, 한도에 도달했을 때 「오래된 것부터 덮어쓸지」「새것을 버릴지」를 의식해 설정합니다.OverwriteOlder는 사용이 권장되지 않으며, 지정해도 실질적으로 「덮어쓰지 않는」 동작이 될 수 있다는 점에도 주의가 필요합니다. 19 - 다국어 환경에서의 메시지 표시. 지역화된 메시지 리소스 파일(
MessageResourceFile)을 쓰는 구성이면, 그 리소스 DLL이 없거나 버전이 다른 환경에서 로그를 볼 때 「이벤트 ID ○○의 설명을 찾을 수 없습니다」라는 표시가 됩니다. 전달된 이벤트 로그를 다른 서버에서 보거나, 앱을 제거한 뒤 로그만 남아 있는 장면에서 일어나기 쉬운 문제입니다. 문자열을WriteEntry로 직접 쓰는(리소스 파일을 쓰지 않는) 구성이면, 이런 문제 자체를 들이지 않고 끝낼 수 있습니다. - 권한 부족으로 쓰지 못하는 경우. 오해하기 쉽지만, 소스 등록에는 관리자 권한이 필요한 반면, 등록된 소스로의 쓰기는 표준 사용자도 할 수 있습니다. 「관리자 권한이 없으면 동작하지 않는다」고 지레짐작해 앱 전체를 관리자 실행으로 만들기 전에, 실제로 어느 조작이 권한을 필요로 하는지 원인을 분리합니다.
8. 정리
이 기사의 골격은 세 가지로 모입니다.
- 세 수단은 역할 분담입니다. 파일 로그는 개발자가 상세를 추적하기 위해, 이벤트 로그는 운영 담당자와 OS 표준 도구가 이상을 알아차리기 위해, ETW/EventPipe는 성능 조사나 재현이 어려운 장애를 파기 위해 씁니다. 이벤트 로그에는 기동·정지·치명적 오류만 쓰고, 상세는 파일 로그에 맡기는 것이 기본 형태입니다(1장).
- 이벤트 소스 등록은 설치 프로그램의 일입니다. 등록에는 관리자 권한이 필요한 반면, 등록된 소스로의 쓰기는 표준 사용자도 할 수 있습니다. 실행 시
CreateEventSource로 자동 등록하려는 설계가 표준 사용자 환경에서의 최초 기동 실패를 만듭니다(2장·3장·7장). - ETW는 구독자가 있어야 비로소 기록됩니다.
EventSource를 쓰기만 해서는 아무 일도 일어나지 않습니다. 뒤집으면, 계측을 넣어 두는 비용은 거의 제로이고, 필요해졌을 때dotnet-trace로 집을 수 있는 상태를 만들 수 있다는 뜻입니다(4장).
다음에 손을 움직인다면 이 순서가 현실적입니다.
- 기존 앱의 이벤트 로그 출력을 점검하고, 이벤트 로그로 흘리는 내용이 「기동·정지·치명적 오류」로 좁혀져 있는지 확인한다. 상세 로그까지 흘리고 있다면
AddEventLog의Filter로 좁힌다(3장). - 이벤트 ID 채번 규칙(기능 영역마다의 대역, 한 번 부여한 ID는 의미를 바꾸지 않음)을 정해 문서에 남긴다(2장).
- 설치 프로그램에 이벤트 소스 등록 처리가 있는지, 로그 최대 크기를 기본 512KB에서 명시적으로 넓혔는지 확인한다(7장).
- 장애 조사에서 매번 같은 좁히기를 하고 있다면, 그 XPath를 사용자 지정 보기로 저장해 둔다(6장).
- 성능 면의 상담이 나올 법한 처리에는 먼저
EventSource로 시작·종료 이벤트만 넣어 둔다. 수집하지 않는 동안의 비용은 거의 제로입니다(4장).
관련 기사
- Windows에서 관리자 권한이 필요한 때는 언제인가 - UAC, 보호 영역, 설계로 구분하는 방법
- Windows서비스 만드는 법과 운영 ── 작업 스케줄러와의 구분부터 BackgroundService의 서비스화까지
- 작업 스케줄러 작업이 실행되지 않거나 0x1로 끝나는 경우 ── 원인 분리와 안전한 운영 설계
- 자작 logger의 최소 요건과 결합 테스트 체크리스트
- Windows 앱 크래시 시 로그와 덤프를 남기는 설계
- Windows 크래시 덤프 수집 입문 - WER/ProcDump/WinDbg
- WinDbg + SOS로 크래시 덤프 읽기 ── 수집 이후의 실무 분석 입문
관련 상담 영역
合同会社小村ソフト에서는 Windows 업무 앱의 로그·이벤트 로그·ETW 기반 설계, 장애 조사를 염두에 둔 관측점 설계의 기술 상담을 다룹니다.
참고 링크
-
Microsoft Learn, EventSource. EventSource가 ETW 및 EventPipe에 대응하는 .NET 내장 고속 구조화 로그 메커니즘이라는 점에 대해. ↩ ↩2
-
Microsoft Learn, EventPipe. EventPipe가 ETW나 perf_events와 비슷한 .NET 런타임 내장 트레이스 메커니즘이라는 점, EventSource 이벤트를 수집할 수 있다는 점, ETW는 Windows 전용이고 관리자 권한이 필요한 반면 EventPipe는 크로스 플랫폼이며 관리자 권한이 필요 없다는 점, EventPipe 범위가 매니지드 코드와 런타임에 한정되어 커널 이벤트나 네이티브 콜 스택을 가져올 수 없다는 점에 대해. ↩ ↩2
-
Microsoft Learn, EventLog.CreateEventSource Method. Windows Vista 이후 이벤트 소스 생성에 관리자 권한이 필요한 이유(Security 로그를 포함한 전체 로그 검색이 필요하기 때문)에 대해. ↩ ↩2 ↩3
-
Microsoft Learn, Getting Started with EventSource. EventSource가 Publish-Subscribe 패턴으로 동작하며 구독자(ETW나 EventPipe)가 없으면 이벤트가 기록되지 않는다는 점, dotnet-trace collect에서의 수집 방법에 대해. ↩ ↩2
-
Microsoft Learn, CA2254: Template should be a static expression. 로그 메시지 템플릿에 문자열 연결이나 문자열 보간을 쓰면 플레이스홀더 이름과 값의 대응 관계가 사라진다는 점에 대해. ↩ ↩2
-
Microsoft Learn, EventLog.MaximumKilobytes Property. 이벤트 로그의 기본 최대 크기가 512킬로바이트라는 점에 대해. ↩ ↩2
-
Microsoft Learn, EventLog Class. Application/System/Security 세 가지 기본 로그, 소스는 한 대 컴퓨터 상에서 유일해야 한다는 점, 쓰기 권한만 있으면 임의의 등록된 소스 이름으로 쓸 수 있는 구조에 대해. ↩ ↩2
-
Microsoft Learn, EventLogSettings Class. AddEventLog의 기본값(LogName이 “Application”, SourceName이 “.NET Runtime”)에 대해. ↩ ↩2
-
Microsoft Learn, Logging providers in .NET. Generic Host의 기본 로깅 프로바이더에 Console·Debug·EventSource·EventLog(Windows만)가 포함된다는 점에 대해. ↩ ↩2
-
Microsoft Learn, Event Sources. 미등록 소스 이름으로 쓰면 Application 로그로 폴백되지만, 메시지 파일이 없어 이벤트 뷰어가 설명문을 표시하지 못하고 오류가 되는 점에 대해. ↩ ↩2
-
Microsoft Learn, Get-WinEvent.
-FilterHashtable에LogName·ProviderName등의 키를 넘겨 이벤트를 좁힐 수 있다는 점, 가져온 이벤트가TimeCreated·Id·LevelDisplayName·Message같은 속성을 가진다는 점에 대해. ↩ -
Microsoft Learn, Event Tracing. Event Tracing for Windows(ETW)가 애플리케이션 트레이스 이벤트의 시작·정지·계측·소비를 수행하는 메커니즘이라는 점에 대해. ↩
-
Microsoft Learn, dotnet-trace performance analysis utility. EventPipe에 기반한 크로스 플랫폼 트레이스 수집 도구 dotnet-trace의 개요와 collect 명령에 대해. ↩
-
Microsoft Learn, Introduction to WPR. Windows Performance Recorder(WPR)가 ETW를 확장해 시스템과 애플리케이션의 상세 기록을 제공하는 도구라는 점에 대해. ↩
-
Microsoft Learn, Logging in C# and .NET. 로그 메시지 템플릿의 {PlaceHolder} 구문과, 템플릿에 포함된 키 이름이 로그 속성 이름이 되는 구조에 대해. ↩
-
Microsoft Learn, High-performance logging in .NET. LoggerMessageAttribute에 의한 소스 생성 로그가 박싱이나 템플릿 분석 비용을 피할 수 있다는 점에 대해. ↩
-
Microsoft Learn, Comparison of ETW and EventLog logger functionality. 이벤트 뷰어에서 XPath 쿼리로 이벤트 이름 등을 써서 사용자 지정 보기를 만드는 방법에 대해. ↩
-
Microsoft Learn, wevtutil. 이벤트 로그 내보내기(epl), 쿼리(qe), 설정 변경(sl) 등의 명령줄 조작에 대해. ↩
-
Microsoft Learn, OverflowAction Enum. 이벤트 로그가 한도 크기에 도달했을 때의 동작(덮어쓰기·폐기)과 OverwriteOlder가 사용 중단된 점에 대해. ↩
관련 기사
같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.
PerfView와 dotnet-trace로 「느림」을 특정한다 ── .NET 성능 조사의 실무 입문
업무 앱이 「느리다」「CPU가 붙어 있다」일 때, 어떤 도구로 무엇을 볼지. PerfView와 dotnet-trace의 역할 분담, CPU 샘플링 읽는 법, ThreadTime으로 블록 시간을 조사하는 절차까지 실무 조사 흐름을 정리합니다.
Windows 프린터 드라이버 제공 종료 ── 업무 앱의 장표·라벨 인쇄는 어떻게 대비할 것인가
Microsoft는 v3/v4 프린터 드라이버의 제공 종료를 단계적으로 진행하고 있으며, 2026년 7월부터는 IPP 클래스 드라이버가 우선됩니다. Windows protected print mode에서 무엇이 사라지는지, 업무 앱의 장표·라벨 ...
WPR/WPA 실무 ── 「PC 전체가 느리다」를 시스템 전체에서 조사하는 성능 조사 입문
「PC 전체가 느리다」「부팅이 느리다」처럼 작업 관리자로는 따라갈 수 없는 성능 문제는 OS 전체 ETW 트레이스를 모아 읽는 WPR/WPA로 조사합니다. wpr.exe 수집 절차부터 WPA에서 CPU·대기·디스크 I/O를 읽는 법까지 설명합니다.
Windows I/O 내부 구조(제1회) ── 모든 읽기·쓰기는 IRP가 된다: I/O 시스템의 전체 그림
Windows I/O 시스템을 밑바닥부터 설명하는 연재의 제1회입니다. Object Manager namespace, driver·device·file 세 가지 object, IRP 수명 주기, CloseHandle 뒤에서 일어나는 일까지를 그림...
슬립・최대 절전 모드・Modern Standby와 장시간 가동 앱 ── '한밤중에 멈춰 있었다'를 설계로 막는다
장시간 동작하는 Windows 앱이 '아침에 보니 멈춰 있었다'가 되는 원인을 S3 슬립/최대 절전 모드/Modern Standby의 차이에서 정리합니다. 슬립 중 타이머와 TCP 연결의 동작, SetThreadExecutionState로 억제하...
관련 토픽
이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.
Windows 기술 토픽
Windows 개발, 장애 조사, 기존 자산 활용에 관한 KomuraSoft LLC 기사를 모은 토픽 허브입니다.
장애 조사 & 장기 가동 장애
간헐적 장애, 통신 진단, 장기 가동 크래시, 실패 경로 테스트 기반을 정리한 토픽 페이지입니다.
이 주제와 연결되는 서비스
이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.
Windows 앱 개발
로그·이벤트 로그·ETW 기반 설계는 Windows 앱 개발 상담 범위에 해당하기 때문입니다.
장애 조사 & 원인 분석
장애 조사를 염두에 둔 로그 설계는 장애 조사·원인 분석 상담으로 직결되기 때문입니다.
자주 묻는 질문
이 기사 주제에 대해 상담 시 자주 나오는 질문을 모았습니다.
- 파일 로그가 있으면 이벤트 로그는 필요 없나요?
- 상세 조사용이라면 파일 로그만으로 충분한 경우도 많습니다. 다만 운영 담당자가 OS 표준 이벤트 뷰어나 모니터링 도구(작업 스케줄러의 실패 알림, SCOM 등)로 앱 이상을 알아차리게 하려면, 이벤트 로그에는 요점만 남기는 출력을 병용하는 편이 실무적입니다. 둘은 대체 관계가 아니라, 독자가 다른 별도 계층의 기록으로 보면 됩니다.
- 이벤트 ID 채번 규칙은 어떻게 정하면 되나요?
- 정해진 정답은 없습니다. 실무에서는 『기능 영역마다 대역을 나눈다(기동 계통은 1000번대, 통신 계통은 2000번대 등)』 『한 번 부여한 ID는 의미를 바꾸지 않고 재사용하지 않는다』 『결번을 허용한다』라는 세 가지를 지키면 사고가 줄어듭니다. ILogger의 EventId는 구조화 로그의 검색 키로도 쓰이므로, 나중에 문서화하기 쉬운 채번으로 두면 유지보수 때 도움이 됩니다.
- Serilog나 NLog를 쓰고 있는데, 이 기사 내용과 어떻게 관련되나요?
- Serilog와 NLog는 ILogger 아래에서 파일·이벤트 로그·ETW·외부 SaaS 등 여러 싱크로 같은 로그를 나누어 보내는 구현 중 하나입니다. 이 기사에서 다룬 『이벤트 로그에는 요점만』 『메시지 템플릿을 깨지 않는다』라는 설계 방침은 ILogger를 직접 쓸 때든 Serilog/NLog를 거칠 때든 마찬가지로 적용됩니다. 이벤트 로그용 싱크(Serilog.Sinks.EventLog 등)를 쓸지, 표준 AddEventLog를 쓸지는 구현상의 선택입니다.
- ETW는 어디까지 배우면 충분한가요?
- 업무 앱의 개발·유지보수가 주 목적이라면, EventSource로 고유 프로바이더를 만들 수 있고, dotnet-trace로 자신의 프로바이더 이벤트를 수집할 수 있는 정도까지 익히면 충분합니다. PerfView에서의 콜 스택 분석이나 WPR의 커널 이벤트 수집은 성능 조사를 전문으로 하는 단계에서 배우면 되는 영역이며, 이 기사에서는 일부러 범위 밖으로 두었습니다.