feat: Serilog 구조적 로깅 설정 (#22) #23

Merged
seonkyu.kim merged 1 commits from feature/#22-serilog-logging into develop 2026-02-09 06:13:10 +00:00
4 changed files with 88 additions and 13 deletions

View File

@ -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);
}
}

View File

@ -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<ExceptionMiddleware>();
// ── 3. X-Request-ID 발급/반환 (클라이언트 디버깅용) ──
app.UseMiddleware<RequestIdMiddleware>();
// ── 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())
{

View File

@ -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}"
}
}
]
}
}

View File

@ -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"
}
}
}