- ๋ชฉ์ฐจ
- Logback ์ดํดํ๊ธฐ
- 1 Logback ๋ชจ๋ ๊ตฌ์กฐ
- 2 Logback ์ค์
- 3 Logback ์ค์ ํ์ผ ๊ตฌ์กฐ
- 4 Spring Boot ๊ธฐ๋ฐ Logback ์ค์ ๋ฐ ์ ๋ต
- ์ถ์ฒ
์๋ฐ ๋ก๊น ํ๋ ์์ํฌ ํ์คํ ๋ฆฌ์ sl4fj ์ดํดํ๊ธฐ์ ์ด์ด ์ด๋ฒ ๊ธ์ slf4j์ binding module์ค ํ๋์ธ Logback์ ์ ๋ฆฌํด๋ณด๊ณ ์ํ๋ค.
ceki Gulcรผ๊ฐ ์๋ฐ ๋ก๊น ํ๋ ์์ํฌ์ ํ์ค ์ธํฐํ์ด์ค ์คํ์ธ slf4j๋ฅผ ๊ฐ๋ฐํ๋ฉด์, ์์ ์ด ๊ฐ๋ฐํ log4j์ ์ฌ๋ฌ๊ฐ์ง ๋จ์ ์ ๊ทน๋ณตํ๊ธฐ์ํด slf4j ๊ตฌํ์ฒด์ธ Logback๋ ๊ฐ์ด ๊ฐ๋ฐํ์๋ค๊ณ ํ๋ค.
์ด๋ฒ ๊ธ์ Logback์ ๊ตฌ์กฐ๋ฅผ ๊ฐ๋จํ ์ดํด๋ณด๊ณ , ๊ฐ๋จํ ํ์ต ํ ์คํธ์ ํจ๊ป ์ค์ ์ ์ด๋ป๊ฒ ํ๋์ง ์ดํด๋ณธ๋ค.
Logback์ log4j์ ํ์ ํ๋ก์ ํธ๋ก log4j๊ฐ ๋ ๋๋ ์์น๋ฅผ ์ด์ด๋ฐ๊ธฐ ์ํด ํ์ํ์๋ค.
๊ทธ๋ฌ๋ฏ๋ก Logback ์ํคํ ์ฒ๋ ๋ง์ ํ๊ฒฝ์์ ์ ์ฉ ๊ฐ๋ฅํ ์ ๋๋ก ์ผ๋ฐ์ ์ด๊ณ ์ ์ฐํ๋ค.
์ผ๋ฐ์ ์ด๊ณ ์ ์ฐํ ๊ตฌ์กฐ๋ฅผ ์ํด Logback์ ์ธ ๊ฐ์ง ๋ชจ๋๋ก ๊ตฌ์ฑ๋์ด์๋ค.
logback-core- ๋ค๋ฅธ ๋ ๋ชจ๋์ ํ ๋๋ฅผ ๋ง๋ จํ๋ค. (๊ธฐ๋ฐ์ด ๋๋ ๋ชจ๋)
Appender,Layout๋ฑ Logback์ ์ฌ์ฉํ๋๋ฐ ๊ผญ ํ์ํ ๊ธฐ๋ฐ ๋ชจ๋์ด๋ค.
logback-classiclogback-core๋ชจ๋์ ํ์ฅํ ๋ชจ๋๋ก์, slf4j api module๋ฅผ ๊ตฌํํ ๋ชจ๋์ด๊ธฐ๋ํ๋ค.- ์ฝ๊ฒ ์๊ธฐํด
logback-core์slfj4 api๋ ๋ชจ๋์ ํฌํจํ๊ณ ์๋ค๊ณ ๋ณด๋ฉด ๋๋ค.- ์ฐธ๊ณ ๋ก
slf4j api๊ฐLogger์ ๋ํ ์ธํฐํ์ด์ค๋ฅผ ํฌํจํ๊ณ ์๋ค.
- ์ฐธ๊ณ ๋ก
logback-access- Tomcat์ด๋ Jetty์ ๊ฐ์ ์๋ธ๋ฆฟ ์ปจํ ์ด๋์ ํตํฉ๋ผ HTTP-Access ๋ก๊ทธ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
Logback์ slf4j์ binding module๋ก์จ slf4j๋ฅผ ๊ตฌํํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ฏ๋ก, ๋น์ฐํ slf4j์ ๊ฐ์ด ์ฌ์ฉํ๊ฒ๋๋ค.
๊ทธ๋ฆฌ๊ณ Logback์ ์ธ ๊ฐ์ง ์ฃผ์ ํด๋์ค๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๊ฒ๋๋ค.
Logger(logback-classic๋ชจ๋์ ์ผ๋ถ)- ์ค์ ๋ก๊น ์ ์ํํ๋ ํด๋์ค.
LoggerFactory๋ก๋ถํฐLogger๊ฐ์ฒด๋ฅผ ๋ถ๋ฌ์ ๋ก๊ทธ๋ฅผ ์ฐ๋ ์ฝ๋๋ฅผ ์์ฑํ๋ค.
Appender(logback-core๋ชจ๋์ ์ผ๋ถ)- ๋ก๊ทธ๋ฅผ ๊ธฐ๋ก (write)ํ๋ ์์ ์ ์ํํ๋ ํด๋์ค.
- ๋ก๊ทธ ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํ ๋์์ ๊ฒฐ์ ๋ฐ ์ค์ ํ๋ค.
Layout(logback-core๋ชจ๋์ ์ผ๋ถ)- ๋ก๊ทธ๋ฅผ ์ง์ ํ ํ์์ผ๋ก ๋ณํํ๋ ์์
์ ์ํํ๋ ํด๋์ค. ํ์ฌ๋
Encoder๋ฅผ ๋ง์ด ์ฌ์ฉํ๋ค. - ๋ก๊ทธ๋ฅผ ๋ฐ์ดํธ ๋ฐฐ์ด๋ก ๋ณํํ๊ณ , ๋ฐ์ดํธ ๋ฐฐ์ด์ OutputStream์ ์ฐ๊ธฐํ๋ ์์ ์ ์ํํ๋๋ฐ ์ด๋ ์ํ๋ ํฌ๋งท์ผ๋ก ๋ณํํ ์ ์๋ค.
- ๋ก๊ทธ๋ฅผ ์ง์ ํ ํ์์ผ๋ก ๋ณํํ๋ ์์
์ ์ํํ๋ ํด๋์ค. ํ์ฌ๋
์ด ์ธ ๊ฐ์ง ์ปดํฌ๋ํธ๊ฐ ํจ๊ป ๋์ํจ์ผ๋ก์จ ๋ฉ์์ง ํ์ ์ด๋ ๋ ๋ฒจ์ ๋ฐ๋ผ ์ด๋ฅผ ๊ธฐ๋กํ ์ ์๊ณ , ๋ฉ์์ง์ ํฌ๋งท๊ณผ ๋ฆฌํฌํ ์์น๋ฅผ ์ ์ดํ ์ ์๋ค.
Logger๋ logback-classic ๋ชจ๋์ ์์นํ๊ณ ์๋๋ฐ, ์ ํํ๋ slf4j-api ๋ชจ๋์ ์์นํ๋ค.
Logger.java
package org.slf4j;
public interface Logger {
// Printing methods:
public void trace(String message);
public void debug(String message);
public void info(String message);
public void warn(String message);
public void error(String message);
}๋ค์ฏ ๊ฐ์ง์ ๋ ๋ฒจ์ ์ ์ํ๊ณ ์์ผ๋ฉฐ ์๋์ ๊ฐ์ด ์ฝ๋์์ ๋ก๊ทธ๋ฅผ ์ฐ๊ณ ์ถ์ ๋ถ๋ถ์ ์ฌ์ฉํ ์ ์๋ค.
Slf4jApiModuleHelloWorld.java
Logger logger = LoggerFactory.getLogger(Slf4jApiModuleHelloWorld.class);
logger.trace("Trace Hello World");
logger.debug("Debug Hello World");
logger.info("Info Hello World");
logger.warn("Warn Hello World");
logger.error("Error Hello World");๐โโ๏ธ ๋ชจ๋ ๋จ์ผ Logger๋ LoggerContext์ ์ฐ๊ฒฐ๋๋ฉฐ, LoggerContext๋ Logger ๊ณ์ธต ๊ตฌ์กฐ๋ก ์ด๋ค์ ์์ฑ ๋ฐ ์ ๋ ฌํ๊ณ ๊ด๋ฆฌํ๋ค.
LoggerContext์ ๋ชจ๋ Logger๋ค์ด ์ฐ๊ฒฐ๋๋ฉฐ, Logger์ ์์ฑ๊ณผ ๊ณ์ธต ๊ตฌ์กฐ๋ฅผ ์ค์ ํ ์ ์๋ค.
์ด๋ Logger๋ ๊ณ์ธต์ ๋ช
๋ช
๊ท์น์ ๋ฐ๋ฅธ๋ค.
Logger์ ์ด๋ฆ์ด .(dot)์ ์ํด ํ์ Logger ์ด๋ฆ์ ์ ๋์ด๊ฐ ๋ ๊ฒฝ์ฐ ์ด๋ ๋ค๋ฅธ Logger์ ์กฐ์์ด๋๋ ๊ตฌ์กฐ๋ค. -> ๊ณ์ธต๊ตฌ์กฐ
com.foo์ Logger๋com.foo.Bar์ธ Logger์ ๋ถ๋ชจ์ด๋ค.java์ Logger๋java.util์ ๋ถ๋ชจ์ด์,java.util.Vector์ ์กฐ์์ด๋๋ค.
์ด๋ฌํ ํน์ง์ ์ด์ฉํ์ฌ ๊ฐ๋ฐ์๊ฐ ์ค์ ํ ๋ถ๋ฅ ๊ธฐ์ค์ ๋ฐ๋ผ ๊ณ์ธต๊ตฌ์กฐ๋ณ ํน์ ๋ ๋ฒจ์ ๋ก๊ทธ๋ฅผ ํ์ฑํ/๋นํ์ฑํํ ์ ์๋ค.
์ฝ๊ฒ ์๊ธฐํ๋ฉด, ๋ชจ๋ ๋ก๊ทธ๋ ๋ ๋ฒจ์ ์ค์ ํ ์ ์์ผ๋ฉฐ, ๋ ๋ฒจ์ ์์๋๋ค.
๋ชจ๋
Logger๋Level์ ์ง์ ํ ์ ์์ผ๋ฉฐ, ๋ชจ๋ ๋ ๋ฒจ์ch.qos.logger.classic.levelํด๋์ค์ ์ ์๋์ด์๋ค.
๋ง์ฝ ์ฃผ์ด์ง Logger์ ๋ ๋ฒจ์ด ์ง์ ๋์ง ์๋๋ค๋ฉด ์ง์ ๋ ๋ ๋ฒจ์ ๊ฐ์ง ๊ฐ์ฅ ๊ฐ๊น์ด ์กฐ์์์ ๋ ๋ฒจ์ ์์๋ฐ๋๋ค.
์ฆ, ์ฃผ์ด์ง Logger A์ ์ ํจ ๋ ๋ฒจ์ A๋ก๋ถํฐ root๋ฅผ ํฅํด ๊ณ์ธต ๊ตฌ์กฐ๋ฅผ ๊ฑฐ์ฌ๋ฌ ์ฌ๋ผ๊ฐ๋ฉด์ ๋ง๋๋ ์ฒซ ๋ฒ์งธ null์ด ์๋ ๋ ๋ฒจ๊ณผ ๊ฐ๋ค.
๐โโ๏ธ ๋ฃจํธ Logger
๋ฃจํธ Logger์ ๊ฒฝ์ฐ Logger ๊ณ์ธต ๋งจ ์์ ์์นํ๋ค.
๊ทธ๋ฆฌ๊ณ ๋ฃจํธ Logger๋ ์๋์ ๊ฐ์ด ๊ฐ์ ธ์ฌ ์ ์๋ค.
Logger rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
๊ธฐํ Logger๋ ์์ ๊ฐ์ ์ฝ๋์ ๋์ผํ๊ฒ ๊ฐ์ ธ์ฌ ์ ์๋ค. (๋งค๊ฐ๋ณ์๋ง ๋ฐ๊ฟ์ฃผ๋ฉด ๋๋ค.)
๐โโ๏ธ ๋ ๋ฒจ ์์
์ด๋ป๊ฒ ์ ์ํ๋๋์ ๋ฐ๋ผ์, ํธ์ถ๋๋ ์ถ๋ ฅ ๋ฉ์๋๊ฐ ๋ก๊น ์์ฒญ ์์ค์ ๊ฒฐ์ ํ๋ค.
์๋ฅผ ๋ค์ด, log.info(...)๋ INFO ๋ ๋ฒจ์ ๋ก๊น
์ฝ๋๋ฅผ ์์ฑํ ๊ฒ์ด๋ค.
์ด๋ ๋ง์ฝ ๋ก๊น
์์ฒญ ๋ ๋ฒจ์ด ํด๋น Logger์ ์ ํจ ๋ ๋ฒจ ์ด์์ธ ๊ฒฝ์ฐ์๋ง ํด๋น ๋ก๊ทธ๊ฐ ๋์ํ๋ฉฐ, ๊ทธ๋ ์ง์์ผ๋ฉด ์์ฒญ์ ๋นํ์ฑํ๋๋ค.
์ฆ, ์ ํจ ๋ ๋ฒจ q๋ฅผ ๊ฐ๋ Logger์์ ํธ์ถ๋ ๋ ๋ฒจ p์ ๋ก๊ทธ ์์ฒญ์ p >= q์ธ ๊ฒฝ์ฐ์๋ง ํ์ฑํ(๋ก๊ทธ๊ฐ ์ฐํ๋ค)๋๋ค.
์ด๋ฅผ ๊ฐ๋จํ ํ๋ก ์ ๋ฆฌํ๋ฉด ์๋์ ๊ฐ๋ค.

