Skip to content
Go back

[Spring๐ŸŒฑ] ๋กœ๊น…(Logging) ์™„๋ฒฝ ๊ฐ€์ด๋“œ ๐Ÿ“

Edit page

์Šคํ”„๋ง ๋ถ€ํŠธ ๋กœ๊น…(Spring Boot Logging) ์™„๋ฒฝ ๊ฐ€์ด๋“œ ๐Ÿ“

์Šคํ”„๋ง ๋ถ€ํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋กœ๊น…(Logging) ๊ธฐ๋Šฅ์„ ํ†ตํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋™์ž‘์„ ์ถ”์ ํ•˜๊ณ  ๋ฌธ์ œ๋ฅผ ์ง„๋‹จํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” Spring Boot ๋กœ๊น…์˜ ๊ธฐ๋ณธ ๋™์ž‘ ๋ฐฉ์‹๊ณผ ์ฃผ์š” ์„ค์ • ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณด๊ณ , ์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ์—์„œ ๋กœ๊น…์„ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•˜๋Š”์ง€์— ๋Œ€ํ•ด ์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ์ž์„ธํžˆ ์•Œ์•„๋ณธ๋‹ค.

์Šคํ”„๋ง ๋ถ€ํŠธ ๋กœ๊น…์˜ ๊ธฐ๋ณธ ์›๋ฆฌ

์Šคํ”„๋ง ๋ถ€ํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๊ธฐ๋ณธ์ ์œผ๋กœ Spring Framework์—์„œ ์ œ๊ณตํ•˜๋Š” commons-logging ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ํ•˜์ง€๋งŒ ์‹ค์ œ ๋กœ๊น… ๊ตฌํ˜„์ฒด๋กœ๋Š” Logback์ด ๊ธฐ๋ณธ ์ฑ„ํƒ๋˜์–ด ์žˆ๋‹ค.

์ฆ‰, ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” commons-logging์œผ๋กœ๋ถ€ํ„ฐ ๋กœ๊น… ํ˜ธ์ถœ์„ ๋ฐ›์œผ๋ฉด, ๋‚ด๋ถ€์ ์œผ๋กœ Logback์„ ํ†ตํ•ด ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค.

์žฅ์ 

  1. ์ผ๊ด€๋œ ๋กœ๊น… API: commons-logging์„ ํ†ตํ•ด SLF4J, Log4j2, Logback ๋“ฑ ๋‹ค์–‘ํ•œ ๊ตฌํ˜„์ฒด๋กœ ์‰ฝ๊ฒŒ ๊ต์ฒด ๊ฐ€๋Šฅ
  2. ๊ฐ„๋‹จํ•œ ์„ค์ •: application.properties(๋˜๋Š” .yml) ํŒŒ์ผ์„ ํ†ตํ•ด ์†์‰ฝ๊ฒŒ ๋กœ๊น… ๋ ˆ๋ฒจ ๋“ฑ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Œ

๋กœ๊น… ํ”„๋ ˆ์ž„์›Œํฌ ๊ตฌ์กฐ ์ดํ•ด

์•„๋ž˜ ๊ทธ๋ฆผ์€ ํ”ํžˆ ๋ณผ ์ˆ˜ ์žˆ๋Š” Java ๋กœ๊น… ๊ตฌ์กฐ์ด๋‹ค:

[Logger ํ˜ธ์ถœ๋ถ€] -> [commons-logging or SLF4J] -> [์‹ค์ œ ๋กœ๊น… ๊ตฌํ˜„์ฒด(Logback, Log4j2...)]

์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‹ค์Œ ํ๋ฆ„์„ ๋”ฐ๋ฅธ๋‹ค:

[์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ] -> [Commons Logging] -> [Logback]

์›ํ•œ๋‹ค๋ฉด Log4j2๋กœ ๊ต์ฒดํ•  ์ˆ˜๋„ ์žˆ๋‹ค. SLF4J๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ๋น„์Šทํ•˜๋‹ค.


์Šคํ”„๋ง ๋ถ€ํŠธ ๊ธฐ๋ณธ ๋กœ๊น… ์„ค์ •

