닷넷에서 로그는 “카테고리”, “레벨”, "메시지"로 구성됩니다.
- 카테고리: string 형식으로 비정형적인 문자열인데, 보통 호출코드가 속한 클래스의 Full name을 사용합니다.
- 레벨: 보통 Information, Warning, Debug 등으로 사전에 지정합니다.
- 메시지: 역시 string 형식으로 비정형적입니다.
아래는 Asp.Net Core 웹이 콘솔에 남긴 로그의 일부입니다.
info: Microsoft.Hosting.Lifetime[0]
Content root path: C:\C#\Biz\UI.Web
warn: Microsoft.AspNetCore.Components.Server.Circuits.RemoteRenderer[100]
Unhandled exception rendering component: InvalidCharacterError: Failed to execute 'setAttribute' on 'Element': 'col-auto""' is not a valid attribute name.
System.InvalidOperationException: InvalidCharacterError: Failed to execute 'setAttribute' on 'Element': 'col-auto""' is not a valid attribute name.
at Microsoft.AspNetCore.Components.RenderTree.Renderer.InvokeRenderCompletedCallsAfterUpdateDisplayTask(Task updateDisplayTask, Int32[] updatedComponents)
fail: Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost[111]
Unhandled exception in circuit 'DF8JgtUw_z6wObgr6rnMgCckfYyZzXziXX_64q4VnPY'.
System.AggregateException: One or more errors occurred. (InvalidCharacterError: Failed to execute 'setAttribute' on 'Element': 'col-auto""' is not a valid attribute name.)
---> System.InvalidOperationException: InvalidCharacterError: Failed to execute 'setAttribute' on 'Element': 'col-auto""' is not a valid attribute name.
at Microsoft.AspNetCore.Components.RenderTree.Renderer.InvokeRenderCompletedCallsAfterUpdateDisplayTask(Task updateDisplayTask, Int32[] updatedComponents)
--- End of inner exception stack trace ---
위 로그는 닷넷의 기본 로그 제공자 중에 하나인 콘솔 로그 제공자가 남긴 것으로, 콘솔 로그 제공자는 로그를 화면에 출력하도록 설정되어 있습니다. 그 형식은
{레벨항목}:{빈칸}{카테고리}{/n}
{탭}{메시지}{/n}
info: Microsoft.Hosting.Lifetime[0]
Content root path: C:\C#\Biz\UI.Web
만약 파일로 저장하도록 설정되어 있다면, 특정 파일에 아래와 같이 남길 것입니다.
info: Microsoft.Hosting.Lifetime[0]\n Content root path: C:\C#\Biz\UI.Web\nwarn: ...
로그 파서의 역할은 고객사의 로그에서 레벨, 카테고리, 메시지를 구별하는 것고, 커스터마이징이라고 하면, "로그 레벨"을 사전에 정의하는 것을 의미할 것 같습니다.
레벨 정보는 그때 그때 고객사에 물어 보고, 이를 파서 앱의 설정 파일(.json)에 저장하면 될 것 같습니다. 예를 들면,
//logstructure.json for a customer
{
"levels" : ["warning", "information", ... ]
}
만약, 각 레벨에 대한 Regex 를 사전에 정의해 놓으면 파서의 코드는 더욱 정형적이 될 것입니다.
//levelregex.json
{
"Information" : "^info:.*$",
...
}
파서가 고객사 로그 파싱을 완료하면, 이를 다시 닷넷의 로깅 도구를 사용하여 로깅합니다. 이때, 커스텀 로깅 제공자를 정의하여, 회사의 로그 저장소로 바로 저장하도록 만들면 될 것 같습니다.
Implement a custom logging provider - .NET | Microsoft Learn
콘솔 로그 제공자는 로그를 화면에 뿌려준다.
=> 커스텀 로그 제공자는 로그를 회사의 저장소에 저장한다.
이러한 구조로 파서를 정의해 두면, 나중에 할 일은 각 회사 마다 .json 파일을 정의하는 거 빼곤 할 일이 없습니다. 모든 케이스를 전부 커버할 수는 없겠지만, 대부분의 케이스에는 적용될 것 같네요.