C# 로그 아키텍처 설계: Serilog, NLog, ILogger 통합 전략
애플리케이션이 복잡해질수록 로그는 단순한 디버깅 도구를 넘어 운영의 눈이 됩니다. 특히 WPF, Blazor, ASP.NET Core 같은 환경에서는 성능, 장애, 사용자 행동 분석까지 로그가 필수입니다. 그러나 로그를 아무렇게나 남기면 오히려 관리가 어렵고, 로그의 의미가 모호해집니다. 이번 글에서는 C# 실무 환경에서 Serilog, NLog, ILogger를 통합하여 효율적이고 유연한 로그 아키텍처를 설계하는 방법을 예제 중심으로 소개합니다.
1. 로그 아키텍처의 목표
로그 시스템을 설계할 때 가장 먼저 고려해야 할 것은 무엇을, 언제, 어떻게 기록할 것인가입니다. 로그는 단순한 메시지의 집합이 아니라, 진단과 추적을 위한 데이터 구조입니다.
좋은 로그 설계의 핵심 원칙
- 일관성: 모든 로그가 동일한 형식, 시간대, 레벨 정책을 따를 것.
- 구조화: 사람이 아닌 시스템이 분석할 수 있도록 JSON 형태로 기록.
- 분리 가능성: 로그를 환경, 모듈, 심각도별로 쉽게 필터링 가능해야 함.
- 확장성: 파일, 콘솔, DB, 클라우드 등 다양한 Sink로 손쉽게 확장.
2. ILogger 기반 공통 인터페이스 설계
C#의 기본 로깅 인터페이스는 Microsoft.Extensions.Logging.ILogger입니다.
이 인터페이스를 기반으로 하면, Serilog이나 NLog 같은 외부 로거를 쉽게 교체하거나 병행 사용할 수 있습니다.
ILogger 인터페이스 (Microsoft.Extensions.Logging)
로깅을 수행하는 데 사용되는 형식을 나타냅니다.
learn.microsoft.com
public class AppService
{
private readonly ILogger<AppService> _logger;
public AppService(ILogger<AppService> logger)
{
_logger = logger;
}
public void Run()
{
_logger.LogInformation("앱이 시작되었습니다.");
try
{
throw new InvalidOperationException("샘플 예외 발생!");
}
catch (Exception ex)
{
_logger.LogError(ex, "예외 발생: {Message}", ex.Message);
}
}
}
이렇게 작성하면 로깅 구현체를 바꾸더라도 코드 수정이 거의 필요 없습니다.
3. Serilog 설정 및 통합
Serilog는 구조화된 로깅을 위한 강력한 프레임워크입니다. JSON 형태로 로그를 남기고, 다양한 Sink를 지원합니다.
Serilog에서 데이터 가져오기 - Microsoft Fabric
실시간 인텔리전스의 KQL 데이터베이스에서 Serilog로부터 데이터를 가져오는 방법을 알아봅니다.
learn.microsoft.com
Serilog 기본 설정 (Program.cs)
using Serilog;
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.File("logs/app_log.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
var builder = Host.CreateDefaultBuilder(args)
.UseSerilog() // ILogger와 Serilog 연결
.ConfigureServices((context, services) =>
{
services.AddSingleton<AppService>();
});
var app = builder.Build();
app.Services.GetRequiredService<AppService>().Run();
Serilog의 강점
- JSON 포맷 로그로 ELK(Elasticsearch, Logstash, Kibana) 연계 용이
- 다양한 출력 대상(Sink) 지원: 콘솔, 파일, Seq, MSSQL, Application Insights 등
- 풍부한 Enricher 기능으로 로그에 Context 정보 자동 추가 가능
예시:
Log.Information("사용자 로그인: {@User}", new { Id = 101, Name = "홍길동" });
결과(JSON 로그):
{
"Timestamp": "2025-10-05T10:15:30Z",
"Level": "Information",
"MessageTemplate": "사용자 로그인: {@User}",
"Properties": {
"User": { "Id": 101, "Name": "홍길동" }
}
}
4. NLog 설정 및 통합
NLog는 오랜 역사를 가진 로깅 프레임워크로, XML 기반 설정 파일을 통해 유연하게 로그 정책을 제어할 수 있습니다.
NLog 싱크를 사용하여 Azure Data Explorer로 데이터 수집 - Azure Data Explorer
Azure Data Explorer NLog 커넥터를 사용하여 클러스터에 데이터를 수집하는 방법을 알아봅니다.
learn.microsoft.com
NLog.config 예시
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true" internalLogLevel="Info">
<targets>
<target xsi:type="File" name="logfile" fileName="logs/nlog.txt" layout="${longdate}|${level}|${logger}|${message}" />
<target xsi:type="Console" name="console" layout="${message}" />
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="logfile,console" />
</rules>
</nlog>
Program.cs 통합
using NLog;
using NLog.Extensions.Logging;
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
try
{
logger.Info("애플리케이션 시작");
}
catch (Exception ex)
{
logger.Error(ex, "애플리케이션 오류 발생");
throw;
}
finally
{
LogManager.Shutdown();
}
NLog는 XML 설정 파일만 교체하면 로그 정책(파일명, 레벨, 출력 형식)을 손쉽게 바꿀 수 있다는 장점이 있습니다.
5. Serilog과 NLog, ILogger의 통합 전략
실무에서는 한 프로젝트 안에서 다양한 모듈이 서로 다른 로깅 프레임워크를 사용하기도 합니다.
이때 가장 중요한 것은 ILogger를 중심으로 통합하는 것입니다.
전략 1: ILogger 중심의 통합
ILogger를 기반으로 Serilog 또는 NLog를 연결해두면, 기존 코드 수정 없이 교체 가능.
Host.CreateDefaultBuilder(args)
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.AddSerilog(); // 또는 AddNLog();
});
전략 2: 로그 레벨 정책 통합
모든 로그 시스템은 공통적으로 LogLevel을 갖습니다.
다만 Serilog은 MinimumLevel, NLog는 minlevel, ILogger는 LogLevel을 사용합니다.
정책을 일관되게 유지하려면 appsettings.json에 로그 수준을 중앙 관리하는 것이 좋습니다.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning"
}
}
}
전략 3: 환경별 로그 분리
- 개발 환경(dev): 콘솔 출력 중심, 실시간 피드백 중시
- 스테이징(stg): 파일 + 콘솔 병행, 문제 재현 추적
- 운영(prod): 파일 + 원격 서버 전송 (예: Seq, ElasticSearch)
if (env.IsDevelopment())
Log.Logger = new LoggerConfiguration().WriteTo.Console().CreateLogger();
else
Log.Logger = new LoggerConfiguration().WriteTo.File("logs/app.log").CreateLogger();
6. 실무 아키텍처 구성 예시
[WPF/Blazor App]
↓
[ILogger Interface]
↓
[Serilog Wrapper Class] ─→ [Console]
├→ [File]
└→ [Seq / ElasticSearch]
예시: LoggerHelper 클래스
public static class LoggerHelper
{
private static readonly ILogger _logger = LoggerFactory.Create(builder =>
{
builder.AddSerilog();
}).CreateLogger("AppLogger");
public static void Info(string message)
{
_logger.LogInformation(message);
}
public static void Error(Exception ex, string message)
{
_logger.LogError(ex, message);
}
}
이렇게 구성하면 비즈니스 로직에서는 LoggerHelper.Info("시작")처럼 간단하게 호출하면서도, 실제 구현은 Serilog이든 NLog이든 자유롭게 교체할 수 있습니다.
7. 로그 모니터링과 분석
로그는 단순히 저장하는 것에서 끝나지 않습니다. 수집, 집계, 시각화를 통해 문제 예측과 성능 분석으로 이어질 수 있습니다.
추천 도구
- Seq: Serilog 전용 실시간 로그 모니터링 툴
- ELK 스택: 대규모 로그 분석에 적합 (Elasticsearch + Logstash + Kibana)
- Application Insights: Azure 기반 로그 통합 분석 서비스
Log.Logger = new LoggerConfiguration()
.WriteTo.Seq("http://localhost:5341")
.Enrich.WithThreadId()
.CreateLogger();
이렇게 하면 실시간으로 로그를 필터링하고, 특정 사용자나 기능별 로그를 분석할 수 있습니다.
8. 결론
C# 로그 아키텍처 설계의 핵심은 표준화와 유연성의 균형입니다.
ILogger를 중심으로 Serilog이나 NLog를 통합하면, 환경별 정책 관리가 쉬워지고 유지보수성도 향상됩니다.
특히 구조화된 로그를 도입하면, 단순한 로그 파일을 넘어 데이터 분석과 운영 자동화의 기반이 됩니다.
'C#' 카테고리의 다른 글
| C# 성능 최적화 실무편: Async 패턴과 병렬 처리 완벽 가이드 (0) | 2025.10.12 |
|---|---|
| C# 멀티스레딩 실무편: Task, Parallel, ThreadPool 완벽 비교와 적용 전략 (0) | 2025.10.08 |
| C#으로 Windows 레지스트리를 제어하여 애플리케이션 값 주고받기 (0) | 2025.09.10 |
| C# .NET에서 메모리 관리 심층 분석 #2 : GC, Span, 메모리 풀링 전략 + BenchmarkDotNet 활용 (0) | 2025.09.07 |
| C# .NET에서 메모리 관리 심층 분석 #1 : GC, Span, 메모리 풀링 전략 (0) | 2025.09.07 |