์Šคํ”„๋ง ๋ถ€ํŠธ์˜ ๊ธฐ๋ณธ ๋กœ๊น…์€ Logback์„ ์‚ฌ์šฉํ•˜๋ฉฐ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํŠน์ง•์„ ๊ฐ€์ง„๋‹ค:

  1. ๊ธฐ๋ณธ ๋ ˆ๋ฒจ: INFO
  2. ๋กœ๊ทธ ์ถœ๋ ฅ ํฌ๋งท: ์‹œ๊ฐ„, ์Šค๋ ˆ๋“œ, ๋กœ๊ฑฐ ์ด๋ฆ„, ๋กœ๊ทธ ๋ ˆ๋ฒจ, ๋กœ๊ทธ ๋ฉ”์‹œ์ง€ ํ˜•ํƒœ๋กœ ์ฝ˜์†” ์ถœ๋ ฅ
  3. ์ปฌ๋Ÿฌ ์ง€์›: ์ฝ˜์†”์—์„œ ๋กœ๊ทธ ๋ ˆ๋ฒจ๋ณ„๋กœ ๊ตฌ๋ถ„๋˜๋Š” ์ปฌ๋Ÿฌ๋ฅผ ์ง€์›

์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” spring-boot-starter-logging ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ, ์ด๋Š” Logback์— ๋Œ€ํ•œ ๊ธฐ๋ณธ ์„ค์ • ํŒŒ์ผ(logback.xml)์„ ๋‚ด์žฅํ•˜๊ณ  ์žˆ๋‹ค.


๋กœ๊น… ๋ ˆ๋ฒจ(Log Levels)

์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ ์ง€์›ํ•˜๋Š” ๋Œ€ํ‘œ์ ์ธ ๋กœ๊น… ๋ ˆ๋ฒจ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค:

  1. TRACE: ๊ฐ€์žฅ ์ƒ์„ธํ•œ ๋กœ๊ทธ ๋ ˆ๋ฒจ. ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ ์ฃผ๋กœ ์‚ฌ์šฉ
  2. DEBUG: ๋””๋ฒ„๊น…์„ ์œ„ํ•œ ์ •๋ณด
  3. INFO: ์ผ๋ฐ˜ ์ •๋ณด์„ฑ ๋ฉ”์‹œ์ง€
  4. WARN: ์ž ์žฌ์ ์ธ ๋ฌธ์ œ
  5. ERROR: ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ๋ฉ”์‹œ์ง€
  6. FATAL (Log4j2 ๋“ฑ ์ผ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์‚ฌ์šฉ): ์น˜๋ช…์ ์ธ ์˜ค๋ฅ˜

๊ธฐ๋ณธ๊ฐ’์€ INFO์ด๋ฉฐ, ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋Š” DEBUG๋‚˜ TRACE๋กœ ์„ธ๋ถ€ ์ •๋ณด ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.


์„ค์ • ํŒŒ์ผ(application.properties) ์˜ˆ์‹œ

์•„๋ž˜๋Š” application.properties์—์„œ ๋กœ๊น… ๋ ˆ๋ฒจ์„ ์„ค์ •ํ•˜๋Š” ์˜ˆ์‹œ์ด๋‹ค:

# ์Šคํ”„๋ง ๋กœ๊น… ๋ ˆ๋ฒจ
logging.level.org.springframework=debug
logging.level.com.hippoo=trace

# ์ฝ˜์†” ์ถœ๋ ฅ ์‹œ, ์ปฌ๋Ÿฌ ํ™œ์„ฑํ™” ์—ฌ๋ถ€
spring.output.ansi.enabled=ALWAYS

# ๋กœ๊ทธ ํŒจํ„ด ๋ณ€๊ฒฝ
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n

Logback & Log4j2 ์„ค์ • ์ปค์Šคํ„ฐ๋งˆ์ด์ง•

Logback ์„ค์ •

์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ Logback ์„ค์ •์„ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•˜๋ ค๋ฉด ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ ๊ฒฝ๋กœ์— logback-spring.xml ๋˜๋Š” logback.xml ํŒŒ์ผ์„ ์ž‘์„ฑํ•œ๋‹ค.

<configuration>
    <property name="LOG_PATH" value="logs"/>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/myapp.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/myapp-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <logger name="com.hippoo" level="DEBUG" additivity="false">
        <appender-ref ref="FILE"/>
    </logger>

    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
    </root>
</configuration>

Log4j2 ์„ค์ •

Log4j2๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด **spring-boot-starter-logging**์„ ์ œ๊ฑฐํ•˜๊ณ , **spring-boot-starter-log4j2**๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  log4j2-spring.xml ํŒŒ์ผ์„ ์ž‘์„ฑํ•˜์—ฌ ์„ค์ •ํ•˜๋ฉด ๋œ๋‹ค.

