로그 백 가이드

1. 개요

Logback은 Java 커뮤니티에서 가장 널리 사용되는 로깅 프레임 워크 중 하나입니다. 이전 버전 인 Log4j를 대체합니다. Logback은 Log4j보다 빠른 구현을 제공하고 구성을위한 더 많은 옵션을 제공하며 오래된 로그 파일을 보관할 때 더 많은 유연성을 제공합니다.

이 소개는 Logback의 아키텍처를 소개하고이를 사용하여 애플리케이션을 개선하는 방법을 보여줍니다.

2. 로그 백 아키텍처

세 가지 클래스는 Logback 아키텍처를 구성합니다. Logger , AppenderLayout .

로거는 로그 메시지의 컨텍스트입니다. 애플리케이션이 로그 메시지를 생성하기 위해 상호 작용하는 클래스입니다.

어 펜더는 로그 메시지를 최종 대상에 배치합니다. Logger에는 둘 이상의 Appender가있을 수 있습니다. 일반적으로 Appenders는 텍스트 파일에 첨부 된 것으로 생각하지만 Logback은 그보다 훨씬 강력합니다.

레이아웃은 출력 할 메시지를 준비합니다. Logback은 기존 메시지에 대한 강력한 구성 옵션뿐만 아니라 메시지 형식을 지정하기위한 사용자 정의 클래스 생성을 지원합니다.

3. 설정

3.1. Maven 종속성

Logback은 SLF4J (Simple Logging Facade for Java)를 기본 인터페이스로 사용합니다. 메시지 로깅을 시작하기 전에 Logback 및 Slf4j를 pom.xml 에 추가해야합니다 .

 ch.qos.logback logback-core 1.2.3   org.slf4j slf4j-api 1.7.30 test  

Maven Central에는 최신 버전의 Logback Core와 최신 버전의 slf4j-api가 있습니다.

3.2. 클래스 경로

Logback은 런타임의 클래스 경로에 logback-classic.jar 도 필요 합니다.

이것을 pom.xml 에 테스트 종속성으로 추가합니다 .

 ch.qos.logback logback-classic 1.2.3  

4. 기본 예 및 구성

애플리케이션에서 Logback을 사용하는 간단한 예제부터 시작하겠습니다.

먼저 구성 파일이 필요합니다. logback.xml 이라는 텍스트 파일을 만들고 클래스 경로에 넣을 것입니다.

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

다음으로 메인 메서드 가있는 간단한 클래스가 필요합니다 .

public class Example { private static final Logger logger = LoggerFactory.getLogger(Example.class); public static void main(String[] args) { logger.info("Example log from {}", Example.class.getSimpleName()); } }

이 클래스는 Logger를 만들고 info () 를 호출 하여 로그 메시지를 생성합니다.

Example 을 실행하면 메시지가 콘솔에 기록 된 것을 볼 수 있습니다.

20:34:22.136 [main] INFO Example - Example log from Example

Logback이 왜 그렇게 인기가 있는지 쉽게 알 수 있습니다. 우리는 몇 분 안에 실행됩니다.

이 구성과 코드는 이것이 어떻게 작동하는지에 대한 몇 가지 힌트를 제공합니다.

  1. 클래스 이름 ConsoleAppender 를 참조하는 STDOUT 이라는 어 펜더 가 있습니다 .
  2. 로그 메시지의 형식을 설명하는 패턴이 있습니다.
  3. 우리 코드는 Logger를 만들고 info () 메서드 를 통해 메시지를 전달했습니다 .

이제 기본 사항을 이해 했으므로 자세히 살펴 보겠습니다.

5. 로거 컨텍스트

5.1. 컨텍스트 생성

Logback에 메시지를 기록하기 위해 SLF4J 또는 Logback에서 Logger 를 초기화했습니다 .

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

그리고 그것을 사용했습니다.

logger.info("Example log from {}", Example.class.getSimpleName()); 

이것이 우리의 로깅 컨텍스트입니다. 그것을 만들 때 우리는 LoggerFactory에 클래스를 전달 했습니다 . 이것은 Logger 에 이름을 부여합니다 ( 문자열 을 받아들이는 과부하도 있습니다 ).

로깅 컨텍스트는 Java 객체 계층과 매우 유사한 계층에 존재합니다.

  1. 로거는 이름과 점이 뒤 따르는 후손 로거의 이름 앞에 오는 경우 조상입니다.
  2. 로거와 자식 사이에 조상이없는 경우 로거는 부모입니다.

예를 들어, 아래 예제 클래스는 com.baeldung.logback 패키지에 있습니다. com.baeldung.logback.appenders 패키지 에는 ExampleAppender 라는 다른 클래스가 있습니다 .

ExampleAppender의 LoggerExample의 Logger의 자식입니다 .

