로깅 - Logging
Logging
설명
Axs Framework
를 Castle
인터페이스를 구현한 Serilog
의 로깅 기능을 기본으로 사용하며 Log4Net, NLog등 다영한 로깅라이브러로 변경 할 수 있습니다.
예제
먼저 로그를 작성하기 위해 Logger 객체를 가져와야합니다.
// 네임스페이스 선언
using Castle.Core.Logging;
public class SampleAppController : ISampleAppController
{
// 객체 속성 주입을 위해 선언
public ILogger Logger { get; set; }
public TaskAppController()
{
// 기본값 선언
Logger = NullLogger.Instance;
}
public void CreateSample(CreateSampleDto input)
{
// 로그 저장
Logger.Info("샘플 코드 저장 description : " + input.Description);
//TODO: 기타 작업 코드 작성
}
}
위 코드가 실행이 되고 로그 파일을 확인하면 아래와 같이 확인이 가능합니다.
08:09:35,556 [INF] 샘플 코드 저장 description : Description!
{"SourceContext": "SampleAppController", "CorrelationId": "9dade8b9-de5a-4e70-a79c-788148daa86a", "ThreadId": 23}
08:09:39,015 [ERR] 에러 로깅 테스트
{"SourceContext": "SampleAppController", "CorrelationId": "9dade8b9-de5a-4e70-a79c-788148daa86a", "ThreadId": 21, "ExceptionDetail": {"Type": "System.ApplicationException", "HResult": -2146232832, "Message": "에러 로깅 테스트", "Source": "MyCompanyName.MyProjectName.Web.Mvc", "TargetSite": "Void MoveNext()"}}
System.ApplicationException: 에러 로깅 테스트
at MyCompanyName.MyProjectName.Web.Areas.App.Pages.Users.IndexModel.OnGetAsync() in ...\Index.cshtml.cs:line 29
at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory.NonGenericTaskHandlerMethod.Execute(Object receiver, Object[] arguments)
at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeHandlerMethodAsync()
at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeNextPageFilterAsync()
로그 관련 환경 설정
기본 구성의 로그 형식은 다음과 같습니다.
날짜 및 시간 : 로그 라인이 작성된 시간입니다.
로그 수준 : DBG, INF, WRN, ERR, FTL.
로그 텍스트 : 작성하는 실제 로그 텍스트입니다.
Properties : 출력의 다른 곳에 표시되지 않는 모든 이벤트 속성 값입니다.
로거 이름 : 일반적으로 로그 라인을 작성하는 클래스 이름입니다.
상관관계 ID : 현재 웹 요청에 대한 로그를 추적하기 위해 Correlation Id 추가합니다.
스레드 번호 : 로그 라인을 작성한 스레드 번호입니다.
예외 상세 : 예외가 발생할경우 구조화된 예외정보 노출
예외 : 예외가 발생할경우 메세지 원문
아래와 같이 응용 프로그램의 Program.cs와 appsettings.json 파일에 정의되어 있습니다.
Program.cs
로 기본DEBUG
시 Console 로깅이 동작하면appsettings.json
에 정의를 통해서Override
된 설정으로 최종 구성된다.
public class Program
{
public static void Main(string[] args) {
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Warning()
.MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information)
.Enrich.FromLogContext()
.Enrich.WithCorrelationIdHeader()
.Enrich.WithAxsThreadId()
#if DEBUG
.Enrich.WithExceptionDetails()
.WriteTo.Async(config => config
.Console(outputTemplate: "{Timestamp:HH:mm:ss,fff} [{Level:u3}] {Message:lj}{NewLine}{Properties:j}{NewLine}{Exception}",
theme: Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme.Code)
)
#endif
.ReadFrom.Configuration(configuration)
.CreateLogger();
// some code...
}
}
{
"Serilog": {
"MinimumLevel": {
"Default": "Warning",
"Override": {
"Microsoft.Hosting.Lifetime": "Information"
}
},
"Properties": {
"Application": "Axs WebApp"
},
"WriteTo": [
{
"Name": "Async",
"Args": {
"configure": [
{
"Name": "File",
"Args": {
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Properties:j}{NewLine}{Exception}",
"path": "Logs\\App-.log", // 파일명 - App-yyyyMMdd.log
"fileSizeLimitBytes": 10240000, // 파일 사이즈
"rollingInterval": "Day", // 롤링 기준 - 하루 단위로 구별
"rollOnFileSizeLimit": true, // 파일 사이즈 제한 여부
"retainedFileCountLimit": 5, // 파일 갯수
"buffered": false,
"shared": true,
"encoding": "System.Text.Encoding::UTF8"
}
}
]
}
}
]
}
}
마지막으로 Startup.cs 파일에서 appsettings.json 파일과 함께 Serilog의 환경 설정 정보를 사용하도록 합니다.
services.AddAxs<MyProjectNameWebMvcModule>(options => {
options.IocManager.IocContainer.AddFacility<LoggingFacility>(
f => f.UseAxSerilog()
);
웹과 호스트 프로젝트에서 초기화 코드를 추가하면 됩니다.
확인
appsettings.json에서 serilog 세셕에서 아래 정보를 확인하고 폴더에서 해당 로그 파일을 확인하면 된다.
// 파일로그 옵션
{
"Name": "File",
"Args": {
"path": "Logs\\App.log"
}
}
로그 파일을 열어 보면 아래와 같은 패턴으로 저장이 되는 것을 확인 할 수 있습니다.
2023-09-19 08:09:19.192 [INF] Starting web server...
{"ThreadId":1}
2023-09-19 08:09:20.883 [INF] Web Server Booting Up...
{"SourceContext":"Default","ThreadId":1}
2023-09-19 08:07:20.563 [INF] Initialize Seed Db.
{"SourceContext":"MyCompanyName.MyProjectName.Persistence.MyProjectNamePersistenceModule","ThreadId":1}
2023-09-19 08:07:20.566 [INF] >> Configuration.Modules.AxsEfCore().AddDbContext ....
{"SourceContext":"MyCompanyName.MyProjectName.Persistence.MyProjectNamePersistenceModule","ThreadId":1}
2023-09-19 08:07:20.972 [INF] Executed DbCommand (3ms) [Parameters=[@__language_Name_1='ko' (Size = 128)], CommandType='Text', CommandTimeout='30']
SELECT CASE
WHEN EXISTS (
SELECT 1
FROM [AxsLanguages] AS [a]
WHERE [a].[TenantId] IS NULL AND ([a].[Name] = @__language_Name_1)) THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END
{"SourceContext":"Microsoft.EntityFrameworkCore.Database.Command","ThreadId":1}
2023-09-19 08:09:35.834 [DBG] Debug!!!!!
{"SourceContext":"MyCompanyName.MyProjectName.Web.Areas.App.Pages.Users.IndexModel","CorrelationId":"87449cf6-c4bf-40d4-9390-116ea4cd35fe","ThreadId":21}
2023-09-19 08:09:35.834 [INF] Info!!!!!
{"SourceContext":"MyCompanyName.MyProjectName.Web.Areas.App.Pages.Users.IndexModel","CorrelationId":"87449cf6-c4bf-40d4-9390-116ea4cd35fe","ThreadId":21}
2023-09-19 08:09:35.834 [WRN] Warn!!!!!
{"SourceContext":"MyCompanyName.MyProjectName.Web.Areas.App.Pages.Users.IndexModel","CorrelationId":"87449cf6-c4bf-40d4-9390-116ea4cd35fe","ThreadId":21}
2023-09-19 08:09:39.015 [ERR] 에러 로깅 테스트
{"SourceContext":"Axs.AspNetCore.Mvc.ExceptionHandling.AxsExceptionPageFilter","CorrelationId":"87449cf6-c4bf-40d4-9390-116ea4cd35fe","ThreadId":21,"ExceptionDetail":{"Type":"System.ApplicationException","HResult":-2146232832,"Message":"에러 로깅 테스트","Source":"MyCompanyName.MyProjectName.Web.Mvc","TargetSite":"Void MoveNext()"}}
System.ApplicationException: 에러 로깅 테스트
at MyCompanyName.MyProjectName.Web.Areas.App.Pages.Users.IndexModel.OnGetAsync() in ...\Index.cshtml.cs:line 29
at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory.NonGenericTaskHandlerMethod.Execute(Object receiver, Object[] arguments)
at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeHandlerMethodAsync()
at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeNextPageFilterAsync()
웹 요청에 대한 로그를 추적하기
CorrelationId: 297e08af-3194-4700-973c-b93a3d030a92
API 호출시 관계된 로그를 분석할수 있습니다.
2023-09-19 08:44:54.018 [INF] GetLanguages 언어조회
{"SourceContext": "SampleAppController", "CorrelationId": "297e08af-3194-4700-973c-b93a3d030a92", "ThreadId": 23}
2023-09-19 08:44:54.018 [INF] >> Configuration.Modules.AxsEfCore().AddDbContext ....
{"SourceContext":"MyProjectNamePersistenceModule","CorrelationId":"297e08af-3194-4700-973c-b93a3d030a92","ThreadId":34}
2023-09-19 08:44:54.025 [INF] Executed DbCommand (2ms) [Parameters=[@p0='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.31' (Size = 512), @p1='::1' (Size = 64), @p2=NULL (Size = 128), @p3=NULL (Size = 2000), @p4=NULL (Size = 2000), @p5='28', @p6='2023-09-19T08:44:53', @p7=NULL (DbType = Int32), @p8=NULL (DbType = Int64), @p9='GetLanguages' (Size = 256), @p10='{}' (Size = 1024), @p11=NULL (Size = 4000), @p12='Axs.Starter.Localization.LanguageAppService' (Size = 256), @p13=NULL (DbType = Int32), @p14='1' (Nullable = true)], CommandType='Text', CommandTimeout='30']
SELECT *
FROM [AxsLanguages]
{"SourceContext":"Microsoft.EntityFrameworkCore.Database.Command","CorrelationId":"297e08af-3194-4700-973c-b93a3d030a92","ThreadId":31}
Last updated