Logback

Logback์—์„œ์˜ ์„ค์ •

Logback configuration ์„ค๋ช…์— ์•ž์„œ, Joran์ด๋ผ๋Š” ์ด๋ฆ„์ด ์ž์ฃผ ๋‚˜์˜ฌ ๊ฒƒ์ž…๋‹ˆ๋‹ค. Joran์€ Logback์ด ์‚ฌ์šฉํ•˜๋Š” configuration ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.

๋˜ํ•œ, ์ค‘๊ฐ„ ์ •๋„์˜ ํฌ๊ธฐ์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด๋ผ๋„ ์ฝ”๋“œ์— ์ˆ˜์ฒœ ๊ฐœ์˜ ๋กœ๊น… ๋ฌธ์„ ํฌํ•จํ•˜๊ธฐ์— ๊ทธ๊ฒƒ๋“ค์„ ํšจ์œจ์ ์ด๊ณ  ๊ฐ„ํŽธํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ๊ฐ€ ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค.

Logback์€ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์œผ๋กœ ๋˜๋Š” XML์ด๋‚˜ Groovy ํฌ๋งท์˜ ์„ค์ • ์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ์„ ํ†ตํ•ด์„œ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Logback์ด ์Šค์Šค๋กœ ์„ค์ •์„ ์ฐพ๋Š” ์Šคํ…์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

classpath์—์„œ logback-test.xml ํŒŒ์ผ์„ ์ฐพ์Šต๋‹ˆ๋‹ค.
(1) ๊ณผ์ •์—์„œ ํŒŒ์ผ์„ ์ฐพ์ง€ ๋ชปํ–ˆ๋‹ค๋ฉด, classpath์—์„œ logback.groovy ํŒŒ์ผ์„ ์ฐพ์Šต๋‹ˆ๋‹ค.
(2) ๊ณผ์ •์—์„œ ํŒŒ์ผ์„ ์ฐพ์ง€ ๋ชปํ–ˆ๋‹ค๋ฉด, classpath์—์„œ logback.xml ํŒŒ์ผ์„ ์ฐพ์Šต๋‹ˆ๋‹ค.
(3) ๊ณผ์ •์—์„œ ํŒŒ์ผ์„ ์ฐพ์ง€ ๋ชปํ–ˆ๋‹ค๋ฉด, JDK 1.6์˜ service-provider loading facility (Service Loader)์— ์˜ํ•ด com.qos.logback.classic.spi.Configurator ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์ฒด๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค. ํƒ์ƒ‰ ์œ„์น˜๋Š” classpath์—์„œ META-INF\services\ch.qos.logback.classic.spi.Configurator ์ž…๋‹ˆ๋‹ค.
์œ„ ๊ณผ์ •์—์„œ ์„ฑ๊ณตํ•œ ๊ฒฝ์šฐ๊ฐ€ ์—†๋‹ค๋ฉด, logback์€ ์ฝ˜์†”์— ์ถœ๋ ฅํ•˜๋Š” BasicConfigurator์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

๋‹น์‹ ์ด ํŠน๋ณ„ํ•œ Logback ์„ค์ •์„ ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๋ผ๋ฉด ์ž๋™์œผ๋กœ (5) ๋ฒˆ์˜ BasicConfigurator๋กœ ์„ค์ •๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, Joran์ด Logback Conguration ํŒŒ์ผ์„ ํŒŒ์‹ฑ ํ•˜๋Š”๋ฐ 100 ๋ฐ€๋ฆฌ์„ธ์ปจ๋“œ ์ •๋„์˜ ์‹œ๊ฐ„์ด ์†Œ์š”๋ฉ๋‹ˆ๋‹ค. ์ด ์ •๋„์˜ ๋ช‡ ๋ฐ€๋ฆฌ์„ธ์ปจ๋“œ๋ผ๋„ ์ค„์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋” ๋น ๋ฅด๊ฒŒ ์‹œ์ž‘๋˜๊ธธ ๋ฐ”๋ž€๋‹ค๋ฉด (1) ~ (4) ์Šคํ…์„ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง์ ‘ BasicConfigurator๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Logback ์ž๋™ ์„ค์ •

