정적 메서드 호출로 Serilog 편하게 쓰기
이철우
객체지향 언어 C#에는 '전역 변수’가 없습니다. 클래스 안에 정적 변수를 public으로 만들어 전역 변수처럼 씁니다. 프로그램을 배우며 가장 먼저 만나고 ‘어디에서나’ 호출할 수 있는
Console.WriteLine("Hello, world!");
에서 'WriteLine’이 클래스 Console의 ‘정적’ 메서드입니다. 화면에 로그 남기는 용도로 이 메서드를 사용하기도 합니다. 파일에 로그를 남기려면 스스로 구현하거나 다른 사람이 만든 것을 쓰는 데, 저는 Serilog를 골랐습니다.
의존성 주입없이, Console.WriteLine 메서드 처럼, ‘어디에서나’ 호출할 수 있기 때문입니다.
Serilog.Log.Information("Hello, world!");
이제 제가 사용하는 방법을 소개하겠습니다. 새로 닷넷 9.0 클래스 라이브러리 프로젝트 Utils를 임의의 솔루션에 더합니다. Nuget에서 패키지 Serilog (4.2.0), Serilog.Sinks.Async(2.1.0), Serilog.Sinks.Console(6.0.0), Serilog.Sinks.File(6.0.0)를 골라 프로젝트 Utils에 설치합니다.
클래스 SerilogSettings 를 Utils에 추가 합니다.
// SerilogSettings.cs
public static class SerilogSettings
{
private static string? _logFilePath;
public static void Initializer(string logFilePath)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
//.WriteTo.Console()
//.WriteTo.File(logFilePath, rollingInterval: RollingInterval.Day, restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Information)
.WriteTo.Async(a => a.Console())
.WriteTo.Async(a => a.File(logFilePath, rollingInterval: RollingInterval.Day, restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Information))
.CreateLogger();
Log.Logger.Information($"{nameof(Serilog)} {INITIALIZED} {logFilePath} {nameof(Initializer)} {nameof(SerilogSettings)}");
_logFilePath = logFilePath;
/* LogEventLevel
*
* Verbose
* Debug
* Information : Default Level
* Warning
* Error
* Fatal
*
*/
}
public static async Task Finalizer()
{
Log.Logger.Information($"{nameof(Serilog)} {FINALLIZING} {_logFilePath} {nameof(Finalizer)} {nameof(SerilogSettings)}");
await Log.CloseAndFlushAsync().ConfigureAwait(false);
}
private static readonly string INITIALIZED = "Initialized.";
private static readonly string FINALLIZING = "Finalizing...";
}
이 프로젝트를 빌드하면, 서비스 Serilog를 사용할 준비가 된 것입니다. 이 서비스를 사용하기 위해 닷넷 9.0 콘솔 프로젝트 Test를 현재 솔루션에 더하고, Utils 프로젝트를 참조하고 Nuget에서 패키지 Serilog (4.2.0)를 설치합니다.
그리고 Program.cs를 아래와 같이 편집합니다.
// Program.cs
using Serilog;
using Test;
using Utils;
SerilogSettings.Initializer(@"D:\Log\Log_.txt");
Log.Information("Hello, World!");
Log.Error("Test error.");
Log.Information("Bye.");
await SerilogSettings.Finalizer().ConfigureAwait(false);
Test 프로젝트를 실행하면, 아래처럼 나타납니다. 아울러 Log_20250315.txt 파일에도 기록이 남습니다.
클래스 Person을 프로젝트 Test에 추가하고 생성자를 아래처럼 바꿉니다.
// Person.cs
public class Person
{
public Person()
{
Serilog.Log.Debug($"Created: {nameof(Person)}");
}
}
클래스 Person을 시험하기 위해 Program.cs을 아래처럼 바꿉니다.
// Program.cs
using Serilog;
using Test;
using Utils;
SerilogSettings.Initializer(@"D:\Log\Log_.txt");
Log.Information("Hello, World!");
var person = new Person();
Log.Information("Bye.");
await SerilogSettings.Finalizer().ConfigureAwait(false);
프로젝트 Test를 실행하면 아래처럼 됩니다.
만약 WPF 프로젝트(출력형태 Console)라면, 프로젝트 Utils를 참조하고 패키지 Serilog (4.2.0)를 설치하고 App.xaml.cs를 아래와 같이 바꿉니다.
// App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
SerilogSettings.Initializer(@"D:\Log\Log_.txt");
}
protected override async void OnExit(ExitEventArgs e)
{
await SerilogSettings.Finalizer().ConfigureAwait(false);
}
}
의존성 주입하지 않고, 어디에서나 호출할 수 있는 Serilog, 정말 편합니다.