์ถ์ฒ: http://logback.qos.ch/manual/architecture.html#basic_selection
๊ฐ๋จํ ๋งํด ๋ก๊น
๋ ๋ฒจ์ ๋ค์๊ณผ ๊ฐ์ ์์๋ก ๋์ด์๋ค: TRACE < DEBUG < INFO < WARN < ERROR
์์
import ch.qos.logback.classic.Level;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
....
// get a logger instance named "com.foo". Let us further assume that the
// logger is of type ch.qos.logback.classic.Logger so that we can
// set its level
ch.qos.logback.โclassic.Logger logger =
(ch.qos.logback.โclassic.Logger) LoggerFactory.โgetLogger("com.foo");
//set its Level to INFO. The setLevel() method requires a logback logger
logger.setLevel(Level. INFO);
Logger barlogger = LoggerFactory.โgetLogger(โ"com.foo.Bar");
// This request is enabled, because WARN >= INFO
logger.warn("Low fuel level.");
// This request is disabled, because DEBUG < INFO.
logger.debug("Starting search for nearest gas station.");
// The logger instance barlogger, named "com.foo.Bar",
// will inherit its level from the logger named
// "com.foo" Thus, the following request is enabled
// because INFO >= INFO.
barlogger.info("Located nearest gas station.");
// This request is disabled, because DEBUG < INFO.
barlogger.debug("Exiting gas station search");๐โโ๏ธ LoggerContext์ Logger๋ ์ฑ๊ธํค์ผ๋ก ๋์ํ๋ค.
// ์๋ Logger๋ ๊ฐ์ ์ฃผ์๋ฅผ ๊ฐ์ง๋ค.
Logger x = LoggerFactory.getLogger("wombat");
Logger y = LoggerFactory.getLogger("wombat");์ ์์์ ๊ฐ์ด LoggerFactory.getLogger ๋ฉ์๋๋ ๊ฐ์ ์ด๋ฆ์ ๋งค๊ฐ๋ณ์๋ก ๋๊ธฐ๋ฉด ํญ์ ๊ฐ์ ์ธ์คํด์ค๋ฅผ ๋ฐํํ๋ค. (์ฑ๊ธํค)
Logback์ ๋ค์ํ ๋ชฉ์ ์ง๋ก ๋ก๊น ๋ด์ฉ์ ๋ณด๋ด๊ณ ์ถ๋ ฅํ ์ ์๋ค.
๊ทธ๋ฆฌ๊ณ Logback์์ ์ด์ ๊ฐ์ด ๋ก๊ทธ ์ถ๋ ฅ ๋ชฉ์ ์ง์ ๊ธฐ๋กํ๋์ญํ ์ Appender๊ฐ ๋ด๋นํ๋ค.
๋ก๊ทธ ๋ฉ์์ง๋ฅผ ์ถ๋ ฅ, ์ ์ฅ, ์ ์ก๋ ๋์์ ๊ฒฐ์ ํ๋ ์ญํ .
๋ชจ๋ Appender๋ ch.qos.logback.core.Appender๋ฅผ ๊ตฌํํ๋ค.
public interface Appender<E> extends LifeCycle, ContextAware, FilterAttachable {
public String getName();
public void setName(String name);
void doAppend(E event);
}doAppend๊ฐ ํต์ฌ ๋ฉ์๋์ด๋ฉฐ, ๋ก๊น
์ด๋ฒคํธ๋ฅผ ์ ์ ํ ํ์์ผ๋ก ์ ์ ํ ์ถ๋ ฅ ์ฅ์น์ ์ถ๋ ฅํ๋ ์ญํ ์ ์ํํ๋ค.
๊ด๋ จ๋ ๋ ์์ธํ ๋ด์ฉ์ ๊ณต์ ๋ฌธ์๋ฅผ ์ฐธ๊ณ .
๐โโ๏ธ OutputStreamAppender์ ํ์ ํด๋์ค๋ค์ด ์์ฃผ ์ฌ์ฉ๋๋ค.