logback์„ ์„ค์ •ํ•˜๋Š” ๊ฐ€์žฅ ์‰ฌ์šด ๋ฐฉ๋ฒ•์€ ๊ทธ๋ƒฅ ์•„๋ฌด ์„ค์ •์„ ์—†์ด BasicConfigurator์—๊ฒŒ ๋งก๊ธฐ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์ง€์ •ํ•  ๊ฒฝ์šฐ console์— ๋กœ๊ทธ ๋ฉ”์‹œ์ง€๊ฐ€ ์ถœ๋ ฅ๋จ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. BasicConfigurator๋ฅผ ํ†ตํ•ด ์„ค์ •ํ•  ๊ฒฝ์šฐ ์ตœ์†Œํ•œ์œผ๋กœ ์„ค์ •๋œ ConsoleAppender๊ฐ€ ๋ฃจํŠธ ๋กœ๊ฑฐ์— ๋ถ€์ฐฉ๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ถœ๋ ฅ ํฌ๋งท์€ ์•„๋ž˜์™€ ๊ฐ™์ด ์ง€์ •๋˜์—ˆ์œผ๋ฉฐ PatternLayoutEncoder์— ๋“ฑ๋ก๋˜์–ด์žˆ๊ณ , ๋ฃจํŠธ ๋กœ๊ฑฐ์˜ ๋ ˆ๋ฒจ์€ DEBUG๋กœ ์ง€์ •๋ฉ๋‹ˆ๋‹ค.

%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n.

์˜ˆ์‹œ)

package chapters.configuration;
  
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
   
public class Foo {
  static final Logger logger = LoggerFactory.getLogger(Foo.class);
  
  public void doIt() {
    logger.debug("Did it again!");
  }
}
16:06:09.046 [main] DEBUG chapters.configuration.Foo - Did it again!

logback-test.xml / logback.xml์„ ์ด์šฉํ•œ Logback ์ž๋™ ์„ค์ •

์•ž์„œ ์„ค์ • ๊ณผ์ •์—์„œ ์•Œ์•„๋ณด์•˜๋‹ค์‹œํ”ผ, Logback์€ ์„ค์ • ํŒŒ์ผ์„ ์ฐพ๋Š” ์ˆœ์„œ๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฒˆ์—๋Š” classpath์— logback.xml ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ  ์•„๋ž˜์™€ ๊ฐ™์ด Logback ์„ค์ •์„ ์ž‘์„ฑํ•ด๋ด…์‹œ๋‹ค. ์•„๋ž˜์˜ ์„ค์ •์€ BasicConfigurator์™€ ๋™์ผํ•œ ์„ค์ •์ž…๋‹ˆ๋‹ค. ์ดํ›„ ๋™์ž‘์‹œ์ผœ๋ณด๋ฉด ์œ„์˜ ๊ฒฐ๊ณผ์™€ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ž„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

<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>

  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

Configuration ํŒŒ์ผ์„ ๋ถ„์„ํ•˜๋Š” ๋™์•ˆ ๊ฒฝ๊ณ  ๋˜๋Š” ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด, Logback์€ ๋‚ด์žฅ ์ƒํƒœ ์‹œ์Šคํ…œ์— ์˜ํ•ด ์ฝ˜์†”์— ๋‚ด๋ถ€ ์ƒํƒœ ๋ฐ์ดํ„ฐ(Status Data)๋ฅผ ์ž๋™์œผ๋กœ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์‚ฌ์šฉ์ž๊ฐ€ ์ƒํƒœ ์ˆ˜์‹  ์ฝ”๋“œ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ๋“ฑ๋กํ•  ๊ฒฝ์šฐ ์ค‘๋ณต์„ ์ œ๊ฑฐํ•˜๊ธฐ ์œ„ํ•ด Logback์˜ ์ž๋™ ์ƒํƒœ ์ถœ๋ ฅ ๊ธฐ๋Šฅ์€ ๋น„ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค.

์ƒํƒœ ๋ฐ์ดํ„ฐ๋Š” StatusPrinter๋ฅผ ์ด์šฉํ•˜์—ฌ ์ถœ๋ ฅํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋Š” ๋ฐ˜๋ฉด, logback.xml ํŒŒ์ผ์„ ํ†ตํ•ด ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐฉ๋ฒ•์€ configuration๋””๋ ‰ํ‹ฐ๋ธŒ์— debug="true" ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

