๐ŸคRyusun๐Ÿค 2025. 3. 28. 16:23

๋ฉ€ํ‹ฐ์“ฐ๋ ˆ๋“œ ํ™œ์šฉ ์‹œ ๋กœ๊ทธ ํ˜„์ƒ

์šด์˜ ์„œ๋น„์Šค์—์„œ ์—ฌ๋Ÿฌ ์š”์ฒญ์ด ๋™์‹œ์— ๋“ค์–ด์˜ฌ ๋•Œ, ๋กœ๊ทธ๊ฐ€ ์ˆœ์„œ ์—†์ด ์ฒ˜๋ฆฌ๋˜๊ณ  ์„ž์—ฌ ์Œ“์ด๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด ๋กœ๊ทธ ์ถ”์ ์ด ์–ด๋ ค์›Œ์ง€๊ณ , ๋ฌธ์ œ๋ฅผ ๋””๋ฒ„๊น…ํ•˜๋Š” ๋ฐ ๋ถˆํŽธํ•จ์„ ์ดˆ๋ž˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋กœ๊ทธ ์ˆœ์„œ: ์š”์ฒญ์€ ์ˆœ์ฐจ์ ์œผ๋กœ ๋“ค์–ด์˜ค์ง€๋งŒ, ์ฒ˜๋ฆฌ ๋„์ค‘์—๋Š” ๋กœ๊ทธ๊ฐ€ ์„ž์—ฌ์„œ ์Œ“์ด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  • ์“ฐ๋ ˆ๋“œ ์ •๋ณด: ๋กœ๊ทธ๋Š” ์‹œ๊ฐ„ ์˜†์— ์“ฐ๋ ˆ๋“œ ์ •๋ณด๋ฅผ ํ‘œ์‹œํ•˜์ง€๋งŒ, ๊ฐ™์€ ์“ฐ๋ ˆ๋“œ๊ฐ€ ์—ฌ๋Ÿฌ ๋ฒˆ ์žฌ์‚ฌ์šฉ๋˜๊ธฐ ๋•Œ๋ฌธ์— ํŠน์ • ์š”์ฒญ์˜ ํ๋ฆ„์„ ์ถ”์ ํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.

 

ํ•ด๊ฒฐ ๋ฐฉ์•ˆ

์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด 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์˜ ์žฅ์ 

  1. ๊ฐ ์“ฐ๋ ˆ๋“œ์— ๋Œ€ํ•œ ๊ณ ์œ  ์ •๋ณด ์ถ”์ : ์—ฌ๋Ÿฌ ์“ฐ๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋˜๋Š” ์š”์ฒญ์„ ์ถ”์ ํ•  ์ˆ˜ ์žˆ์–ด ๋ฉ€ํ‹ฐ ์“ฐ๋ ˆ๋”ฉ ํ™˜๊ฒฝ์—์„œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  2. ๋””๋ฒ„๊น… ๋ฐ ๋ฌธ์ œ ๋ถ„์„ ์šฉ์ด: ์ถ”๊ฐ€์ ์ธ ์ •๋ณด๋ฅผ ๋กœ๊ทธ์— ํฌํ•จ์‹œ์ผœ ๋ฌธ์ œ๋ฅผ ๋น ๋ฅด๊ฒŒ ์ถ”์ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  3. ์œ ์—ฐํ•œ ์„ค์ •: ํŠน์ • ๋กœ๊น… ์ •๋ณด๋ฅผ ์†์‰ฝ๊ฒŒ ์ถ”๊ฐ€ํ•˜๊ณ , ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

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>
  1. AWS CloudWatch๋กœ ๋กœ๊ทธ ์ „์†ก (AWS_LOGS)
    • logGroupName : CloudWatch ๋กœ๊ทธ ๊ทธ๋ฃน ์ด๋ฆ„
    • logRegion : AWS ๋ฆฌ์ „
    • retentionTimeDays=7 : 7์ผ๊ฐ„ ๋กœ๊ทธ ๋ณด๊ด€
  2. Console ๋กœ๊ทธ ์ถœ๋ ฅ
    • ๋ชจ๋“  ํ™˜๊ฒฝ์—์„œ Console์—๋„ ๋กœ๊ทธ๋ฅผ ์ถœ๋ ฅ
  3. Discord ์•Œ๋ฆผ (DISCORD)
    • ERROR ๋ ˆ๋ฒจ ๋กœ๊ทธ๋Š” Discord Webhook์„ ํ†ตํ•ด ์•Œ๋ฆผ ์ „์†ก
  4. ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ
    • CloudWatch (ASYNC_AWS_LOGS)์™€ Discord (ASYNC_DISCORD) ๋กœ๊ทธ ์ „์†ก์„ ๋น„๋™๊ธฐ๋กœ ์ฒ˜๋ฆฌํ•˜์—ฌ ์„ฑ๋Šฅ ์ตœ์ ํ™”
  5. Spring Profile ์ ์šฉ
    • local โ†’ Console๋งŒ ์‚ฌ์šฉ
    • dev, prod โ†’ CloudWatch + Console
    • ERROR ๋กœ๊ทธ โ†’ Discord๋กœ ์ „์†ก

 

[์ฐธ๊ณ ]

https://dev-jwblog.tistory.com/126

 

[Java] MDC ๋ฅผ ์‚ฌ์šฉํ•œ ๋กœ๊ทธ(Log)์ถ”์ ํ•˜๊ธฐ

ํšŒ์‚ฌ์—์„œ ์†Œ์Šค๋ฅผ ๋ถ„์„ํ•˜๋ฉด์„œ ๋กœ๊ทธ๋ฅผ ๊ธฐ๋กํ•˜๋Š” ๊ฒƒ์— MDC ๋ผ๋Š” ๊ฒƒ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์—ˆ๋‹ค. (์ƒˆ๋กœ์šด ํšŒ์‚ฌ์—์„œ ์†Œ์Šค ๋ถ„์„ํ•˜๋ฉด์„œ ๋ชจ๋“ ๊ฒŒ ์ƒˆ๋กญ๋‹ค... ๊ธฐ์กด์— ๋‚ด๊ฐ€ ์‚ฌ์šฉํ•˜๋˜ ๊ฒƒ๋“ค์€ ์–ด๋””์—...) ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌ๊ธ€

dev-jwblog.tistory.com

 

https://0soo.tistory.com/246

 

Logback + MDC๋ฅผ ์ด์šฉํ•œ request ๋กœ๊น…

MDC๋ฅผ ์ด์šฉํ•œ ๋กœ๊น… MDC Mapped Diagnostic Context Logging ์„ ๋‚จ๊ธฐ๊ณ , ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ Logging์„ ์ถ”์ ํ•  ๋•Œ ๋ฉ€ํ‹ฐ์“ฐ๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ๋กœ๊ทธ๊ฐ€ ๋’ค์„ž์—ฌ ์š”์ฒญ๋ณ„๋กœ ํ๋ฆ„์„ ํŒŒ์•…ํ•˜๊ธฐ๊ฐ€ ํž˜๋“ญ๋‹ˆ๋‹ค. ์ด๋•Œ ์š”์ฒญ(Request)๋ณ„๋กœ

0soo.tistory.com