์ถ์ฒ: https://logback.qos.ch/manual/appenders.html
OutputStreamAppender๋ ์์ฃผ ์ฌ์ฉ๋๋ ์๋ Appender๋ค์ ์์ ํด๋์ค์ด๋ค.
UnsynchronizedAppenderBase- ๊ธฐ๋ณธ์ ์ผ๋ก ์ฌ์ฉ๋๋ ์ถ์ ํด๋์ค์ธ
AppenderBase๋doAppend()๊ฐsynchronized๋ฅผ ์ฌ์ฉํ๋ค. - ๋ฐ๋ฉด์,
UnsynchronizedAppenderBase๋ ๋๊ธฐ๋ก ๋์ํ๊ธธ์์น์์๋ ์ฌ์ฉ๋๋ค.
- ๊ธฐ๋ณธ์ ์ผ๋ก ์ฌ์ฉ๋๋ ์ถ์ ํด๋์ค์ธ
OutputStreamAppenderOutputStream์ ๋ก๊ทธ ์ด๋ฒคํธ appendํ๋ ์ญํ ์ํ๋ค.
๐โโ๏ธ ConsoleAppender
๋ก๊ทธ๋ฅผ OuputStream์ writeํ์ฌ, ์ต์ข ์ ์ผ๋ก ์ฝ์์ ์ถ๋ ฅ๋๋๋กํ๋ค.
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg %n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>๐โโ๏ธ FileAppender
๋ก๊ทธ์ ๋ด์ฉ์ ์ง์ ๋ File์ ์ ์ฅํ๋ค.
๋งค ๋ก๊ทธ๋ง๋ค ์ ๋ํฌํ ์ด๋ฆ์ ์๋ก์ด ๋ก๊ทธ ํ์ผ์ ๊ธฐ๋กํ๊ธฐ์ํด์ timestamp๋ฅผ ์ด์ฉํ์ฌ ํ๊น ํ์ผ์ ๋์ ์ผ๋ก ์ค์ ํด์ค ์ ์๋ค.
<configuration>
<!-- Insert the current time formatted as "yyyyMMdd'T'HHmmss" under
the key "bySecond" into the logger context. This value will be
available to all subsequent configuration elements. -->
<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<!-- use the previously created timestamp to create a uniquely
named log file -->
<file>log-${bySecond}.txt</file>
<encoder>
<pattern>%logger{35} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>๐โโ๏ธ RollingFileAppender
FileAppender๋ฅผ ์์๋ฐ์ Appender๋ก, ๋ ์ง, ์ต๋ ์ฉ๋๋ฑ์ ์ค์ ํ์ฌ ์ง์ ํ ํ์ผ๋ช
ํจํด์ ๋ฐ๋ผ ๋ก๊ทธ๊ฐ ๋ค๋ฅธ ํ์ผ์ ๊ธฐ๋ก๋๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
์๋ฅผ ๋ค์ด, ํน์ ํ์ผ ์ด๋ฆ์ผ๋ก ๋ก๊ทธ๋ฅผ appendํ๋ค ํน์ ์กฐ๊ฑด (์๊ฐ, ์ฉ๋)์ ๋ค๋ค๋ฅด๋ฉด, ์ด์ ํ์ผ์ ์ ์ฅํ๊ณ , ๋ค๋ฅธ ๋ก๊ทธ ํ๊ฒํ์ผ์ ๋ง๋ค์ด ์ ์ฅํ๋ ํ์์ด๋ค.
์๋ฒ ์ ํ๋ฆฌ์ผ์ด์ ์์ ๊ฐ์ฅ ์์ฃผ ์ฌ์ฉ๋๋ Appender๋ก ๋๋ ๋ก๊ทธ ๊ธฐ๋ก์ ํจ๊ณผ์ ์ด๋ค.
2๊ฐ์ง์ Rolling ์ ์ฑ ์ด ์กด์ฌํ๋ฉฐ, ๊ณตํต์ ์ผ๋ก ์๋ properties๋ฅผ ๊ฐ์ง๋ค.
- file: ํ๊นํ์ผ์ ์ด๋ฆ.
- append: append ์ ์ฑ
.
- true - ์ด์ด์ฐ๊ธฐ
- false - ๋ฎ์ด์ฐ๊ธฐ
- encoder: ๋ก๊ทธ ์ด๋ฒคํธ๊ฐ OutputStreamAppender์ ๊ธฐ๋ก๋๋ ๋ฐฉ์.
- ex. LogstashEncoder
- rollingPolicy: rollover ๋ฐ์์ RollingFileAppender์ ํ๋์ ์ ์.
- triggerPolicy: rollover ํ์ฑํ ์์ ์ ์.
- predent: prudent mode ์ฌ๋ถ.
TimeBasedRollingPolicy
์๊ฐ์ ๊ธฐ๋ฐํ์ฌ Rolling ์ ์ฑ ์ ์ ์ํ ์ ์์ผ๋ฉฐ, ๋ณดํต ์ผ ๋๋ ์ ๋จ์๋ก ์ค์ ํ๋ค.
<configuration>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logFile.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover -->
<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- keep 30 days' worth of history capped at 3GB total size -->
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root>
</configuration>์ ๋ก๊ทธ ์ค์ ์ ํด์ํ๋ฉด Application์ด ๋์ํ ๋ ํ์ฑํ๋ logFile.log์ ๋ก๊ทธ๊ฐ ์์ง๋ง, ๋งค์ผ ์์ ์ด ์ง๋๋ฉด logFile.2023-03-17.log์ ๊ฐ์ ์ด๋ฆ์ผ๋ก ์์นด์ด๋ธ๋๋ค.
์์นด์ด๋ธ ๋๋ ํ์ผ์ ๊ฐ์๋ ์ต๋ 30๊ฐ์ด๋ฉฐ, ์์นด์ด๋ธ ๋ ํ์ผ์ ํฌ๊ธฐ๋ ์ด 3GB๋ฅผ ๋์ ์ ์๋ค.
์ด ์กฐ๊ฑด์ ๋ง์กฑํ์ง ๋ชปํ ๊ฒฝ์ฐ ์์นด์ด๋ธ๋ ๋ก๊ทธ ํ์ผ ์ค ๊ฐ์ฅ ์ค๋๋ ํ์ผ์ ์ญ์ ํ๋ค.
์ค์ ๋ณ ์์ธํ ์ค๋ช ์ ์๋๋ฅผ ์ฐธ๊ณ .
- fileNamePattern (ํ์)
- ์ ์ฅ๋ ๋ก๊ทธ ํ์ผ์ ํจํด์ ์ ์.
%d๋ฅผ ์ด์ฉํด ํ์ผ์ ์ ์ ํ ๋ถ๋ถ์ datetime ํจํด์ผ๋ก ์ ์ํ๋ค. (ex.%d{yyyy-MM-dd})
- maxHistory
- ์ ์ฅ๋๋ ๋ก๊ทธ ํ์ผ์ ์ต๋ ๊ฐ์๋ฅผ ์ง์ .
- ex. rollover์ ํ๋ฃจ ๋ง๋ค ๋๋ค๊ณ ๊ฐ์ ํ ๋, maxHistory๋ฅผ 30์ผ๋ก ์ง์ ํ๋ฉด ์ ์ฅ๋๋ ํ์ผ์ ๊ฐ์๋ ์ต๋ 30์ผ(30๊ฐ)์ด๋ค.
- totalSizeCap
- ๋ก๊ทธ ํ์ผ์ ์ ์ฅํ๋ ์ ์ฅ์(๋๋ ํ ๋ฆฌ)์ ์ต๋ํฌ๊ธฐ๋ฅผ ์ง์ .
- totalSizeCap์ ์ด๊ณผํ๋ฉด ๊ฐ์ฅ ์ค๋๋ ํ์ผ์ด ์ญ์ ๋๋ ๊ตฌ์กฐ.
- maxHistory์ ๊ฐ์ด ์ฌ์ฉ๋ ๊ฒฝ์ฐ, 1์์๋ก maxHistory์ ๋ํ์ฌ ์ฒ๋ฆฌ๋ ํ totalSizeCap์ด ์ ์ฉ๋จ.
- cleanHistoryOnStart
- ์ ํ๋ฆฌ์ผ์ด์ ์ด ์์๋ ๋ ๋ก๊ทธ ํ์ผ์ ๋ชจ๋ ์ง์ด๋ค. (๋ํดํธ๋ true)
fileNamePattern์ ๋ฐ๋ผ rollover๋๋ ์ฃผ๊ธฐ๊ฐ ๋ฌ๋ผ์ง๋ค.
.%d: default %d๋ yyyy-MM-dd. ๋งค์ผ ์์ ์ ์๋ก์ด ๋ก๊ทธ ํ์ผ๋ก rollover๋๋ค..%d{yyyy-MM-dd_HH-mm}:๋งค ๋ถ ์๋ก์ด ๋ก๊ทธ ํ์ผ๋ก rollover๋๋ค./%d{yyyy/MM}/foo.txt: ๋งค์ ์๋ก์ด ๋๋ ํ ๋ฆฌ๋ฅผ ๋ง๋ค์ด ํ์์ foo.txt ํ์ผ๋ก rolloverํ๋ค..%d.gz: ๋งค์ผ ์๋ก์ด ๋ก๊ทธ ํ์ผ๋ก rolloverํ๊ณ , ์ด์ ๋ก๊ทธํ์ผ์ GZIP์ผ๋ก ์์ถ๋๋ค.
SizeAndTimeBasedRollingPolicy
TimeBasedRollingPolicy์ ๋ํด ๊ฐ๊ฐ์ ๋ก๊ทธ ํ์ผ์ ํฌ๊ธฐ์ ์ ํ์์ฃผ๋ RollingPolicy์ด๋ค.
TimeBasedRollingPolicy์ ๋ค๋ฅด๊ฒ fileNamePattern์ %i์ %d๊ฐ ํ์์ด๋ฉฐ, ๊ฐ๊ฐ์ ๋ก๊ทธ ํ์ผ์ด ๊ฐ์ง ์ ์๋ ์ต๋ ํฌ๊ธฐ๋ฅผ ์ ํํ๋ maxFileSize ์ค์ ์ด ์ถ๊ฐ๋์๋ค.
%i ํ ํฐ์ด index ์ญํ ์ํ์ฌ ๋ค์ ๋ก๊ทธ ํ์ผ์ ์ด๋ฆ์ ๊ฒฐ์ ํ๋ค.
<configuration>
<appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>mylog.txt</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>60</maxHistory>
<totalSizeCap>20GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="ROLLING" />
</root>
</configuration>์ด์ธ์ ๋ค์ํ Appender๋ ๊ณต์ ๋ฌธ์๋ฅผ ์ฐธ๊ณ .
Encoder๋ ๋ก๊ทธ๋ฅผ ๋ฐ์ดํธ ๋ฐฐ์ด๋ก ๋ณํํ๊ณ , ๋ฐ์ดํธ ๋ฐฐ์ด์ OutputStream์ ์ฐ๊ธฐํ๋ ์์ ์ ์ํํ๋ค.
์ด๋ ๋ก๊ทธ๋ฅผ ๊ฐ ์ํฉ๋ณ ์ํ๋ ํฌ๋งท์ผ๋ก ๋ณํํ ์ ์๋ค.
logback 0.9.19๋ถํฐ๋ layout๋ Deprecated๋์์ผ๋ฉฐ, Encoder ์ฌ์ฉ์ ์งํฅํ๋ค. ์ฐธ๊ณ
Encoder.java
package ch.qos.logback.core.encoder;
public interface Encoder<E> extends ContextAware, LifeCycle {
/**
* This method is called when the owning appender starts or whenever output
* needs to be directed to a new OutputStream, for instance as a result of a
* rollover.
*/
void init(OutputStream os) throws IOException;
/**
* Encode and write an event to the appropriate {@link OutputStream}.
* Implementations are free to defer writing out of the encoded event and
* instead write in batches.
*/
void doEncode(E event) throws IOException;
/**
* This method is called prior to the closing of the underling
* {@link OutputStream}. Implementations MUST not close the underlying
* {@link OutputStream} which is the responsibility of the owning appender.
*/
void close() throws IOException;
}Encoder ์ธํฐํ์ด์ค๋ ์์ ๊ฐ์ผ๋ฉฐ, ์ ๋ ฅ์ผ๋ก ๋ค์ด์ค๋ ๋ก๊ทธ Event๋ฅผ ๋ฐ์ดํธ ๋ฐฐ์ด๋ก ๋ณํํ๊ณ ๊ฒฐ๊ณผ ๋ฐ์ดํธ ๋ฐฐ์ด์ ์ ์ ํ OutputStream์ ๊ธฐ๋กํ๋ค.
์ด๋ Appender๊ฐ ๊ด๋ฆฌํ๋ OutputStream์ ๋ฐ์ดํธ ๋ฐฐ์ด์ ์ธ์ ๋ฌด์์ ์ฐ๊ธฐํ ์ง ์ ์ดํ ์ ์๋ค.
๐โโ๏ธ PatternLayoutEncoder
๋ณดํต ๋ก๊น
์ฉ์ผ๋ก Logback์ ์ฌ์ฉํ ๋ PatternLayoutEncoder๊ฐ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ๋๋ค.
๊ธฐ์กด์
PatternLayout๋ ๋ํํ ํด๋์ค์ธLayoutWrappingEncoder๋ก ํธํ๋๊ฒ๋ง๋ค์๋ค๊ณ ํ๋ค.
PatternLayoutEncoder์ ์ฌ์ฉ๋ฒ์ ์๋์ ๊ฐ๋ค.
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>testFile.log</file>
...
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%msg%n</pattern>
</encoder>
</appender>pattern์ ๋ค์ํ๊ฒ ์ค์ ๊ฐ๋ฅํ๋ฉฐ ๊ด๋ จ๋ ๋ด์ฉ์ ๊ณต์ ๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์.
์ง๊ธ๊น์ง Logback์ ์ค์ ์ ์ด๋ค ์์๋ก ๊ตฌ์ฑ๋์๋์ง ์์๋ณด์์ผ๋, ์ด๋ฒ์ ์ด๋ค ๊ตฌ์กฐ๋ก ์ค์ ๋๋์ง ์ดํด๋ณธ๋ค.
์ผ๋ฐ์ ์ธ Java ์ ํ๋ฆฌ์ผ์ด์
์ Logback ์์กด์ฑ์ ์ถ๊ฐํ์ฌ ์ฌ์ฉํ ๋, Logback์ ContextInitializer.configureByResource ์ ๊ตฌํ์๋ฐ๋ผ ์ค์ ์ ๋ก๋ํ๋ค.
ํ์ธ๊ฒฐ๊ณผ ๋งค ๋ฒ์ ๋ง๋ค ์กฐ๊ธ์ฉ ๋ค๋ฅธ ๋ฏํ๋ค.
์ฌ์ฉํ๋ ๋ฒ์ ์ ํ์ธํ์ฌ ํด๋น ํด๋์ค๋ฅผ ์ฐพ์ ์ด๋ค ์์์ธ์ง ํ์ธํ๋ ๊ฒ์ด ์ข์ ๋ฏ ํ๋ค.
ํ์์๊ฒฝ์ฐ ๋ณดํต classpath์
logback.xml๋ง์ ํตํด ์ค์ ํ๋ค.

