[Spring] MDC ๋ก๊น & Logback ์ค์
๋ฉํฐ์ฐ๋ ๋ ํ์ฉ ์ ๋ก๊ทธ ํ์
์ด์ ์๋น์ค์์ ์ฌ๋ฌ ์์ฒญ์ด ๋์์ ๋ค์ด์ฌ ๋, ๋ก๊ทธ๊ฐ ์์ ์์ด ์ฒ๋ฆฌ๋๊ณ ์์ฌ ์์ด๋ ๊ฒฝ์ฐ๊ฐ ๋ฐ์ํฉ๋๋ค. ์ด๋ก ์ธํด ๋ก๊ทธ ์ถ์ ์ด ์ด๋ ค์์ง๊ณ , ๋ฌธ์ ๋ฅผ ๋๋ฒ๊น ํ๋ ๋ฐ ๋ถํธํจ์ ์ด๋ํ ์ ์์ต๋๋ค.
- ๋ก๊ทธ ์์: ์์ฒญ์ ์์ฐจ์ ์ผ๋ก ๋ค์ด์ค์ง๋ง, ์ฒ๋ฆฌ ๋์ค์๋ ๋ก๊ทธ๊ฐ ์์ฌ์ ์์ด๊ฒ ๋ฉ๋๋ค.
- ์ฐ๋ ๋ ์ ๋ณด: ๋ก๊ทธ๋ ์๊ฐ ์์ ์ฐ๋ ๋ ์ ๋ณด๋ฅผ ํ์ํ์ง๋ง, ๊ฐ์ ์ฐ๋ ๋๊ฐ ์ฌ๋ฌ ๋ฒ ์ฌ์ฌ์ฉ๋๊ธฐ ๋๋ฌธ์ ํน์ ์์ฒญ์ ํ๋ฆ์ ์ถ์ ํ๊ธฐ ์ด๋ ต์ต๋๋ค.
ํด๊ฒฐ ๋ฐฉ์
์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด MDC๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ ์์ฒญ์ ๊ณ ์ ๊ฐ์ ํ ๋นํ๊ณ , ์ด๋ฅผ ๋ก๊ทธ์ ์ถ๋ ฅํจ์ผ๋ก์จ ๋ก๊ทธ๋ฅผ ๊ตฌ์ฒด์ ์ผ๋ก ์ถ์ ํ ์ ์์ต๋๋ค.
Interceptor, Filter, AOP ๋ฑ ๋ค์ํ ๋ฐฉ๋ฒ์ผ๋ก MDC๋ฅผ ์ ์ฉํ ์ ์์ง๋ง, ๋ณธ ์์ ์์๋ AOP๋ฅผ ์ฌ์ฉํ์ฌ MDC๋ฅผ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ์ค๋ช ํฉ๋๋ค.
MDC (Mapped Diagnostic Context)
MDC๋ Key-Value ๊ธฐ๋ฐ์ ์ ์ฅ์๋ฅผ ์ ๊ณตํ์ฌ, ํด๋ผ์ด์ธํธ๋ณ ํน์ง์ ์ธ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ ์ ์๋ ๋ก๊น ๋ฉ์ปค๋์ฆ์ ๋๋ค. ๊ฐ ์ฐ๋ ๋๋ณ๋ก ๋ ๋ฆฝ์ ์ธ ๋งฅ๋ฝ(Context) ์ ๋ณด๋ฅผ ์ ์งํ์ฌ, ํน์ ์์ฒญ์ด๋ ์ฌ์ฉ์์ ๊ด๋ จ๋ ๋ก๊ทธ๋ฅผ ์ฝ๊ฒ ํํฐ๋งํ๊ณ ์ถ๋ ฅํ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด ๋๋ฒ๊น ๊ณผ ๋ถ์์ ์ฉ์ดํ๊ฒ ํ ์ ์์ต๋๋ค.
- ๋ก๊น ํ๋ ์์ํฌ: SLF4J, Logback, Log4j2 ๋ฑ์์ ์ ๊ณต
- ThreadContext์ ์์กดํ์ฌ, ์ฐ๋ ๋๋ง๋ค ๋ ๋ฆฝ์ ์ธ ์ ๋ณด๋ฅผ ์ ์ฅ
- ์ถ์ ์ฑ ํฅ์: HTTP ์์ฒญ ID, ์ฌ์ฉ์ ID ๋ฑ ํน์ ์ ๋ณด๋ฅผ ๋ก๊ทธ์ ํจ๊ป ์ถ๋ ฅ
MDC์ ํ์ฉ
MDC๋ ํน์ ์์ฒญ์ด๋ ํธ๋์ญ์ ์ ๋ํ ์ ๋ณด(์: ์ฌ์ฉ์ ID, ์ธ์ ID, ์์ฒญ ID ๋ฑ)๋ฅผ ๋ก๊ทธ์ ํฌํจ์ํค๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. ์ฌ๋ฌ ์ฐ๋ ๋์์ ์คํ๋๋ ๋ก๊ทธ๋ฅผ ์ถ์ ํ ๋ ๊ฐ ์ฐ๋ ๋๋ง๋ค ๋ณ๋๋ก ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ ์ ์๊ธฐ ๋๋ฌธ์, ๋ฉํฐ ์ฐ๋ ๋ฉ ํ๊ฒฝ์์ ์ ์ฉํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ์น ์ ํ๋ฆฌ์ผ์ด์ ์์๋ ๊ฐ HTTP ์์ฒญ์ ๋ํด ๊ณ ์ ํ requestId๋ฅผ ์์ฑํ๊ณ , ์ด๋ฅผ MDC์ ์ ์ฅํ์ฌ ๋ชจ๋ ๋ก๊ทธ ๋ฉ์์ง์ requestId๋ฅผ ํจ๊ป ์ถ๋ ฅํ ์ ์์ต๋๋ค.
MDC์ ์ฅ์
- ๊ฐ ์ฐ๋ ๋์ ๋ํ ๊ณ ์ ์ ๋ณด ์ถ์ : ์ฌ๋ฌ ์ฐ๋ ๋์์ ์คํ๋๋ ์์ฒญ์ ์ถ์ ํ ์ ์์ด ๋ฉํฐ ์ฐ๋ ๋ฉ ํ๊ฒฝ์์ ์ ์ฉํฉ๋๋ค.
- ๋๋ฒ๊น ๋ฐ ๋ฌธ์ ๋ถ์ ์ฉ์ด: ์ถ๊ฐ์ ์ธ ์ ๋ณด๋ฅผ ๋ก๊ทธ์ ํฌํจ์์ผ ๋ฌธ์ ๋ฅผ ๋น ๋ฅด๊ฒ ์ถ์ ํ ์ ์์ต๋๋ค.
- ์ ์ฐํ ์ค์ : ํน์ ๋ก๊น ์ ๋ณด๋ฅผ ์์ฝ๊ฒ ์ถ๊ฐํ๊ณ , ํ๊ฒฝ์ ๋ฐ๋ผ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.
MDC ์ฃผ์ ๋ฉ์๋
MDC ํด๋์ค๋ ๋ด๋ถ์ Map์์ key์ value๋ฅผ ์กฐ์ํ๋ ๋ค์ํ ๋ฉ์๋๋ฅผ ์ ๊ณตํฉ๋๋ค.
- get(String key): ์ฃผ์ด์ง key์ ์ฐ๊ด๋ value(๋ฐ์ดํฐ)๋ฅผ ๋ฐํ
- put(String key, Object value): ์ฃผ์ด์ง key์ ์ฐ๊ด๋ value(๋ฐ์ดํฐ)๋ฅผ ์ ์ฅ
- remove(String key): ์ฃผ์ด์ง key์ ์ฐ๊ด๋ value(๋ฐ์ดํฐ)๋ฅผ ์ญ์
- clear(): MDC์ ์ ์ฅ๋ ๋ชจ๋ ํญ๋ชฉ์ ์ญ์
๊ธฐ๋ณธ๊ฐ ์ค์ (DEFAULT_MDC)
MDC ๊ฐ์ด ์์ ๋ ์ถ๋ ฅํ ๊ธฐ๋ณธ๊ฐ์ ์ค์ ํ๋ ๊ฒ์ ์ค์ํ ์ค์ ์ ๋๋ค. ๋ก๊ทธ๋ฅผ ๊ธฐ๋กํ ๋ ํน์ MDC ๊ฐ์ด ์์ผ๋ฉด, ๊ทธ๊ฒ์ด ๋๋ฝ๋ ์ํ๋ก ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๊ธฐ ๋๋ฌธ์, ์ด๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์ ์ํ ํ์๊ฐ ์์ต๋๋ค. ์ค์ ๋ ๊ธฐ๋ณธ๊ฐ์ ๋ก๊ทธ ๋ฉ์์ง์์ ํด๋น MDC ๊ฐ์ด ์์ ๊ฒฝ์ฐ ํ์๋ฉ๋๋ค.
<property name="DEFAULT_MDC" value="-"/>
DEFAULT_MDC: MDC ๊ฐ์ด ์์ ๋ ํ์๋ ๊ธฐ๋ณธ๊ฐ์ ์ ์ํ๋ ํ๋กํผํฐ์ ๋๋ค. ์๋ฅผ ๋ค์ด, request_id๋ user_id์ ๊ฐ์ ํ์ ์ ๋ณด๋ฅผ ๋ก๊น ํด์ผ ํ์ง๋ง, ๋ง์ฝ ๊ทธ ๊ฐ์ด ์์ ๊ฒฝ์ฐ์๋ -๋ฅผ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ค์ ํ์ฌ ๋ก๊ทธ๊ฐ ๋น ๊ฐ ์์ด ์ถ๋ ฅ๋๋๋ก ํฉ๋๋ค.
- ์ด ๊ธฐ๋ณธ๊ฐ์ ๋ก๊ทธ์์ ํด๋น MDC ์ ๋ณด๊ฐ ๋๋ฝ๋์์ ๋ ๋น ์๋ฆฌ๊ฐ ์๋ -์ ๊ฐ์ ์ง์ ๋ ๋ฌธ์์ด๋ก ์ฑ์์ง๊ฒ ๋ฉ๋๋ค. ์ด๋ฅผ ํตํด ๋ก๊ทธ์ ์ผ๊ด์ฑ์ ์ ์งํ ์ ์๊ณ , ๋๋ฝ๋ ์ ๋ณด๋ฅผ ์ฝ๊ฒ ์๋ณํ ์ ์์ต๋๋ค.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- ๊ธฐ๋ณธ MDC ๊ฐ ์ค์ -->
<property name="DEFAULT_MDC" value="-"/>
<!-- ๊ณตํต ๋ก๊ทธ ํจํด -->
<property name="COMMON_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%.-5level] [%X{request_id:-${DEFAULT_MDC}}] [%X{layer:-${DEFAULT_MDC}}] [%X{api:-${DEFAULT_MDC}}] [%X{error_source:-${DEFAULT_MDC}}] %logger - %msg%n"/>
<!-- Spring Property ๊ฐ ๊ฐ์ ธ์ค๊ธฐ -->
<springProperty name="LOG_GROUP_NAME" source="logging.cloudwatch.log-group"/>
<springProperty name="AWS_REGION" source="logging.cloudwatch.region"/>
<!-- AWS CloudWatch๋ก ๋ก๊ทธ ์ ์ก -->
<appender name="AWS_LOGS" class="ca.pjer.logback.AwsLogsAppender">
<layout>
<pattern>${COMMON_PATTERN}</pattern>
</layout>
<logGroupName>${LOG_GROUP_NAME}</logGroupName>
<logStreamUuidPrefix>spring-boot-app-</logStreamUuidPrefix>
<logRegion>${AWS_REGION}</logRegion>
<maxBatchLogEvents>50</maxBatchLogEvents>
<maxFlushTimeMillis>30000</maxFlushTimeMillis>
<maxBlockTimeMillis>5000</maxBlockTimeMillis>
<retentionTimeDays>7</retentionTimeDays> <!-- ๋ก๊ทธ ๋ณด๊ด ๊ธฐ๊ฐ -->
</appender>
<!-- Console ๋ก๊ทธ ์ถ๋ ฅ -->
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${COMMON_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<!-- ๋น๋๊ธฐ ์ฒ๋ฆฌ -->
<appender name="ASYNC_AWS_LOGS" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="AWS_LOGS"/>
</appender>
<!-- ๋ก์ปฌ ํ๊ฒฝ: Console๋ง ์ฌ์ฉ -->
<springProfile name="local">
<root level="INFO">
<appender-ref ref="Console"/>
</root>
</springProfile>
<!-- ๊ฐ๋ฐ ๋ฐ ํ๋ก๋์
ํ๊ฒฝ: AWS CloudWatch + Console -->
<springProfile name="dev,prod">
<root level="ERROR">
<appender-ref ref="ASYNC_AWS_LOGS"/>
<appender-ref ref="Console"/>
</root>
</springProfile>
</configuration>
- AWS CloudWatch๋ก ๋ก๊ทธ ์ ์ก (AWS_LOGS)
- logGroupName : CloudWatch ๋ก๊ทธ ๊ทธ๋ฃน ์ด๋ฆ
- logRegion : AWS ๋ฆฌ์
- retentionTimeDays=7 : 7์ผ๊ฐ ๋ก๊ทธ ๋ณด๊ด
- Console ๋ก๊ทธ ์ถ๋ ฅ
- ๋ชจ๋ ํ๊ฒฝ์์ Console์๋ ๋ก๊ทธ๋ฅผ ์ถ๋ ฅ
- Discord ์๋ฆผ (DISCORD)
- ERROR ๋ ๋ฒจ ๋ก๊ทธ๋ Discord Webhook์ ํตํด ์๋ฆผ ์ ์ก
- ๋น๋๊ธฐ ์ฒ๋ฆฌ
- CloudWatch (ASYNC_AWS_LOGS)์ Discord (ASYNC_DISCORD) ๋ก๊ทธ ์ ์ก์ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ์ฌ ์ฑ๋ฅ ์ต์ ํ
- Spring Profile ์ ์ฉ
- local โ Console๋ง ์ฌ์ฉ
- dev, prod โ CloudWatch + Console
- ERROR ๋ก๊ทธ โ Discord๋ก ์ ์ก
[์ฐธ๊ณ ]
https://dev-jwblog.tistory.com/126
[Java] MDC ๋ฅผ ์ฌ์ฉํ ๋ก๊ทธ(Log)์ถ์ ํ๊ธฐ
ํ์ฌ์์ ์์ค๋ฅผ ๋ถ์ํ๋ฉด์ ๋ก๊ทธ๋ฅผ ๊ธฐ๋กํ๋ ๊ฒ์ MDC ๋ผ๋ ๊ฒ์ ์ฌ์ฉํ๊ณ ์์๋ค. (์๋ก์ด ํ์ฌ์์ ์์ค ๋ถ์ํ๋ฉด์ ๋ชจ๋ ๊ฒ ์๋กญ๋ค... ๊ธฐ์กด์ ๋ด๊ฐ ์ฌ์ฉํ๋ ๊ฒ๋ค์ ์ด๋์...) ๊ฐ๋จํ๊ฒ ๊ตฌ๊ธ
dev-jwblog.tistory.com
Logback + MDC๋ฅผ ์ด์ฉํ request ๋ก๊น
MDC๋ฅผ ์ด์ฉํ ๋ก๊น MDC Mapped Diagnostic Context Logging ์ ๋จ๊ธฐ๊ณ , ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ฌ Logging์ ์ถ์ ํ ๋ ๋ฉํฐ์ฐ๋ ๋ ํ๊ฒฝ์์ ๋ก๊ทธ๊ฐ ๋ค์์ฌ ์์ฒญ๋ณ๋ก ํ๋ฆ์ ํ์ ํ๊ธฐ๊ฐ ํ๋ญ๋๋ค. ์ด๋ ์์ฒญ(Request)๋ณ๋ก
0soo.tistory.com