모든 로거는 사전 정의 된 루트 로거의 자손입니다.

로거 보유 수준, 구성을 통해 하나 또는로 설정 될 수있다 ()는 Logger.setLevel. 코드에서 수준을 설정하면 구성 파일이 재정의됩니다.

가능한 수준은 우선 순위에 따라 TRACE, DEBUG, INFO, WARNERROR입니다.각 수준에는 해당 수준에서 메시지를 기록하는 데 사용하는 해당 방법이 있습니다.

Logger에 명시 적으로 수준이 할당되지 않은 경우 가장 가까운 조상의 수준을 상속합니다. 루트 로거의 기본값은 DEBUG입니다. 아래에서이를 재정의하는 방법을 살펴 보겠습니다.

5.2. 컨텍스트 사용

로깅 계층 구조 내에서 컨텍스트를 사용하는 방법을 보여주는 예제 프로그램을 만들어 보겠습니다.

ch.qos.logback.classic.Logger parentLogger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com.baeldung.logback"); parentLogger.setLevel(Level.INFO); Logger childlogger = (ch.qos.logback.classic.Logger)LoggerFactory.getLogger("com.baeldung.logback.tests"); parentLogger.warn("This message is logged because WARN > INFO."); parentLogger.debug("This message is not logged because DEBUG < INFO."); childlogger.info("INFO == INFO"); childlogger.debug("DEBUG < INFO"); 

이것을 실행하면 다음 메시지가 표시됩니다.

20:31:29.586 [main] WARN com.baeldung.logback - This message is logged because WARN > INFO. 20:31:29.594 [main] INFO com.baeldung.logback.tests - INFO == INFO

com.baeldung.logback 이라는 로거 를 검색하여 ch.qos.logback.classic.Logger 로 캐스트하는 것으로 시작합니다 .

Logback 컨텍스트는 다음 문에서 수준을 설정하는 데 필요합니다. SLF4J의 추상 로거는 setLevel ()을 구현하지 않습니다 .

컨텍스트 수준을 INFO로 설정했습니다 .그런 다음 com.baeldung.logback.tests 라는 또 다른 로거를 만듭니다 .

계층 구조를 보여주기 위해 각 컨텍스트와 함께 두 개의 메시지를 기록합니다. Logback는이 기록 WARN,정보 메시지와 필터 DEBUG를메시지.

이제 루트 로거를 사용해 보겠습니다.

ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger)LoggerFactory.getLogger("com.baeldung.logback"); logger.debug("Hi there!"); Logger rootLogger = (ch.qos.logback.classic.Logger)LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); logger.debug("This message is logged because DEBUG == DEBUG."); rootLogger.setLevel(Level.ERROR); logger.warn("This message is not logged because WARN < ERROR."); logger.error("This is logged."); 

이 스 니펫을 실행하면 다음 메시지가 표시됩니다.

20:44:44.241 [main] DEBUG com.baeldung.logback - Hi there! 20:44:44.243 [main] DEBUG com.baeldung.logback - This message is logged because DEBUG == DEBUG. 20:44:44.243 [main] ERROR com.baeldung.logback - This is logged. 

결론적으로 Logger 컨텍스트로 시작 하여 DEBUG 메시지를 인쇄했습니다 .

그런 다음 정적으로 정의 된 이름을 사용하여 루트 로거를 검색하고 레벨을 ERROR로 설정했습니다 .

마지막으로 Logback은 실제로 오류보다 적은 문을 필터링한다는 것을 보여주었습니다.

5.3. 매개 변수화 된 메시지

위의 샘플 스 니펫의 메시지와 달리 가장 유용한 로그 메시지는 문자열을 추가해야합니다 . 이것은 메모리를 할당하고, 객체를 직렬화하고, 문자열을 연결 하고, 잠재적으로 나중에 쓰레기를 정리하는 것을 수반 합니다.

다음 메시지를 고려하십시오.

log.debug("Current count is " + count); 

Logger가 메시지를 기록하는지 여부에 관계없이 메시지 구축 비용이 발생합니다.

Logback은 매개 변수화 된 메시지로 대안을 제공합니다.

log.debug("Current count is {}", count); 

The braces {} will accept any Object and uses its toString() method to build a message only after verifying that the log message is required.

Let's try some different parameters:

String message = "This is a String"; Integer zero = 0; try { logger.debug("Logging message: {}", message); logger.debug("Going to divide {} by {}", 42, zero); int result = 42 / zero; } catch (Exception e) { logger.error("Error dividing {} by {} ", 42, zero, e); } 

This snippet yields:

21:32:10.311 [main] DEBUG com.baeldung.logback.LogbackTests - Logging message: This is a String 21:32:10.316 [main] DEBUG com.baeldung.logback.LogbackTests - Going to divide 42 by 0 21:32:10.316 [main] ERROR com.baeldung.logback.LogbackTests - Error dividing 42 by 0 java.lang.ArithmeticException: / by zero at com.baeldung.logback.LogbackTests.givenParameters_ValuesLogged(LogbackTests.java:64) ... 