์ถ์ฒ: http://logback.qos.ch/manual/configuration.html
- Configuration
- Appender
- 0๊ฐ ํน์ ๊ทธ ์ด์
- Logger
- 0๊ฐ ํน์ ๊ทธ ์ด์
- Root
- ์ต๋ 1๊ฐ
๐โโ๏ธ Appender ์ค์

์ถ์ฒ: http://logback.qos.ch/manual/configuration.html#configuringAppenders
<appender>๋ฅผ ์ด์ฉํด์ Appender (๋ก๊น ๋ชฉ์ ์ง)๋ฅผ ์ค์ ํ ์ ์๋ค.<appender>์ ๋งค๊ฐ๋ณ์name: appender์ ์ด๋ฆ์ ์ค์ ํ๋ค.class: appender์ ํด๋์ค๋ฅผ FQCN์ผ๋ก ์ค์ ํ๋ค.ConsoleAppender: ๋ก๊ทธ๋ฅผ OuputStream์ writeํ์ฌ, ์ต์ข ์ ์ผ๋ก ์ฝ์์ ์ถ๋ ฅ๋๋๋ก ํ๋ค.FileAppender: ๋ก๊ทธ์ ๋ด์ฉ์ ์ง์ ๋ File์ ์ ์ฅํ๋ค.RollingFileAppender:FileAppender๋ฅผ ์์๋ฐ์ appender๋ก, ๋ ์ง, ์ต๋ ์ฉ๋๋ฑ์ ์ค์ ํ์ฌ ์ง์ ํ ํ์ผ๋ช ํจํด์ ๋ฐ๋ผ ๋ก๊ทธ๊ฐ ๋ค๋ฅธ ํ์ผ์ ๊ธฐ๋ก๋๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค. -> ๋๋ ๋ก๊ทธ ๊ธฐ๋ก์ ํจ๊ณผ์ .- ์ด์ธ์๋ ๋ค์ํ appender๋ฅผ ์ ๊ณตํ๋ค. (DBAppender, SMTPAppender๋ฑ๋ฑ)
<appender>์ ์์<layout>,<encoder>,<filter>
๐โโ๏ธ Logger ์ค์
<logger>๋ฅผ ์ด์ฉํด์ Logger๋ฅผ ์ค์ ํ ์ ์๋ค.- ์ฆ, ์ค์ ๋ก๊ทธ ๊ธฐ๋ฅ์ ์ํํ๋ ๊ฐ์ฒด์ธ Logger๋ง๋ค ์ด๋ฆ์ ๋ถ์ฌํ๊ณ ๋ ๋ฒจ๋ฑ์ ์ค์ ํ ์ ์๋ค.
- ํด๋น ๋ก๊ฑฐ๊ฐ ์ฌ์ฉ๋ ํจํค์ง์ ๋ก๊ทธ ๋ ๋ฒจ์ ์ค์ .
<logger>์ ๋งค๊ฐ๋ณ์name: ๊ณ์ธต์ Logger ์ค์ ์ ์ ์ฉํ๊ฒ ์ฌ์ฉ๋๋ค. (ํ๋)level: ๋ก๊ฑฐ์ ๋ ๋ฒจ์ ์ค์ ํ๋ค. (optional)additivity: appender๋ฅผ ์์ํ๋๋ก ํ ๊ฒ์ธ์ง ์ค์ ํ๋ค. (optional)
<logger>์ ์์<appender-ref>: appender๋ฅผ ์ค์ ํ ๋ ์ฌ์ฉ๋๋ค. ๊ฐ ์์ค๋ก๋ถํฐ ์ ๋ ฅ๋ฐ์ ๋ก๊น ๋ฉ์์ง๋ ๋ก๊ทธ ๋ ๋ฒจ์ ๋ฐ๋ผ Appender๋ก ์ ๋ฌ๋๋ค.
- ์ฃผ์ ์ฌํญ
- ๊ธฐ๋ณธ์ ์ผ๋ก ์ต์์ ๋ก๊ฑฐ์ธ Root Logger๋ฅผ ์ค์ ํด์ฃผ์ด์ผ ํ๋ค.
๐โโ๏ธ Root ์ค์
<root>๋ฅผ ์ด์ฉํด์ Root Logger๋ฅผ ์ค์ ํ๋ค.<root>์ ๋งค๊ฐ๋ณ์level: ๋ ๋ฒจ์ ์ค์ ํ๋ค.
<root>์ ์์<appender-ref>: appender๋ฅผ ์ค์ ํ ์ ์๋ค. (<logger>์appender-ref์ ๋์ผ)
- ์ฃผ์ ์ฌํญ
- Root Logger์ name์
ROOT์ด๋ฉฐ, ๋ค๋ฅธ ์ค์ ์ ์ ๊ณตํ์ง ์๋๋ค.
- Root Logger์ name์
์ฝ์์ ์ถ๋ ฅํ๋ ์ค์ ์์
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="chapters.configuration" level="INFO" />
<logger name="chapters.configuration.Foo" level="DEBUG" />
<logger name="{Logger ์ด๋ฆ => ํด๋์ค ์ด๋ฆ}" level="INFO"/>
<!-- Strictly speaking, the level attribute is not necessary since -->
<!-- the level of the root level is set to DEBUG by default. -->
<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>ํ์ผ์ ์ถ๋ ฅํ๋ ์ค์ ์์
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>{ํ์ผ ์์น}.log</file>
<encoder>
<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="FILE" />
<appender-ref ref="STDOUT" />
</root>
</configuration>RollingFileAppender ์ค์ ์์
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- config ํ์ผ์์ ์ฌ์ฉ๋ ์์ฑ -->
<property name="LOGS_ABSOLUTE_PATH" value="/Users/binghe819/Desktop" />
<!-- ์ธ๋ถ ์ค์ ๊ฐ์ ธ์ค๋ ์ฝ๋ -->
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{36} - %msg%n</Pattern>
</layout>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOGS_ABSOLUTE_PATH}/logback.log</file>
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOGS_ABSOLUTE_PATH}/logback.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>5MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
<logger name="org.springframework.web" level="DEBUG">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</logger>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>include๋ฅผ ์ด์ฉํ์ฌ ์ค์ ๋ถ๋ฆฌํ ์์
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="LOGS_ABSOLUTE_PATH" value="/Users/binghe819/Desktop" />
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<!-- include๋ฅผ ํตํ ์ค์ -->
<include resource="logback/stdout-appender.xml"/>
<include resource="logback/file-appender.xml"/>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
<logger name="org.springframework.web" level="DEBUG">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</logger>
</configuration><!-- logback/file-appender.xml -->
<included>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOGS_ABSOLUTE_PATH}/logback.log</file>
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOGS_ABSOLUTE_PATH}/logback.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>15MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
</included><!-- logback/stdout-appender.xml -->
<included>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{36} - %msg%n</Pattern>
</layout>
</appender>
</included>์๋ง ๋๋ถ๋ถ์ slf4j + Logback์ ์ฌ์ฉํ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ Spring์ผ ๊ฒ์ด๋ค.
Spring์์ ์ด๋ป๊ฒ Logback์ ์ค์ ํ๊ณ ์ ๋ต์ ์ด๋ป๊ฒ ๊ฐ์ ธ๊ฐ๋์ง ์๊ณ ์ถ๋ค๋ฉด ๋ค์ ๊ธ์ ์ฐธ๊ณ ๋ฐ๋.