From d6b15c3cd8ec2af4328fa5ca153902db03a085ee Mon Sep 17 00:00:00 2001 From: SEAN Date: Mon, 9 Feb 2026 15:10:17 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20Serilog=20=EA=B5=AC=EC=A1=B0=EC=A0=81?= =?UTF-8?q?=20=EB=A1=9C=EA=B9=85=20=EC=84=A4=EC=A0=95=20(#22)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - appsettings.json/Development.json에 Serilog 섹션 추가 (Console/File Sink, Rolling Daily) - RequestIdMiddleware 구현 (X-Request-ID 헤더 발급/반환) - Program.cs에 Serilog 호스트 빌더 + UseSerilogRequestLogging 등록 - 환경별 로그 레벨 분리 (Development: Debug, Production: Warning) --- SPMS.API/Middlewares/RequestIdMiddleware.cs | 19 ++++++++++++ SPMS.API/Program.cs | 22 +++++++++++++- SPMS.API/appsettings.Development.json | 28 ++++++++++++++---- SPMS.API/appsettings.json | 32 +++++++++++++++++---- 4 files changed, 88 insertions(+), 13 deletions(-) create mode 100644 SPMS.API/Middlewares/RequestIdMiddleware.cs diff --git a/SPMS.API/Middlewares/RequestIdMiddleware.cs b/SPMS.API/Middlewares/RequestIdMiddleware.cs new file mode 100644 index 0000000..a8038eb --- /dev/null +++ b/SPMS.API/Middlewares/RequestIdMiddleware.cs @@ -0,0 +1,19 @@ +namespace SPMS.API.Middlewares; + +public class RequestIdMiddleware +{ + private readonly RequestDelegate _next; + + public RequestIdMiddleware(RequestDelegate next) => _next = next; + + public async Task InvokeAsync(HttpContext context) + { + var requestId = context.Request.Headers["X-Request-ID"].FirstOrDefault() + ?? Guid.NewGuid().ToString("N"); + + context.Items["RequestId"] = requestId; + context.Response.Headers["X-Request-ID"] = requestId; + + await _next(context); + } +} diff --git a/SPMS.API/Program.cs b/SPMS.API/Program.cs index 7aeebdd..17bab8d 100644 --- a/SPMS.API/Program.cs +++ b/SPMS.API/Program.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using Serilog; using SPMS.API.Extensions; using SPMS.API.Middlewares; using SPMS.Application.Interfaces; @@ -15,6 +16,10 @@ var builder = WebApplication.CreateBuilder(new WebApplicationOptions ?? "wwwroot" }); +// ===== 1. Serilog ===== +builder.Host.UseSerilog((context, config) => + config.ReadFrom.Configuration(context.Configuration)); + builder.Services.AddControllers(); builder.Services.AddOpenApi(); @@ -36,6 +41,21 @@ var app = builder.Build(); // ── 1. 예외 처리 (최외곽 — 이후 모든 미들웨어 예외 포착) ── app.UseMiddleware(); +// ── 3. X-Request-ID 발급/반환 (클라이언트 디버깅용) ── +app.UseMiddleware(); + +// ── 4. Serilog 구조적 로깅 (X-Request-ID 이후에 위치) ── +app.UseSerilogRequestLogging(options => +{ + options.EnrichDiagnosticContext = (diagnosticContext, httpContext) => + { + if (httpContext.Items.TryGetValue("RequestId", out var requestId)) + { + diagnosticContext.Set("RequestId", requestId); + } + }; +}); + // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { @@ -66,4 +86,4 @@ app.UseAuthorization(); app.MapControllers(); app.MapFallbackToFile("index.html"); -app.Run(); \ No newline at end of file +app.Run(); diff --git a/SPMS.API/appsettings.Development.json b/SPMS.API/appsettings.Development.json index 46f97e8..e26f77c 100644 --- a/SPMS.API/appsettings.Development.json +++ b/SPMS.API/appsettings.Development.json @@ -1,10 +1,4 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, "AllowedHosts": "*", "ConnectionStrings": { "DefaultConnection": "" @@ -22,5 +16,27 @@ "UserName": "", "Password": "", "VirtualHost": "dev" + }, + "Serilog": { + "MinimumLevel": { + "Default": "Debug", + "Override": { + "SPMS": "Debug", + "Microsoft.AspNetCore": "Information", + "Microsoft.EntityFrameworkCore": "Warning" + } + }, + "WriteTo": [ + { "Name": "Console" }, + { + "Name": "File", + "Args": { + "path": "Logs/spms-.log", + "rollingInterval": "Day", + "retainedFileCountLimit": 7, + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{RequestId}] {Message:lj}{NewLine}{Exception}" + } + } + ] } } \ No newline at end of file diff --git a/SPMS.API/appsettings.json b/SPMS.API/appsettings.json index b70ce76..4af3f63 100644 --- a/SPMS.API/appsettings.json +++ b/SPMS.API/appsettings.json @@ -1,10 +1,4 @@ { - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, "AllowedHosts": "*", "ConnectionStrings": { "DefaultConnection": "" @@ -22,5 +16,31 @@ "UserName": "", "Password": "", "VirtualHost": "/" + }, + "Serilog": { + "Using": ["Serilog.Sinks.Console", "Serilog.Sinks.File"], + "MinimumLevel": { + "Default": "Warning", + "Override": { + "SPMS": "Information", + "Microsoft.AspNetCore": "Warning", + "Microsoft.EntityFrameworkCore": "Warning" + } + }, + "WriteTo": [ + { + "Name": "File", + "Args": { + "path": "Logs/spms-.log", + "rollingInterval": "Day", + "retainedFileCountLimit": 30, + "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{RequestId}] {Message:lj}{NewLine}{Exception}" + } + } + ], + "Enrich": ["FromLogContext"], + "Properties": { + "Application": "SPMS_API" + } } } \ No newline at end of file -- 2.45.1