We see how a String, an int, and an Integer can be passed in as parameters.

Also, when an Exception is passed as the last argument to a logging method, Logback will print the stack trace for us.

6. Detailed Configuration

In the previous examples, we were using the 11-line configuration file to we created in section 4 to print log messages to the console. This is Logback's default behavior; if it cannot find a configuration file, it creates a ConsoleAppender and associates it with the root logger.

6.1. Locating Configuration Information

A configuration file can be placed in the classpath and named either logback.xml or logback-test.xml.

Here's how Logback will attempt to find configuration data:

  1. Search for files named logback-test.xml, logback.groovy,or logback.xml in the classpath, in that order.
  2. If the library does not find those files, it will attempt to use Java's ServiceLoader to locate an implementor of the com.qos.logback.classic.spi.Configurator.
  3. Configure itself to log output directly to the console

Note: the current version of Logback does not support Groovy configuration due to there not being a version of Groovy compatible with Java 9.

6.2. Basic Configuration

Let's take a closer look at our example configuration.

The entire file is in tags.

We see a tag that declares an Appender of type ConsoleAppender, and names it STDOUT. Nested within that tag is an encoder. It has a pattern with what looks like sprintf-style escape codes:

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

Last, we see a root tag. This tag sets the root logger to DEBUG mode and associates its output with the Appender named STDOUT:

6.3. Troubleshooting Configuration

Logback configuration files can get complicated, so there are several built-in mechanisms for troubleshooting.

To see debug information as Logback processes the configuration, you can turn on debug logging:

 ... 

Logback will print status information to the console as it processes the configuration:

23:54:23,040 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback-test.xml] at [file:/Users/egoebelbecker/ideaProjects/logback-guide/out/test/resources/logback-test.xml] 23:54:23,230 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender] 23:54:23,236 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT] 23:54:23,247 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property 23:54:23,308 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to DEBUG 23:54:23,309 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT] 23:54:23,310 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration. 23:54:23,313 |-INFO in [email protected] - Registering current configuration as safe fallback point

If warnings or errors are encountered while parsing the configuration file, Logback writes status messages to the console.

There is a second mechanism for printing status information:

  ... 

The StatusListener intercepts status messages and prints them during configuration and while the program is running.

The output from all configuration files is printed, making it useful for locating “rogue” configuration files on the classpath.

6.4. Reloading Configuration Automatically

Reloading logging configuration while an application is running is a powerful troubleshooting tool. Logback makes this possible with the scan parameter:

 ... 

The default behavior is to scan the configuration file for changes every 60 seconds. Modify this interval by adding scanPeriod:

 ... 

We can specify values in milliseconds, seconds, minutes, or hours.

6.5. Modifying Loggers

In our sample file above we set the level of the root logger and associated it with the console Appender.

We can set the level for any logger:

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

Let's add this to our classpath and run this code:

Logger foobar = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com.baeldung.foobar"); Logger logger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com.baeldung.logback"); Logger testslogger = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com.baeldung.logback.tests"); foobar.debug("This is logged from foobar"); logger.debug("This is not logged from logger"); logger.info("This is logged from logger"); testslogger.info("This is not logged from tests"); testslogger.warn("This is logged from tests"); 

We see this output:

00:29:51.787 [main] DEBUG com.baeldung.foobar - This is logged from foobar 00:29:51.789 [main] INFO com.baeldung.logback - This is logged from logger 00:29:51.789 [main] WARN com.baeldung.logback.tests - This is logged from tests 

By not setting the level of our Loggers programmatically, the configuration sets them; com.baeldung.foobar inherits DEBUG from the root logger.

Loggersalso inherit the appender-ref from the root logger. As we'll see below, we can override this.

6.6. Variable Substitution

Logback configuration files support variables. We define variables inside the configuration script or externally. A variable can be specified at any point in a configuration script in place of a value.

For example, here is the configuration for a FileAppender:

  ${LOG_DIR}/tests.log true  %-4relative [%thread] %-5level %logger{35} - %msg%n  

At the top of the configuration, we declared a propertynamed LOG_DIR.Then we used it as part of the path to the file inside the appender definition.

Properties are declared in a tag in configuration scripts. But they are also available from outside sources, such as system properties. We could omit the property declaration in this example and set the value of LOG_DIR on the command line:

$ java -DLOG_DIR=/var/log/application com.baeldung.logback.LogbackTests

We specify the value of the property with ${propertyname}. Logback implements variables as text replacement. Variable substitution can occur at any point in a configuration file where a value can be specified.

7. Appenders