<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - %msg%n"/>
        </Console>
        <File name="File" fileName="logs/myapp.log">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - %msg%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy />
                <SizeBasedTriggeringPolicy size="10MB"/>
            </Policies>
            <DefaultRolloverStrategy max="30"/>
        </File>
    </Appenders>

    <Loggers>
        <Logger name="com.hippoo" level="debug" additivity="false">
            <AppenderRef ref="File"/>
        </Logger>
        <Root level="info">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="File"/>
        </Root>
    </Loggers>
</Configuration>

SLF4J์—์„œ Logging ํŒŒ์ผ ์ด๋ฆ„๊ณผ ๊ฒฝ๋กœ ์„ค์ •ํ•˜๊ธฐ ๐Ÿ“

**SLF4J (Simple Logging Facade for Java)**๋Š” ์ž๋ฐ” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋‹ค์–‘ํ•œ ๋กœ๊น… ํ”„๋ ˆ์ž„์›Œํฌ(Logback, Log4j2 ๋“ฑ)๋ฅผ ์ถ”์ƒํ™”ํ•˜์—ฌ ์ผ๊ด€๋œ ๋กœ๊น… API๋ฅผ ์ œ๊ณตํ•˜๋Š” ํŽ˜์ด์„œ(facade)์ด๋‹ค. ์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ๋Š” SLF4J๋ฅผ ๊ธฐ๋ณธ ๋กœ๊น… API๋กœ ์‚ฌ์šฉํ•˜๋ฉฐ, ๊ธฐ๋ณธ ๊ตฌํ˜„์ฒด๋กœ Logback์„ ์ฑ„ํƒํ•˜๊ณ  ์žˆ๋‹ค. SLF4J๋ฅผ ํ†ตํ•ด ๋กœ๊น… ํŒŒ์ผ์˜ ์ด๋ฆ„๊ณผ ๊ฒฝ๋กœ๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž.

1. application.properties๋ฅผ ํ†ตํ•œ ์„ค์ •

์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ๋Š” application.properties ๋˜๋Š” application.yml ํŒŒ์ผ์„ ํ†ตํ•ด ๊ฐ„๋‹จํ•˜๊ฒŒ ๋กœ๊น… ํŒŒ์ผ์˜ ์ด๋ฆ„๊ณผ ๊ฒฝ๋กœ๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Š” ๊ฐ€์žฅ ์†์‰ฌ์šด ๋ฐฉ๋ฒ•์œผ๋กœ, ๋ณ„๋„์˜ ์„ค์ • ํŒŒ์ผ์„ ์ž‘์„ฑํ•  ํ•„์š” ์—†์ด ๊ธฐ๋ณธ์ ์ธ ๋กœ๊น… ์„ค์ •์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

# application.properties

# ๋กœ๊น… ํŒŒ์ผ ์ด๋ฆ„ ์„ค์ •
logging.file.name=logs/myapp.log

# ๋กœ๊น… ํŒŒ์ผ ๊ฒฝ๋กœ ์„ค์ •
logging.file.path=/var/log/myapp

# ๋˜๋Š” ๋กœ๊น… ํŒŒ์ผ์˜ ์ „์ฒด ๊ฒฝ๋กœ๋ฅผ ์„ค์ •ํ•  ์ˆ˜๋„ ์žˆ๋‹ค
logging.file=/var/log/myapp/myapp.log

2. Logback ์„ค์ • ํŒŒ์ผ(logback-spring.xml)์„ ํ†ตํ•œ ์„ค์ •

๋ณด๋‹ค ์„ธ๋ฐ€ํ•œ ๋กœ๊น… ์„ค์ •์ด ํ•„์š”ํ•˜๋‹ค๋ฉด, logback-spring.xml ํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜์—ฌ SLF4J์™€ Logback์„ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด ๋กœ๊ทธ ํŒŒ์ผ์˜ ํ˜•์‹, ๋กค๋ง ์ •์ฑ…, ๋กœ๊ทธ ๋ ˆ๋ฒจ ๋“ฑ์„ ์ƒ์„ธํ•˜๊ฒŒ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค.

Logback ์„ค์ • ์˜ˆ์‹œ

<!-- src/main/resources/logback-spring.xml -->

<configuration>

    <!-- ํŒŒ์ผ ๊ฒฝ๋กœ์™€ ์ด๋ฆ„์„ ํ”„๋กœํผํ‹ฐ๋กœ ์„ค์ • -->
    <property name="LOG_PATH" value="${LOG_PATH:-logs}"/>
    <property name="LOG_FILE" value="${LOG_FILE:-myapp.log}"/>

    <!-- ์ฝ˜์†” ๋กœ๊ทธ ์„ค์ • -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- ํŒŒ์ผ ๋กœ๊ทธ ์„ค์ • -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${LOG_FILE}</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_PATH}/${LOG_FILE}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- ํŠน์ • ํŒจํ‚ค์ง€์— ๋Œ€ํ•œ ๋กœ๊น… ๋ ˆ๋ฒจ ์„ค์ • -->
    <logger name="com.hippoo" level="DEBUG" additivity="false">
        <appender-ref ref="FILE"/>
    </logger>

    <!-- ๋ฃจํŠธ ๋กœ๊ฑฐ ์„ค์ • -->
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
    </root>