<configuration debug="true"> <!-- ์ƒํƒœ ๋ฐ์ดํ„ฐ ์ถœ๋ ฅ -->

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
    <!-- encoders are  by default assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

๋˜ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์€ StatusListener๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. debug="true" ์†์„ฑ์€ OnConsoleStatusListener๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๊ฒƒ๊ณผ ์™„์ „ํžˆ ๋™์ผํ•˜๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

<configuration>
  <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />  

  ... the rest of the configuration file  
</configuration>

์‹œ์Šคํ…œ ๋ณ€์ˆ˜๋ฅผ ํ†ตํ•œ Logback Configuration ๊ฒฝ๋กœ ์ง€์ •

Logback์ด ์Šคํ…์„ ๋”ฐ๋ผ ์„ค์ •์„ ์ฐพ๋Š” ๋ฐฉ๋ฒ• ์ด์™ธ logback.configuration ์ด๋ฆ„์˜ ์‹œ์Šคํ…œ ๋ณ€์ˆ˜๋กœ ๊ฒฝ๋กœ๋ฅผ ์ง€์ •ํ•ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฒฝ๋กœ์˜ logback ์„ค์ • ํŒŒ์ผ์˜ ํ™•์žฅ์ž๋Š” ํ•ญ์ƒ ".xml" ํ˜น์€ ".groovy"์ด์–ด์•ผ๋งŒ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์‹œ์Šคํ…œ ์„ค์ •์€ java ์‹คํ–‰ command ์˜ต์…˜์œผ๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋˜ํ•œ Application Code์—์„œ ์ถ”๊ฐ€ํ•ด ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

java -Dlogback.configurationFile=/path/to/config.xml chapters.configuration.MyApp1
import ch.qos.logback.classic.util.ContextInitializer;

public class ServerMain {
    public static void main(String args[]) throws IOException, InterruptedException {
       // must be set before the first call to  LoggerFactory.getLogger();
       // ContextInitializer.CONFIG_FILE_PROPERTY is set to "logback.configurationFile"
       System.setProperty(ContextInitializer.CONFIG_FILE_PROPERTY, "/path/to/config.xml");
       ...
    }   
}

์ž๋™ Configuration ์žฌ๊ตฌ์„ฑ

logback-classic ๋ชจ๋“ˆ์€ ์ฃผ๊ธฐ์ ์œผ๋กœ configuration ํŒŒ์ผ์„ ์ฝ์–ด Logback์˜ ์„ค์ •์„ ์žฌ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” Application์ด ๋™์ž‘ ์ค‘์ผ ๋•Œ ์žฌ์‹œ์ž‘ ์—†์ด ์„ค์ • ํŒŒ์ผ์„ ์ˆ˜์ •ํ•˜๊ณ  ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ž๋™์œผ๋กœ Logback ์„ค์ • ์žฌ๊ตฌ์„ฑ์„ ์œ„ํ•ด configuration ๋””๋ ‰ํ‹ฐ๋ธŒ์— scan="true" ์†์„ฑ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์œผ๋กœ ์ ์šฉ๋˜๋Š” scan ์ฃผ๊ธฐ๋Š” 1๋ถ„์ž…๋‹ˆ๋‹ค. scan์ฃผ๊ธฐ๋Š” scanPeriod ์†์„ฑ์„ ์ด์šฉํ•˜์—ฌ ์ง€์ •ํ•ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

<configuration scan="true" scanPeriod="30 seconds" > 
  ...
</configuration> 

scanPeriod์˜ ๊ฐ’์€ ์‹œ๊ฐ„๊ณผ ์‹œ๊ฐ„์˜ ๋‹จ์œ„("milliseconds", "seconds", "minutes", "hours")๋ฅผ ์กฐํ•ฉํ•˜์—ฌ ๊ธฐ์ˆ ํ•ฉ๋‹ˆ๋‹ค.

ํŒจํ‚ค์ง€ ๋ฐ์ดํ„ฐ ๋กœ๊น…