Loggers pass LoggingEvents to Appenders.Appenders do the actual work of logging. We usually think of logging as something that goes to a file or the console, but Logback is capable of much more. Logback-core provides several useful appenders.

7.1. ConsoleAppender

We've seen ConsoleAppenderin action already. Despite its name, ConsoleAppender appends messages to System.outor System.err.

It uses an OutputStreamWriter to buffer the I/O, so directing it to System.err does not result in unbuffered writing.

7.2. FileAppender

FileAppenderappends messages to a file. It supports a broad range of configuration parameters. Let's add a file appender to our basic configuration:

    %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n    tests.log true  %-4relative [%thread] %-5level %logger{35} - %msg%n          

The FileAppender is configured with a file name via . The tag instructs the Appenderto append to an existing file rather than truncating it. If we run the test several times, we see that the logging output is appended to the same file.

If we re-run our test from above, messages from com.baeldung.logback.tests go to both the console and to a file named tests.log. The descendant logger inherits the root logger's association with the ConsoleAppender with its association with FileAppender. Appenders are cumulative.

We can override this behavior:

Setting additivity to falsedisables the default behavior. Tests will not log to the console, and neither will any of its descendants.

7.3. RollingFileAppender

Oftentimes, appending log messages to the same file is not the behavior we need. We want files to “roll” based on time, log file size, or a combination of both.

For this, we have RollingFileAppender:

  ${LOG_FILE}.log   ${LOG_FILE}.%d{yyyy-MM-dd}.gz  30 3GB   %-4relative [%thread] %-5level %logger{35} - %msg%n   

A RollingFileAppenderhas a RollingPolicy.In this sample configuration, we see a TimeBasedRollingPolicy.

Similar to the FileAppender, we configured this appender with a file name. We declared a property and used it for this because we'll be reusing the file name below.

We define a fileNamePattern inside the RollingPolicy.This pattern defines not just the name of files, but also how often to roll them. TimeBasedRollingPolicyexamines the pattern and rolls at the most finely defined period.

For example:

   ${LOG_DIR}/${LOG_FILE}.log  ${LOG_DIR}/%d{yyyy/MM}/${LOG_FILE}.gz 3GB 

The active log file is /var/logs/application/LogFile.This file rolls over at the beginning of each month into /Current Year/Current Month/LogFile.gzand RollingFileAppender createsa new active file.

When the total size of archived files reaches 3GB, RollingFileAppenderdeletes archives on a first-in-first-out basis.

There are codes for a week, hour, minute, second, and even millisecond. Logback has a reference here.

RollingFileAppenderalso has built-in support for compressing files. It compresses our rolled files because named our them LogFile.gz.

TimeBasedPolicyisn't our only option for rolling files. Logback also offers SizeAndTimeBasedRollingPolicy,which will roll based on current log file size as well as time. It also offers a FixedWindowRollingPolicywhich rolls log file names each time the logger is started.

We can also write our own RollingPolicy.

7.4. Custom Appenders

We can create custom appenders by extending one of Logback's base appender classes. We have a tutorial for creating custom appenders here.

8. Layouts

Layouts format log messages. Like the rest of Logback, Layoutsare extensible and we can create our own. However, the default PatternLayout offers what most applications need and then some.

We've used PatternLayout in all of our examples so far:

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

This configuration script contains the configuration for PatternLayoutEncoder.We pass an Encoder to our Appender,and this encoder uses the PatternLayout to format the messages.

The text in the tag defines how log messages are formatting. PatternLayout implements a large variety of conversion words and format modifiers for creating patterns.

Let's break this one down. PatternLayout recognizes conversion words with a %, so the conversions in our pattern generate:

  • %d{HH:mm:ss.SSS} – a timestamp with hours, minutes, seconds and milliseconds
  • [%thread] – the thread name generating the log message, surrounded by square brackets
  • %-5level – the level of the logging event, padded to 5 characters
  • %logger{36} – the name of the logger, truncated to 35 characters
  • %msg%n – the log messages followed by the platform dependent line separator character

So we see messages similar to this:

21:32:10.311 [main] DEBUG com.baeldung.logback.LogbackTests - Logging message: This is a String

An exhaustive list of conversion words and format modifiers can be found here.

9. Conclusion

In this extensive guide, we covered the fundamentals of using Logback in an application.

Logback 아키텍처의 세 가지 주요 구성 요소 인 로거, 어 펜더 및 레이아웃을 살펴 보았습니다. Logback에는 메시지 필터링 및 형식 지정을 위해 구성 요소를 조작하는 데 사용되는 강력한 구성 스크립트가 있습니다. 또한 로그 파일을 생성, 롤오버, 구성 및 압축하는 데 가장 일반적으로 사용되는 두 가지 파일 추가자를 살펴 보았습니다.

평소처럼 코드 스 니펫은 GitHub에서 찾을 수 있습니다.