</configuration>

3. ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋˜๋Š” JVM ์˜ต์…˜์„ ํ†ตํ•œ ์„ค์ •

์Šคํ”„๋ง ๋ถ€ํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•  ๋•Œ, ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋‚˜ JVM ์˜ต์…˜์„ ํ†ตํ•ด ๋กœ๊น… ํŒŒ์ผ์˜ ์ด๋ฆ„๊ณผ ๊ฒฝ๋กœ๋ฅผ ์„ค์ •ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ • ์˜ˆ์‹œ

export LOG_FILE=/var/log/myapp/custom.log
export LOG_PATH=/var/log/myapp
java -jar myapp.jar

JVM ์˜ต์…˜ ์„ค์ • ์˜ˆ์‹œ

java -Dlogging.file.name=/var/log/myapp/custom.log -Dlogging.file.path=/var/log/myapp -jar myapp.jar

์ด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด, ์ฝ”๋“œ๋‚˜ ์„ค์ • ํŒŒ์ผ์„ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ ๋„ ๋กœ๊ทธ ํŒŒ์ผ์˜ ์œ„์น˜์™€ ์ด๋ฆ„์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

4. ์ฝ”๋“œ ๋‚ด์—์„œ ๋กœ๊น… ์„ค์ • ๋ณ€๊ฒฝ

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

import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.Level;

public void changeLogLevel(String loggerName, String levelStr) {
    Logger logger = (Logger) LoggerFactory.getLogger(loggerName);
    Level level = Level.toLevel(levelStr, Level.INFO);
    logger.setLevel(level);
}

์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ์—์„œ ๋กœ๊น… ํ™œ์šฉํ•˜๊ธฐ ๐Ÿ› ๏ธ

์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ์—์„œ ๋กœ๊น…์„ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ์‚ดํŽด๋ณด์ž. SLF4J์™€ Logback์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์–‘ํ•œ ๋กœ๊น… ๋ ˆ๋ฒจ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•œ๋‹ค.

1. SLF4J์™€ Logback์„ ์ด์šฉํ•œ ๋กœ๊น… ์„ค์ •

์Šคํ”„๋ง ๋ถ€ํŠธ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ SLF4J์™€ Logback์ด ์„ค์ •๋˜์–ด ์žˆ๋‹ค. Lombok์„ ์‚ฌ์šฉํ•˜๋ฉด ๋กœ๊ฑฐ ์„ค์ •์ด ๋”์šฑ ๊ฐ„ํŽธํ•ด์ง„๋‹ค. Lombok์˜ @Slf4j ์• ๋„ˆํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๊ฑฐ๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

Lombok์„ ์‚ฌ์šฉํ•œ ๋กœ๊น… ์˜ˆ์‹œ

package com.hippoo.learnspringboot.controller;

import com.hippoo.learnspringboot.service.CourseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/courses")
@Slf4j
public class CourseController {

    @Autowired
    private CourseService courseService;

    @GetMapping("/{id}")
    public Course getCourseById(@PathVariable Long id) {
        log.info("Fetching course with id: {}", id);
        Course course = courseService.findById(id);
        if (course == null) {
            log.warn("Course with id: {} not found", id);
        } else {
            log.debug("Course details: {}", course);
        }
        return course;
    }

    @PostMapping
    public Course createCourse(@RequestBody Course course) {
        log.info("Creating new course: {}", course.getName());
        Course createdCourse = courseService.save(course);
        log.info("Created course with id: {}", createdCourse.getId());
        return createdCourse;
    }

    @DeleteMapping("/{id}")
    public void deleteCourse(@PathVariable Long id) {
        log.info("Deleting course with id: {}", id);
        courseService.deleteById(id);
        log.info("Deleted course with id: {}", id);
    }
}

2. ์ง์ ‘ ๋กœ๊ฑฐ ์ƒ์„ฑํ•˜๊ธฐ

Lombok์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ, SLF4J์˜ LoggerFactory๋ฅผ ์ด์šฉํ•˜์—ฌ ๋กœ๊ฑฐ๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