Logback์„ ์ด์šฉํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ํŒจํ‚ค์ง€ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๊น…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 1.1.4 ๋ฒ„์ „ ์ดํ›„ ์ด ์„ค์ •์€ ๊ธฐ๋ณธ๊ฐ’์ด disable ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ํŒจํ‚ค์ง€ ๋ฐ์ดํ„ฐ ๋กœ๊น…์„ ์œ„ํ•ด์„œ๋Š” configuration ๋””๋ ‰ํ‹ฐ๋ธŒ์— packagingData="true" ์†์„ฑ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” versioning ์ด์Šˆ์— ๋Œ€ํ•œ ๋ฌธ์ œ๋ฅผ ํŒŒ์•…ํ•˜๋Š”๋ฐ ํฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ํŒจํ‚ค์ง• ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒ€์‚ฌํ•˜๊ณ  ๋กœ๊น…ํ•˜๋Š” ๊ณผ์ •์€ ๋น„๊ต์  ํฐ ๋น„์šฉ์„ ๊ฐ–๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ์— ๋”ฐ๋ผ ์‚ฌ์šฉ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ์„ค์ •์€ LoggerContext์—์„œ๋„ ์ง€์ • ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

<configuration packagingData="true">
  ...
</configuration>
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
lc.setPackagingDataEnabled(true);

JoranConfigurator ์ง์ ‘ ์ด์šฉํ•˜๊ธฐ

Logback์€ logback-core ๋ชจ๋“ˆ ๋‚ด ์กด์žฌํ•˜๋Š” Joran์ด๋ผ๋Š” configuration ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ Configuration ๋ฉ”์ปค๋‹ˆ์ฆ˜์œผ๋กœ Logback์€ classpath์—์„œ ์ฐพ์€ ๊ธฐ๋ณธ configuration ํŒŒ์ผ์—์„œ JoranConfigurator๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์–ด๋– ํ•œ ์ด์œ ๋กœ ๊ธฐ๋ณธ ๋กœ๊ทธ๋ฐฑ์˜ configuration ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์˜ค๋ฒ„๋ผ์ด๋“œํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ง์ ‘ JoranConfigurator๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜์—ฌ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

package chapters.configuration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.StatusPrinter;

public class MyApp3 {
  final static Logger logger = LoggerFactory.getLogger(MyApp3.class);

  public static void main(String[] args) {
    // assume SLF4J is bound to logback in the current environment
    LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
    
    try {
      JoranConfigurator configurator = new JoranConfigurator();
      configurator.setContext(context);
      // Call context.reset() to clear any previous configuration, e.g. default 
      // configuration. For multi-step configuration, omit calling context.reset().
      context.reset(); 
      configurator.doConfigure(args[0]);
    } catch (JoranException je) {
      // StatusPrinter will handle this
    }
    StatusPrinter.printInCaseOfErrorsOrWarnings(context);

    logger.info("Entering application.");

    Foo foo = new Foo();
    foo.doIt();
    logger.info("Exiting application.");
  }
}

์˜ˆ์‹œ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ด…์‹œ๋‹ค. ์šฐ์„  LoggerContext๋ฅผ ์ง์ ‘ ๊ฐ€์ ธ์˜ค๊ณ , JoranConfigurator๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. JoranConfigurator์˜ context๋กœ ์ด์ „ ์ค„์—์„œ ๊ฐ€์ ธ์˜จ LoggerContext๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. context๋Š” configuration ํŒŒ์ผ์„ ์ฝ๊ณ  ํŒŒ์‹ฑ ํ•˜๋Š” ๋™์ž‘์„ ์œ„ํ•ด ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ดํ›„, LoggerContext๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  main ๋ฉ”์„œ๋“œ๋กœ ์ „๋‹ฌ๋ฐ›์€ configuration file ์ด๋ฆ„์„ ํ†ตํ•ด ์„ค์ •ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. JoranConfigurator์— context๋ฅผ ์„ธํŒ…ํ•œ ํ›„ StatusPrinter๋ฅผ ํ†ตํ•ด ๋‚ด๋ถ€ ์ƒํƒœ ๋˜ํ•œ ์ถœ๋ ฅํ•˜๋„๋ก ์„ค์ •ํ•˜์—ฌ ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

