1. Общ преглед
Logback е една от най-често използваните системи за регистриране в Java общността. Това е заместител на предшественика си Log4j. Logback предлага по-бързо внедряване от Log4j, предоставя повече опции за конфигуриране и повече гъвкавост при архивиране на стари регистрационни файлове.
Това въведение ще представи архитектурата на Logback и ще ви покаже как можете да го използвате, за да подобрите приложенията си.
2. Архитектура на обратната връзка
Три класа се състоят от архитектурата Logback; Logger , Appender и Layout .
Регистраторът е контекст за регистрационни съобщения. Това е класът, с който приложенията си взаимодействат, за да създават регистрационни съобщения.
Апендерите поставят регистрационни съобщения в крайните си дестинации. Регистратор може да има повече от един Appender. Обикновено смятаме Appenders като прикачени към текстови файлове, но Logback е много по-мощен от това.
Layout подготвя съобщения за извеждане. Logback поддържа създаването на персонализирани класове за форматиране на съобщения, както и стабилни опции за конфигуриране на съществуващите.
3. Настройка
3.1. Зависимост на Maven
Logback използва Simple Logging Facade за Java (SLF4J) като свой собствен интерфейс. Преди да започнем да регистрираме съобщения, трябва да добавим 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-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 и разговори информация () , за да генерира съобщение дневник.
Когато стартираме Example , виждаме нашето съобщение, регистрирано в конзолата:
20:34:22.136 [main] INFO Example - Example log from Example
Лесно е да се разбере защо Logback е толкова популярен; ние работим за минути.
Тази конфигурация и код ни дават няколко съвета как става това.
- Имаме приложение, наречено STDOUT, което се позовава на име на клас ConsoleAppender.
- Има модел, който описва формата на нашето дневник съобщение.
- Нашата код създава Logger и минахме нашето послание към него чрез информация () метод .
Сега, след като разбрахме основите, нека разгледаме по-отблизо.
5. дървар контексти
5.1. Създаване на контекст
За да регистрираме съобщение в Logback, инициализирахме Logger от SLF4J или Logback:
private static final Logger logger = LoggerFactory.getLogger(Example.class);
И след това го използвах:
logger.info("Example log from {}", Example.class.getSimpleName());
Това е нашият контекст на регистриране. Когато го създадохме, преминахме LoggerFactory нашия клас. Това дава име на регистратора (има и претоварване, което приема низ).
Контексти за регистриране съществуват в йерархия, която много прилича на йерархията на Java обекти:
- Дърводобивът е предшественик, когато името му, последвано от точка, поставя пред името на потомков регистратор
- Дърводобивът е родител, когато между него и дете няма предци
Например примерният клас по-долу е в пакета com.baeldung.logback . Има и друг клас, наречен ExampleAppender в com.baeldung.logback.appenders пакета.
Регистраторът на ExampleAppender е дъщерно на Logger на Example.
Всички регистратори са потомци на предварително дефинирания root регистратор.
А ударно има ниво, което може да се настрои или чрез конфигурация или с Logger.setLevel (). Задаването на нивото в кода заменя конфигурационните файлове.
Възможните нива са по ред на приоритет: TRACE, DEBUG, INFO, WARN и ERROR.Всяко ниво има съответния метод, който използваме, за да регистрираме съобщение на това ниво.
Ако регистраторът не получи изрично ниво, той наследява нивото на най-близкия си предшественик. Основният регистратор по подразбиране е 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");
When we run this, we see these messages:
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
We start by retrieving a Logger named com.baeldung.logback and cast it to a ch.qos.logback.classic.Logger.
A Logback context is needed to set the level in the next statement; note that the SLF4J's abstract logger does not implement setLevel().
We set the level of our context to INFO;we then create another logger named com.baeldung.logback.tests.
We log two messages with each context to demonstrate the hierarchy. Logback logs the WARN, and INFO messages and filters the DEBUGmessages.
Now, let's use the root logger:
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.");
We see these messages when we execute this snippet:
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.
To conclude, we started with a Logger context and printed a DEBUG message.
We then retrieved the root logger using its statically defined name and set its level to ERROR.
And finally, we demonstrated that Logback actually does filter any statement less than an error.
5.3. Parameterized Messages
Unlike the messages in the sample snippets above, most useful log messages required appending Strings. This entails allocating memory, serializing objects, concatenating Strings, and potentially cleaning up the garbage later.
Consider the following message:
log.debug("Current count is " + count);
We incur the cost of building the message whether the Logger logs the message or not.
Logback offers an alternative with its parameterized messages:
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:
- Search for files named logback-test.xml, logback.groovy,or logback.xml in the classpath, in that order.
- 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.
- 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.