์ง์ ‘ ๋กœ๊ฑฐ๋ฅผ ์ƒ์„ฑํ•œ ๋กœ๊น… ์˜ˆ์‹œ

package com.hippoo.learnspringboot.service;

import com.hippoo.learnspringboot.repository.CourseRepository;
import com.hippoo.learnspringboot.model.Course;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class CourseService {

    private static final Logger logger = LoggerFactory.getLogger(CourseService.class);

    @Autowired
    private CourseRepository courseRepository;

    public Course findById(Long id) {
        logger.info("Service: Fetching course with id: {}", id);
        try {
            Course course = courseRepository.findById(id);
            if (course == null) {
                logger.warn("Service: Course with id: {} not found", id);
            } else {
                logger.debug("Service: Found course: {}", course);
            }
            return course;
        } catch (Exception e) {
            logger.error("Service: Error fetching course with id: {}", id, e);
            throw e;
        }
    }

    public Course save(Course course) {
        logger.info("Service: Saving new course: {}", course.getName());
        try {
            Course savedCourse = courseRepository.save(course);
            logger.info("Service: Saved course with id: {}", savedCourse.getId());
            return savedCourse;
        } catch (Exception e) {
            logger.error("Service: Error saving course: {}", course.getName(), e);
            throw e;
        }
    }

    public void deleteById(Long id) {
        logger.info("Service: Deleting course with id: {}", id);
        try {
            courseRepository.deleteById(id);
            logger.info("Service: Deleted course with id: {}", id);
        } catch (Exception e) {
            logger.error("Service: Error deleting course with id: {}", id, e);
            throw e;
        }
    }
}

3. ๋กœ๊น… ๋ ˆ๋ฒจ๋ณ„ ์‚ฌ์šฉ ์‚ฌ๋ก€


๋กœ๊น… ์ถœ๋ ฅ ์œ„์น˜์™€ ๋กค๋ง ํŒŒ์ผ ์„ค์ •

Logback ๋กค๋ง ํŒŒ์ผ ์„ค์ • ์˜ˆ์‹œ

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>logs/myapp.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>logs/myapp-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
        <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
            <maxFileSize>10MB</maxFileSize>
        </timeBasedFileNamingAndTriggeringPolicy>
        <maxHistory>30</maxHistory>
    </rollingPolicy>
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{36} - %msg%n</pattern>
    </encoder>
</appender>

๊ฒฐ๋ก  ๐ŸŽฏ

Spring Boot Logging์€ ๊ฐ•๋ ฅํ•œ ์ถ”์ƒํ™” ๋•๋ถ„์— ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋ณต์žกํ•œ ๋กœ๊น… ์„ค์ •์„ ํฌ๊ฒŒ ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์•„๋„ ๋˜๋„๋ก ๋„์™€์ค€๋‹ค. ์ƒํ™ฉ์— ๋”ฐ๋ผ Logback, Log4j2 ๋“ฑ ๊ตฌํ˜„์ฒด๋ฅผ ์ž์œ ๋กญ๊ฒŒ ์„ ํƒํ•  ์ˆ˜ ์žˆ๊ณ , application.properties๋งŒ์œผ๋กœ๋„ ์ถฉ๋ถ„ํžˆ ๊ฐ„๋‹จํ•œ ์„ค์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

SLF4J๋ฅผ ํ†ตํ•ด ๋กœ๊น… API๋ฅผ ์ผ๊ด€๋˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋กœ๊น… ํŒŒ์ผ์˜ ์ด๋ฆ„๊ณผ ๊ฒฝ๋กœ๋ฅผ ์œ ์—ฐํ•˜๊ฒŒ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ธฐ๋ณธ ์„ค์ •์„ ํ™œ์šฉํ•˜๊ฑฐ๋‚˜, logback-spring.xml๊ณผ ๊ฐ™์€ ์„ค์ • ํŒŒ์ผ์„ ํ†ตํ•ด ์„ธ๋ฐ€ํ•˜๊ฒŒ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ์–ด ๋‹ค์–‘ํ•œ ์š”๊ตฌ์‚ฌํ•ญ์„ ์ถฉ์กฑํ•  ์ˆ˜ ์žˆ๋‹ค.

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


์ฐธ๊ณ ์ž๋ฃŒ ๐Ÿ“š


Edit page
Share this post on:

Previous Post
[Spring๐ŸŒฑ] JDBC,Spring JDBC,Spring JPA ๊ฐœ๋…๊ณผ ์‚ฌ์šฉ๋ฒ• ์ด์ •๋ฆฌ
Next Post
[Spring๐ŸŒฑ] application.properties ํ™œ์šฉ