LoggerContext ์ดˆ๊ธฐํ™”๋Š” multi-step configuration ๊ณผ์ •์„ ์œ„ํ•ด ๋ฐ˜๋“œ์‹œ ํ˜ธ์ถœ๋˜์–ด์•ผ ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค.

Logger์˜ ์ƒํƒœ ๋ฉ”์‹œ์ง€ ํ™”๋ฉด ์„ค์ •

Logback์€ LoggerContext๋ฅผ ํ†ตํ•ด StatusManager๋ผ๋Š” ๊ฐ์ฒด์— ๋‚ด๋ถ€ ์ƒํƒœ ๋ฐ์ดํ„ฐ๋“ค์„ ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค. Logback์— ์ง€์ •๋œ default StatusManager๋Š” ๋ฉ”๋ชจ๋ฆฌ ์ ์ธ ์ธก๋ฉด์—์„œ ํ•„์š”ํ•œ ์ •๋„์˜ ์–‘๋งŒ ๊ฐ–๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด ๋‘ (Head์™€ Tail) ๋ถ€๋ถ„์œผ๋กœ ์ƒํƒœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๊ธ€์ž ๊ทธ๋Œ€๋กœ Header๋Š” ๋กœ๊ทธ์˜ ์ฒซ ๋ถ€๋ถ„์„ ์˜๋ฏธํ•˜๋ฉฐ Tail์€ ๋ (์ตœ์‹ ) ๋ถ€๋ถ„์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. Default ์„ค์ •๋œ ๋ฉ”๋ชจ๋ฆฌ์— ์œ ์ง€ํ•  ๋กœ๊ทธ์˜ ๊ฐœ์ˆ˜๋Š” 150๊ฐœ์ž…๋‹ˆ๋‹ค (์ด๋Š” ์ถ”ํ›„ release์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค).

logback-classic์—์„œ๋Š” ์ด๋Ÿฌํ•œ ์ƒํƒœ ๋ฐ์ดํ„ฐ๋ฅผ ์‹œ๊ฐํ™”ํ•˜์—ฌ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ViewStatusMessageServlet์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ViewStatusMessageServlet์€ ํ˜„์žฌ ์‚ฌ์šฉ ์ค‘์ธ LoggerContext์— ํ•ด๋‹นํ•˜๋Š” StatusManager ๋‚ด์šฉ๋“ค์„ HTML ํ…Œ์ด๋ธ”๋กœ ์ถœ๋ ฅํ•ด์ค๋‹ˆ๋‹ค. ์‚ฌ์šฉ์„ ์œ„ํ•ด WEB-INF/web.xml ํŒŒ์ผ์— ViewStatusMessageServlet์„ ์„ค์ •ํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  <servlet>
    <servlet-name>ViewStatusMessages</servlet-name>
    <servlet-class>ch.qos.logback.classic.ViewStatusMessagesServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>ViewStatusMessages</servlet-name>
    <url-pattern>/lbClassicStatus</url-pattern>
  </servlet-mapping>

ViewStatusMessage ์„œ๋ธ”๋ฆฟ์˜ ๋™์ž‘์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด์„œ http://{host}/lbClassicStatus ๋กœ ์ ‘์†ํ•ฉ๋‹ˆ๋‹ค. (host๋Š” ๋™์ž‘ํ•œ Application์˜ host ์ฃผ์†Œ๋กœ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค). \

์ƒํƒœ ๋ฉ”์‹œ์ง€ ๋ฆฌ์Šค๋„ˆ

StatutManager์— StatusListener๋ฅผ ๋ถ™์—ฌ ์ƒํƒœ ๋ฉ”์‹œ์ง€์— ๋Œ€ํ•œ ์‘๋‹ต์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Logback์€ StatusListener์˜ ๊ตฌํ˜„์ฒด๋กœ ์ฝ˜์†”์— ์ƒˆ๋กœ ๋“ค์–ด์˜ค๋Š” ์ƒํƒœ ๋ฉ”์‹œ์ง€๋ฅผ ์ถœ๋ ฅํ•˜๋Š” OnConsolerStatusListener๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

   LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); 
   StatusManager statusManager = lc.getStatusManager();
   OnConsoleStatusListener onConsoleListener = new OnConsoleStatusListener();
   statusManager.add(onConsoleListener);

