====== Логирование ======
===== Сведения =====
:!: Logback
**Logback** - одна из наиболее используемых библиотек ведения журнала, замена предшественника **Log4j**\\
Состоит из трех классов:\\
**Logger**:\\
Контекст для сообщений журнала. Класс с которым взаимодействуют приложения для создания сообщений\\
**Appenders**:\\
Помещает сообщения в места назначения. У логгера может быть несколько, можно сказать что аппендер привязан к файлу, но он более функционален\\
**Layout**:\\
Форматирование сообщений\\
Maven зависимости:(?)\\
ch.qos.logback
logback-core
1.3.5
org.slf4j
slf4j-api
2.0.4
test
# И еще возможно это
ch.qos.logback
logback-classic
1.3.5
Минимальный пример:
# Файл 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());
}
}
# Еще такой пример есть, пока неясно зачем приведение и что ето за класс
ch.qos.logback.classic.Logger logger =
(ch.qos.logback.classic.Logger)LoggerFactory.getLogger("com.baeldung.logback");
logger.debug("Hi there!");
:!: Уровни
* TRACE — самый низкий уровень информации, в основном используемый для очень глубокой отладки кода, обычно не включаемый в производственные журналы.
* DEBUG — информация низкого уровня, используемая для целей отладки, обычно не включаемая в производственные журналы.
* INFO — серьезность журнала, содержащая информацию, например, о начавшейся или завершенной операции.
* WARN – уровень журнала, информирующий о событии, которое может потребовать нашего внимания, но не является критическим и может быть ожидаемым.
* ОШИБКА — уровень журнала, сообщающий об ошибке, ожидаемой или неожиданной, обычно означающей, что часть системы работает неправильно.
Библиотека "logback-core" содержит следующие классы:\\
* ConsoleAppender — добавляет события журнала в System.out или System.err.
* OutputStreamAppender — добавляет события журнала в java.io.Outputstream, предоставляя базовые услуги для других приложений.
* FileAppender — добавляет события журнала в файл.
* RollingFileAppender — добавляет события журнала в файл с возможностью автоматического переключения файлов.
"Logback-classic" расширяет перечень:\\
* SocketAppender — добавляет события журнала в сокет
* SSLSocketAppender — добавляет события журнала в сокет, используя безопасное соединение.
* SMTPAppender — накапливает данные в пакетах и отправляет содержимое пакета на указанный пользователем адрес электронной почты после наступления указанного пользователем события.
* DBAppender — добавляет данные в таблицы базы данных
* SyslogAppender — добавляет данные в место назначения, совместимое с Syslog.
* SiftingAppender — приложение, которое может разделять ведение журнала в соответствии с заданным атрибутом времени выполнения.
* AsyncAppender — асинхронно добавляет события журнала.
===== Конфигурация =====
:!: Конфигурация
Журнал отладки конфигурации:
...
# Либо такой метод
...
Автоматическая перезагрузка конфигурации
...
# По умолчанию 60 секунд, можно изменить
...
Конфигурация логгеров
# Уровень логирования можно задать каждому персонально
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
# Далее использование:
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");
Переменные
${LOG_DIR}/tests.log
true
%-4relative [%thread] %-5level %logger{35} - %msg%n
# Значение объявлено в первой строке, либо можно указать в аргументах запуска джарки (возможно и через переменные окружения, но это не точно)
$ java -DLOG_DIR=/var/log/application com.baeldung.logback.LogbackTests
:!: Appenders
Файловые аппендеры
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
tests.log
true
%-4relative [%thread] %-5level %logger{35} - %msg%n
Ротируемый аппендер
${LOG_FILE}.log
${LOG_FILE}.%d{yyyy-MM-dd}.gz
30
3GB
%-4relative [%thread] %-5level %logger{35} - %msg%n
===== Использование =====
:!: Приемы/детали
**Отдельный класс**\\
Для того чтобы перенаправить определенные сообщение в какой либо файл, создаем ему отдельный логгер, указав любое имя, создаем ему аппендер, с нужной конфигурацией, и в логере указываем **additivity="false"** чтобы сообщение не передавалось дальше\\
# Параметризованные сообщения
# фигурные скобки используют "toString()" при возможности/необходимости
log.debug("Current count is " + count);
log.debug("Current count is {}", count);
logger.debug("Going to divide {} by {}", 42, zero);
:!: Макеты
Например
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
* %d{HH:mm:ss.SSS} – метка времени с часами, минутами, секундами и миллисекундами
* [%thread] — имя потока, создающего сообщение журнала, заключенное в квадратные скобки.
* %-5level — уровень события логирования, дополненный до 5 символов
* %logger{36} – имя логгера , усеченное до 35 символов.
* %msg%n — сообщения журнала, за которыми следует символ-разделитель строк, зависящий от платформы.
[[https://logback.qos.ch/manual/layouts.html#conversionWord|Перечень]]\\
:!: Итого
root-логер является корневым, перехватывает все, с заданным уровнем\\
В апендерах можно настроить фильтрацию, в основном по уровню, но как то особо не развернешься с одними только ими\\
Есть вариант создавать свои логеры, но там нужно указывать класс, с которого идет обращения сообщения, из приложения, тобишь в приложении нужно создавать объект логера с таким же (или базовым) классом.\\
Вроде вариант, но базовый логер эти сообщения тоже перехватывает\\
Пример с разделением на три файла, общий, ошибки и спец файл, в него пишется из отдельного класса, правда в общие файлы эти сообщения тоже попадают т.к. там корневой логер\\
%d{dd-MM-yyyy HH:mm:ss.SSS} %magenta([%thread]) %highlight(%-5level) %logger{36}.%M - %msg%n
${LOGS}/system.log
%d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n
${LOGS}/archived/system_%d{yyyy-MM-dd}_%i.log.zip
50MB
INFO
${LOGS}/error.log
%d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n
${LOGS}/archived/error_%d{yyyy-MM-dd}_%i.log.zip
50MB
ERROR
${LOGS}/webapp.log
%d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n
${LOGS}/archived/webapp_%d{yyyy-MM-dd}_%i.log.zip
50MB
ERROR
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final Logger waLogger = LoggerFactory.getLogger("com.baeldung.logback");
logger.info("Запись в лог");
waLogger.info("(WA) Запись в лог");
:!: Еще пример
./logs/commands/commands.log
true
%d{yyyy-MM-dd} %d{HH:mm:ss.SSS} %-5level - %msg%n
./logs/commands/commands-%d{yyyy-MM-dd}.log.zip
50
(...)
:!: Рабочий пример, есть Json
System.out
%d{yyyy-MM-dd} %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
./log/FileMultiplicator.log
true
%d{yyyy-MM-dd} %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
log/FileMultiplicator-%d{yyyy-MM-dd}.%i.zip
100MB
7
20GB
log/json/FileMultiplicator.log
true
{
"timestamp": "%date{yyyy-MM-dd'T'HH:mm:ss}",
"logger": "%logger{36}",
"message": "%message",
"level": "%level"
}
30
2048
20
^sun\.reflect\..*\.invoke
^net\.sf\.cglib\.proxy\.MethodProxy\.invoke
true
log/json/FileMultiplicator-%d{yyyy-MM-dd}.%i.zip
100MB
7
20GB
INFO
./log/commands/commands.log
true
%d{yyyy-MM-dd} %d{HH:mm:ss.SSS} %-5level - %msg%n
./log/commands/commands-%d{yyyy-MM-dd}.log.zip
50
===== Ротирование логов =====
Аппендер класса **"RollingFileAppender"** расширяет класс **"FileAppender"**, добавляя возможность ротирования файлов\\
Два важных подкомпонента: **RollingPolicy** ("что делать") и **TriggeringPolicy** ("когда это делать")\\
Первый может реализовать интерфейс для второго, тогда достаточно указать только его\\
==== RollingPolicy ====
Внутри аппендера создается xml компонент политики, имеются разные классы, определяющие поведение\\
:!: Примеры классов для RollingPolicy
**TimeBasedRollingPolicy** - На основе времени, один обязательный аргумент "fileNamePattern", так же есть "MaxHistory"\\
Шаблон имени должен сдержать спецификатор "%d", тот может содержать шаблон даты/времени, по умолчанию "гггг-ММ-дд". **Период ротирования выводится из значения в fileNamePattern**\\
т.е. в данном случае в полночь, если время не указано\\
**Сжатие** - для этого нужно добавить расширение "gz/zip" к имени файла\\
**SizeAndTimeBaseRollingPolicy** - собсна по дате и размеру. Здесь добавляется еще один обязательный токен именования - "%i", обозначающий порядковый номер\\
Пример конфигурации
mylog.txt
true
mylog-%d{yyyy-MM-dd}.%i.txt
100MB
60
20GB
%d{yyyy-MM-dd} %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n