C# Serilog 사용법
로깅은 “나중에 문제 생겼을 때, 과거에 무슨 일이 있었는지”를 재현하기 위해 남겨두는 블랙박스 같은 역할을 합니다. 그 중에서도 Serilog 는 구조화 로그(Structured Logging)를 지원해서, 단순 문자열 로그를 넘어서 검색/분석 하기 좋은 로그를 남길 수 있게 해줍니다. 이글 에서는:
- Serilog 가 뭐고, 왜 쓰는지
- Nuget 설치
- 가장 단순한 콘솔 예제
- **파일 로그, 로그 레벨, 롤링 파일
- WPF 앱에서 Serilog 초기화 & 전역 예외 처리
- 구조화 로그(파라미터)와 Tip 까지 한 번에 정리해볼게요.
1. Serilog 는 뭐가 좋은가?
간단히 정리하면:
- 설정이 쉽다: 코드 몇 줄로 바로 로그 파일 생성 가능
- Structured Logging:
UserId=123,OrderId=456같은 필드를 따로 남겨서, 나중에 “UserId=123 인 로그 만 검색” 이런 게 쉬움 - Sinks 가 많다: 파일, 콘솔, Seq, ElasticSearch, DB 등등 다양
- 레벨별 필터링: 개발/운영 환경에 따라 Debug를 끄거나, Error만 남기기를 쉽게 설정 가능
2. Nuget 패키지 설치
기본적으로 아래 정도만 설치해도 웬만하면 다 됩니다.
SerilogSerilog.Sinks.File(파일 로그)Serilog.Sinks.Console(콘솔)Serilog.Sinks.Async(비동기 기록 - UI 끊김 방지)- (선택)
Serilog.Exceptions(예외 정보 디테일하게 남기기) - (선택)
Serilog.Enrichers.CalllerInfo(콜 함수 이름, 라인 번호 남기기) - (선택)
Serilog.Enrichers.Thread(쓰레드 정보 남기기)
Visual Studio 패키지 관리자 콘솔에서:
1
2
3
4
5
6
7
Install-Package Serilog
Install-Package Serilog.Sinks.File
Install-Package Serilog.Sinks.Console
Install-Package Serilog.Sinks.Async
Install-Package Serilog.Exceptions
Install-Package Serilog.Enrichers.CalllerInfo
Install-Package Serilog.Enrichers.Thread
3. 가장 단순한 콘솔 에제
Serilog 기본 감 잡기용으로, 콘솔 앱 에제를 먼저 볼게요.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using Serilog;
using Serilog.Events;
using Serilog.Exceptions;
class Program
{
static void Main(string[] args)
{
// 1) 전역 로거 구성
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug() // 최소 로그 레벨
.Enrich.FromLogContext()
.Enrich.WithExceptionDetails() // 예외 정보 자세히
// 콘솔 출력
.WriteTo.Console()
// 파일 출력 (하루에 한 파일씩)
.WriteTo.Async(a => a.File(
path: "logs/app-.log",
rollingInterval: RollingInterval.Day,
retainedFileCountLimit: 14
))
.CreateLogger();
try
{
Log.Information("프로그램 시작. args={@Args}", args);
// 예제 코드
int a = 10;
int b = 0;
Log.Debug("나누기 실행 전. a={A}, b={B}", a, b);
var result = a / b; // 예외 발생
Log.Information("결과: {Result}", result);
}
catch (Exception ex)
{
Log.Error(ex, "예외 발생! 뭔가 잘못됐습니다.");
}
finally
{
Log.CloseAndFlush();
}
}
}
포인트 정리
MinimunLevel.Debug()- Debug 이상 (Debug, Information, Warning, Error, Fatal) 로그를 남김
WriteTo.Async(...)- 파일 기록을 비동기로 처리해서, 성능과 UI 끊김을 줄임
Log.Information("...{값}", value){}안에 들어가는 건 “필드 이름”이 되고, Serilog 내부에서는 구조화된 데이터로 저장됨"args={@Args}"처럼 앞에@를 쓰면 객체를 구조적으로 기록
4.로그 레벨 간단 정리
Serilog (그리고 대부분 로깅 라이브러리)는 로그 레벨을 아래처럼 씁니다.
Verbose: 디버깅용 아주 상세한 로그Debug: 개발 중 디버깅용Information: 정상적인 흐름(사용자 행동, 상태 변화)Warning: 이상 징후, 하지만 아직 동작은 됨Error: 기능이 실패, 예외 발생 등Fatal: 시스템 전체에 치명적인 오류 (앱 다운 수준)
실무에서 많이 쓰는 패턴:
- 로컬 개발: `MinimumLevel.Debug()
- 운영 서버:
MinimumLevel.Information()또는Warning()
이렇게 두고,appsettings.json이나 환경변수로 바꿔주기도 합니다.
5. 파일 로그, 롤링 설정 예시
조금 더 실무스럽게 파일 설정을 보자면:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.Enrich.FromLogContext()
.Enrich.WithExceptionDetails()
.WriteTo.Async(a => a.File(
path: "logs/app/app-.log",
rollingInterval: RollingInterval.Day, // 매일 새로운 파일
retainedFileCountLimit: 30, // 30일치 보관
fileSizeLimitBytes: 50 * 1024 * 1024, // 파일 50MB 넘으면 분할
rollOnFileSizeLimit: true, // 사이즈 초과 시 app-20251113_001.log 이런 식으로
shared: true,
encoding: Encoding.UTF8
))
.CreateLogger();
6. WPF에서 Serilog 적용하기
WPF에서는 앱이 시작될 때 한번만 로거를 구성하고,
각 ViewModel이나 서비스에서 Log.Information(...) 을 사용하는 방식이 깔끔합니다.
6-1. App.xaml.cs 에서 전역 설정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
using Serilog;
using Serilog.Exceptions;
using Serilog.Events;
using System;
using System.Windows;
using System.Text;
public partial class App : Application
{
public App()
{
ConfigureLogging();
RegisterGlobalExceptionHandlers();
}
private void ConfigureLogging()
{
Log.Logger = new LoggerConfiguration()
// Microsoft, System 로그는 Warning 이상만
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Debug()
.Enrich.FromLogContext()
.Enrich.WithExceptionDetails()
.WriteTo.Async(a => a.File(
path: "logs/app/app-.log",
rollingInterval: RollingInterval.Day,
retainedFileCountLimit: 14,
encoding: Encoding.UTF8
))
.CreateLogger();
Log.Information("===== WPF App 시작 =====");
}
private void RegisterGlobalExceptionHandlers()
{
// WPF UI 스레드 예외
this.DispatcherUnhandledException += (s, e) =>
{
Log.Fatal(e.Exception, "DispatcherUnhandledException");
e.Handled = true; // 앱을 계속 살려둘지 여부는 상황에 따라
};
// Task에서 처리 안 된 예외
AppDomain.CurrentDomain.UnhandledException += (s, e) =>
{
if (e.ExceptionObject is Exception ex)
{
Log.Fatal(ex, "UnhandledException");
}
};
}
protected override void OnExit(ExitEventArgs e)
{
Log.Information("===== WPF App 종료 =====");
Log.CloseAndFlush();
base.OnExit(e);
}
}
6-2. ViewModel / 서비스에서 사용
간단하게 그냥 static Log를 직접 사용해도 됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using Serilog;
public class MainWindowViewModel
{
public MainWindowViewModel()
{
Log.Information("MainWindowViewModel 생성됨");
}
public void OnClickStart()
{
Log.Information("Start 버튼 클릭됨");
try
{
// 작업...
}
catch (Exception ex)
{
Log.Error(ex, "Start 작업 중 예외 발생");
}
}
}
나중에 규모가 커지면 ILogger 를 DI로 주입해서 쓰는 방식(HostBuilder + Microsoft.Extensions.Logging 연동)으로 확장하면 됩니다.
7. 구조화 로그 (파라미터 활용하기)
Serilog의 핵심 장점이 바로 Structured Logging입니다.
1
Log.Information("사용자 로그인. UserId={UserId}, Ip={Ip}", userId, ip);
위 코드는 내부적으로는:
- 메시지 텍스트:
"사용자 로그인. UserId=123, Ip=1.2.3.4" - 구조화 필드:
UserId: 123Ip: "1.2.3.4"
두 가지를 모두 기록해줍니다.
나중에 Seq나 ElasticSearch 같은 데 연동하면
“UserId=123인 로그만 보기” “Ip=1.2.3.4인 에러만 보기” 같은 필터링이 엄청 쉬워집니다.
복잡한 객체는 @를 붙여서:
1
Log.Information("주문 생성. {@Order}", orderDto);
이렇게 남겨두면, orderDto 안의 필드들이 전체 다 구조화되어 로그에 찍힙니다.
8. 자주 쓰는 패턴/팁 정리
8-1. 로그 폴더를 역할별로 나누기
예: UI 로그와 통신 로그를 분리하고 싶을 때
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public static class LogService
{
public enum LogType { App, Comm }
private const string LogTypeProperty = "LogType";
public static readonly ILogger App;
public static readonly ILogger Comm;
static LogService()
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.Enrich.FromLogContext()
.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(e =>
e.Properties.TryGetValue(LogTypeProperty, out var v) &&
v.ToString() == "\"App\"")
.WriteTo.File("logs/app/app-.log",
rollingInterval: RollingInterval.Day))
.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(e =>
e.Properties.TryGetValue(LogTypeProperty, out var v) &&
v.ToString() == "\"Comm\"")
.WriteTo.File("logs/comm/comm-.log",
rollingInterval: RollingInterval.Day))
.CreateLogger();
App = Log.ForContext(LogTypeProperty, LogType.App);
Comm = Log.ForContext(LogTypeProperty, LogType.Comm);
}
}
사용은:
1
2
LogService.App.Information("버튼 클릭");
LogService.Comm.Information("송신 JSON: {Json}", json);
이렇게 하면 logs/app 에는 UI/일반 로그, logs/comm/ 에는 통신 로그만 쌓이게 할 수 있습니다.
9. 마무리
요약하면:
- Serilog 설치(Serilog + File/Console/Async/Exceiptions)
Log.Logger = new LoggerConfiguration()...로 전역 로거 구성- 콘솔/파일 Sink 추가
- WPF 라면
App.xaml.cs에서 한 번만 설정 + 전역 예외 핸들링 - 실체 코드에서는
Log.Information,Log.Error(ex,"...")사용 {Property}형식으로 구조화 로그를 적극 활용