StatusListener๋Š” ๋“ฑ๋ก๋œ ํ›„ ์ƒˆ๋กœ ๋“ค์–ด์˜ค๋Š” ์ƒํƒœ ์ด๋ฒคํŠธ์— ๋Œ€ํ•œ ๋ฉ”์‹œ์ง€๋ฅผ ์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ฆฌ์Šค๋„ˆ ๋“ฑ๋ก ์ด์ „์— ๋ฐœ์†ก๋œ ๋ฉ”์‹œ์ง€๋“ค์„ ์ˆ˜์‹ ํ•˜์ง€ ์•Š๊ธฐ์—, Configuration ๊ณผ์ • ์ค‘ StatusListener์˜ ๋“ฑ๋ก์„ ๊ฐ€์žฅ ์•ž์— ๋‘๋Š” ๊ฒƒ์ด ์ข‹์€ ๋“ฑ๋ก ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

Configuration ํŒŒ์ผ์— ํ•˜๋‚˜ ํ˜น์€ ์ด์ƒ์˜ StatusListener๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. logback.xml์— StatusListener๋ฅผ ์ง€์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

<configuration>
  <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />  

  ... the rest of the configuration file  
</configuration>

StatusListener๋Š” ์‹œ์Šคํ…œ ๋ณ€์ˆ˜๋กœ์จ ์ „๋‹ฌํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณ€์ˆ˜์˜ "logback.statusListenerClass"์œผ๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณต๋˜๋Š” StatusListener์˜ ์ข…๋ฅ˜๋Š” OnConsoleStatusListener, OnErrorConsoleStatusListener, NopStatusListener๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

java -Dlogback.statusListenerClass=ch.qos.logback.core.status.OnConsoleStatusListener ...
packageStatusListener๋™์ž‘ ๋ฐฉ์‹

ch.qos.logback.core.status

OnConsoleStatusListener

์ฝ˜์†”์— ์ถœ๋ ฅ

System.out

OnErrorConsoleStatusListener

System.err๋ฅผ ํ†ตํ•ด ์ถœ๋ ฅ

System.err

NopStatusListener

์ƒํƒœ ๋ฉ”์‹œ์ง€ drop (๋ฌด์‹œ)

StatusListener๊ฐ€ ์ง€์ •๋˜์ง€ ์•Š์œผ๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ ์ƒํƒœ ๋ฉ”์‹œ์ง€๋Š” ์ถœ๋ ฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋Š” NopStatusListener๋ฅผ ๋ฆฌ์Šค๋„ˆ๋กœ ์ง€์ •ํ–ˆ์„ ๋•Œ์™€ ๋™์ผํ•˜๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

logback-classic ์ค‘์ง€์‹œํ‚ค๊ธฐ

logback-classic ๋ชจ๋“ˆ์— ์˜ํ•ด ์‚ฌ์šฉ๋˜๋˜ ๋ฆฌ์†Œ์Šค๋ฅผ ํ•ด์ œ์‹œํ‚ฌ ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์€ logback context๋ฅผ ์ค‘์ง€์‹œํ‚ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. context๋ฅผ ์ค‘์ง€์‹œํ‚ค๋ฉด context๋ฅผ ํ†ตํ•ด์„œ logger์— ๋ถ€์ฐฉ๋˜์—ˆ๋˜ ๋ชจ๋“  appender๋“ค์ด close ๋˜๋ฉฐ, active ์ƒํƒœ์˜ ์Šค๋ ˆ๋“œ๋ฅผ ์ฐจ๋ก€๋Œ€๋กœ ์ค‘์ง€์‹œํ‚ต๋‹ˆ๋‹ค.

import org.sflf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
...

// assume SLF4J is bound to logback-classic in the current environment
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
loggerContext.stop();

์œ„์˜ ์ฝ”๋“œ๋Š” LoggerContext๋ฅผ ์ค‘์ง€์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. loggerContext.stop() ๋ฉ”์„œ๋“œ๋Š” ServletContextListener์˜ contextDestroyed ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋กœ ์ธํ•ด logback-classic์ด ์ •์ง€๋˜๋ฉฐ ์‚ฌ์šฉํ•˜๋˜ ๋ฆฌ์†Œ์Šค๋“ค์„ ํ•ด์ œํ•ฉ๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ JVM shutdown hook์„ ์ด์šฉํ•ด์„œ logback-classic์„ ์ค‘์ง€์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ์„  configuration ํŒŒ์ผ์— shutdownHook ๋””๋ ‰ํ‹ฐ๋ธŒ๋ฅผ ์ถ”๊ฐ€ํ•ด์ค๋‹ˆ๋‹ค. shutdownHook ๋””๋ ‰ํ‹ฐ๋ธŒ์— class ์†์„ฑ์œผ๋กœ ์ง€์ •ํ•  shutdown hook ํด๋ž˜์Šค๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์•„๋ฌด ์†์„ฑ์„ ์ง€์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๊ธฐ๋ณธ์œผ๋กœ DefaultShutdownHook ํด๋ž˜์Šค๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค.

<configuration debug="true">
   <!-- in the absence of the class attribute, assume 
   ch.qos.logback.core.hook.DefaultShutdownHook -->
   <shutdownHook/>
  .... 
</configuration>

DefaultShutdownHook์€ hook ๋ฐœ์ƒ ํ›„ ๊ณง๋ฐ”๋กœ(delay 0 by default) logback context๋ฅผ ์ค‘์ง€์‹œํ‚ต๋‹ˆ๋‹ค. context ์ค‘์ง€์—๋Š” ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋กœ๊ทธ ํŒŒ์ผ ์••์ถ• ์ž‘์—…์ด ์ผ์–ด๋‚˜๋Š” ๊ฒƒ์„ ๋Œ€๋น„ํ•ด ์ตœ๋Œ€ 30์ดˆ์˜ delay๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. JVM ์ข…๋ฃŒ ํ›„์—๋„ ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋กœ ๋™์ž‘ํ•˜๋Š” ์••์ถ• ์ž‘์—…๋“ฑ์„ ์™„๋ฃŒํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด์„  ๋‹จ์ˆœํžˆ <shutdownHook/> ๋””๋ ‰ํ‹ฐ๋ธŒ๋งŒ์„ ์ง€์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์›น์„œ๋ฒ„์—์„œ๋Š” webShutdownHook์ด ์ž๋™์œผ๋กœ shutdownHook ๋””๋ ‰ํ‹ฐ๋ธŒ๋ฅผ ์ง€์ •ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์„ค์ •์„ ํ•˜์ง€ ์•Š๋„๋ก ํ•ฉ๋‹ˆ๋‹ค (์„ค์ • ์ค‘๋ณต ๋ฐœ์ƒ ๊ฐ€๋Šฅ).

servlet-api 3.x ์ดํ›„์—๋Š” logback-classic์ด ์ž๋™์œผ๋กœ ์›น ์„œ๋ฒ„์—๊ฒŒ ServletContanierInitialzer๋ฅผ implements ํ•˜๋Š” LogbackServletContatinerInitializer๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค. LogbackServletContatinerInitializer๋Š” LogbackServletContextListener๋ฅผ ์ž๋™์œผ๋กœ ๋“ฑ๋กํ•˜๋Š”๋ฐ ์ด ๋ฆฌ์Šค๋„ˆ๋ฅผ ํ†ตํ•ด ์›น ์„œ๋ฒ„๊ฐ€ stop ํ˜น์€ reload ๋  ๋•Œ ์ž๋™์œผ๋กœ logback-classic context๋ฅผ ์ค‘์ง€์‹œ์ผœ์ค๋‹ˆ๋‹ค.

๋งŒ์•ฝ ์ž๋™์œผ๋กœ ๋ฆฌ์Šค๋„ˆ๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๊ฒƒ์„ ์›์น˜ ์•Š๋Š”๋‹ค๋ฉด web.xml ํŒŒ์ผ์— logbackDisableServletContainerInitializer๋ฅผ ์„ธํŒ…ํ•จ์œผ๋กœ์จ ์ž๋™ ๋“ฑ๋ก์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

<web-app>
    <context-param>
        <param-name>logbackDisableServletContainerInitializer</param-name>
        <param-value>true</param-value>
    </context-param>
    .... 
</web-app>

Last updated