Обратный вход в систему предоставляется из коробки с Spring Boot, когда вы используете одну из зависимостей Spring Boot starter, поскольку они включают spring-boot-starter-logging
, обеспечивающую ведение журнала без какой-либо конфигурации, и при необходимости могут быть изменены для работы по-другому. Существует два способа предоставления вашей собственной конфигурации, если вам нужны только более простые изменения, их можно добавить в файл свойств, такой как application.properties
, или для более сложных нужд вы можете использовать XML или Groovy для указания своих настроек. В этом руководстве мы сосредоточимся на использовании XML для определения пользовательской конфигурации ведения журнала и рассмотрим некоторые основы этого, а также кратко рассмотрим использование файлов свойств для указания простых изменений в стандартной настройке, предоставляемой Spring Boot.
В этом посте я использовал зависимость spring-boot-starter
для подключения spring-boot-starter-logging
, которую можно найти ниже.
org.springframework.boot spring-boot-starter
Это позволит использовать spring-boot-starter-logging
, от которого, в свою очередь, зависят.
ch.qos.logback logback-classic 1.2.3 compile org.apache.logging.log4j log4j-to-slf4j 2.11.1 compile org.slf4j jul-to-slf4j 1.7.25 compile
logback-classic
содержит зависимость logback-core
, и между ними они содержат все, что нам нужно для начала. Версии библиотек, показанные выше, предназначены для версии 2.1.1. ОСВОБОЖДЕНИЕ
пружинного ботинка. Эти зависимости остаются неизменными между версиями Spring Boot, но их собственные версии могут немного отличаться.
Прежде чем мы начнем рассматривать настройку обратного входа, стоит быстро просмотреть, как отправить сообщение в журнал из класса.
@Service public class MyServiceImpl implements MyService { private static final Logger LOGGER = LoggerFactory.getLogger(MyServiceImpl.class); @Override public void doStuff(final String value) { LOGGER.trace("doStuff needed more information - {}", value); LOGGER.debug("doStuff needed to debug - {}", value); LOGGER.info("doStuff took input - {}", value); LOGGER.warn("doStuff needed to warn - {}", value); LOGGER.error("doStuff encountered an error with value - {}", value); } }
РЕГИСТРАТОР
позволяет записывать сообщения в журнал, используя методы, которые представляют каждый уровень ведения журнала, трассировка
, отладка
, информация
, предупреждение
, ошибка
за этим следует сообщение. Фигурные скобки/фигурные скобки будут заменены значением, переданным в качестве параметра метода.
Теперь мы можем приступить к настройке самого входа в систему, начав с относительно простого примера. Ниже приведен logback.xml файл, который является одним из файлов, которые Logback будет искать для настройки его параметров.
%d{dd-MM-yyyy HH:mm:ss.SSS} %magenta([%thread]) %highlight(%-5level) %logger{36}.%M - %msg%n
Он создает приложение класса ConsoleAppender
, который будет выводить сообщения журнала на консоль, как System.out.print
обычно. Устанавливается шаблон, которому будут соответствовать сообщения журнала, снабженные некоторыми обозначениями, которые заменяются сгенерированными значениями в зависимости от сообщения, отправленного регистратору. Некоторые обозначения были включены в пример, и ниже приведены объяснения того, что каждый из них делает.
%d
– выводит время, в течение которого произошло сообщение журнала, в форматах, которые позволяетSimpleDateFormat
.%поток
– выводит имя потока, в котором произошло сообщение журнала.$-$-уровень
– выводит уровень ведения журнала сообщения журнала.%регистратор{36}
– выводит пакет + имя класса, в котором произошло сообщение журнала. Число внутри скобок представляет максимальную длину пакета + имя класса. Если выходные данные длиннее указанной длины, они будут принимать подстроку первого символа каждого отдельного пакета, начиная с корневого пакета, пока выходные данные не станут меньше максимальной длины. Имя класса никогда не будет уменьшено. Хорошую диаграмму этого можно найти в документах Conversion word/|. %M– выводит имя метода, в котором произошло сообщение журнала (по-видимому, это довольно медленно в использовании и не рекомендуется, если вы не беспокоитесь о производительности или имя метода особенно важно для вас).
%msg– выводит фактическое сообщение журнала.
%n– разрыв строки.
% пурпурный ()– устанавливает цвет вывода, содержащегося в скобках, в пурпурный (доступны другие цвета).
выделить()– устанавливает цвет выходных данных, содержащихся в скобках, в зависимости от уровня ведения журнала (например).
На созданное приложение затем ссылаются в корневом регистраторе. В приведенном выше примере уровень ведения журнала был установлен на INFO (можно использовать нижний или верхний регистр). В результате он выводит только сообщения, определенные на уровне информации журнала или выше (ИНФОРМАЦИЯ, ПРЕДУПРЕЖДЕНИЕ, ОШИБКА).
Доступные уровни ведения журнала в Logback следующие:
- ВЫКЛ. (вывод без журналов)
- ошибка
- предупреждать
- информация
- отлаживать
- след
Возвращаясь к фрагменту, показанному выше, с уровнем регистрации ИНФОРМАЦИИ, в журнал выводятся только сообщения об уровне информации или выше (ПРЕДУПРЕЖДЕНИЕ и ОШИБКА). Так что, если бы мы позвонили MyService.doStuff ("значение")
он сгенерирует следующее (журналы, связанные с spring, были удалены из этого и всех следующих примеров вывода).
28-08-2017 13:32:18.549 [main] INFO com.lankydan.service.MyServiceImpl.doStuff - doStuff took input - value 28-08-2017 13:32:18.549 [main] WARN com.lankydan.service.MyServiceImpl.doStuff - doStuff needed to warn - value 28-08-2017 13:32:18.549 [main] ERROR com.lankydan.service.MyServiceImpl.doStuff - doStuff encountered an error with value - value
Обратите внимание, что, хотя сообщения уровня ТРАССИРОВКИ и отладки были отправлены в регистратор, они не отображались, поскольку они находятся ниже уровня INFO.
Если вы хотите написать эквивалент предыдущего примера кода из application.properties
вы могли бы сделать это следующим образом.
logging.level.root=info logging.pattern.console=%d{dd-MM-yyyy HH:mm:ss.SSS} %magenta([%thread]) %highlight(%-5level) %logger.%M - %msg%n
Когда это делается в такой форме, logback.xml файл не требуется, и, как вы можете видеть, конфигурация немного короче и полезна для более простых настроек.
При использовании Spring Boot предоставляется конфигурация по умолчанию для обратного входа, которая переопределяется при добавлении собственной logback.xml . Если вы хотите включить конфигурацию Spring Boot, вы можете добавить следующее в теги.
Дополнительную информацию об этом см. в разделе Документы по загрузке Spring – Настройка обратного входа для ведения журнала .
Если вы хотите регистрировать сообщения класса на уровне, отличном от корневого, вы можете определить свой собственный регистратор для класса. Это позволит вам установить уровень ведения журнала для этого конкретного класса, а также указать другие свойства, уникальные для этого класса. Ниже показано, как вы определили бы регистратор для одного класса.
Если вы затем продолжите запускать этот фрагмент кода, с все еще определенным корневым регистратором, он будет генерировать выходные данные.
27-08-2017 17:02:10.248 [main] DEBUG com.lankydan.service.MyServiceImpl.doStuff - doStuff needed to debug - value 27-08-2017 17:02:10.248 [main] DEBUG com.lankydan.service.MyServiceImpl.doStuff - doStuff needed to debug - value 27-08-2017 17:02:10.248 [main] INFO com.lankydan.service.MyServiceImpl.doStuff - doStuff took input - value 27-08-2017 17:02:10.248 [main] INFO com.lankydan.service.MyServiceImpl.doStuff - doStuff took input - value 27-08-2017 17:02:10.248 [main] WARN com.lankydan.service.MyServiceImpl.doStuff - doStuff needed to warn - value 27-08-2017 17:02:10.248 [main] WARN com.lankydan.service.MyServiceImpl.doStuff - doStuff needed to warn - value 27-08-2017 17:02:10.248 [main] ERROR com.lankydan.service.MyServiceImpl.doStuff - doStuff encountered an error with value - value 27-08-2017 17:02:10.248 [main] ERROR com.lankydan.service.MyServiceImpl.doStuff - doStuff encountered an error with value - value
Как вы можете видеть, каждое сообщение журнала было сгенерировано дважды, что, вероятно, не то, что вам нужно. Чтобы исправить это, необходимо использовать аддитивность="ложь"
. Неиспользование аддитивности="false"
приведет к тому, что сообщение будет распечатано дважды из-за добавления корневого журнала и добавления уровня класса, которые записываются в журнал. Даже если корневой уровень равен ОШИБКА
, установив уровень класса в отлаживать
он перезаписывает его глобально и приведет к тому, что корневое приложение также выполнит запись на уровень DEBUG
для класса MyServiceImpl
. Ниже показано, как должен выглядеть код с включенным этим свойством.
Другое возможное решение – установить уровень журнала только для класса без записи в журнал (из-за отсутствия определенного приложения), это эквивалентно версии выше, но предполагает, что другое приложение журнала (в данном случае корневое приложение) записывает в журнал, чтобы он работал
Если используется любое из этих решений, результат возвращается к ожидаемому.
27-08-2017 16:30:47.818 [main] DEBUG com.lankydan.service.MyServiceImpl.doStuff - doStuff needed to debug - value 27-08-2017 16:30:47.834 [main] INFO com.lankydan.service.MyServiceImpl.doStuff - doStuff took input - value 27-08-2017 16:30:47.834 [main] WARN com.lankydan.service.MyServiceImpl.doStuff - doStuff needed to warn - value 27-08-2017 16:30:47.834 [main] ERROR com.lankydan.service.MyServiceImpl.doStuff - doStuff encountered an error with value - value
Ведение журнала на уровне класса можно записать в application.properties
, добавив следующее.
logging.level.com.lankydan.service.MyServiceImpl=debug
Ведение журнала на уровне пакета также можно определить, просто используя имя пакета вместо имени класса в теге регистратора.
Дополнительные доказательства можно найти, добавив ведение журнала в один из пакетов springframework, а затем перейдя вместо этого в один из классов. Например
по сравнению с
Выводит совершенно другое количество строк журнала. Может быть, сотни против одной или двух строк, с приложением Spring
журналы содержатся внутри org.springframework.загрузка
журналы.
Вход на уровне пакета в application.properties
выполняется в том же формате, в котором используется пакет вместо имени класса.
logging.level.com.lankydan.service=debug
Свойства могут быть определены, позволяя повторно использовать их через файл конфигурации, что удобно, когда вам нужно пометить выходную папку для журналов, в которую нужно перейти.
Это свойство с именем LOG_PATH
используется в дальнейших примерах и будет использовать каталог DEV_HOME/журналы
где ДОМ_ДЕВУШКИ
– это корневой каталог вашего проекта (по крайней мере, так было в моем случае…). Вероятно, это не лучшее место для сохранения журналов в реальности, но для нужд этого руководства оно подходит. LOG_PATH
– это свойство, которое имеет значение для настройки ведения журнала загрузки Spring по умолчанию, но может быть создано свойство с любым именем. Значение ПУТЬ К ЖУРНАЛУ
затем можно получить доступ ко всей остальной конфигурации, добавив ${LOG_PATH}
.
Эта конфигурация может быть достигнута с помощью application.properties
как LOG_PATH
имеет важное значение в Spring Boot.
logging.path=logs
Это также работает, когда вы определяете свое собственное свойство/переменную, позволяя ссылаться на нее из остальной части вашего кода. Например.
propertyA=value propertyB=${propertyA} # extra configuration if required
${${свойство}
будет заменено значением свойство
, позволяющим свойство
использовать его.
Для сохранения в логи можно использовать файл FileAppender
. Это простое приложение для файлов, которое сохранит все журналы в отдельный файл, который может стать очень большим, поэтому вы с большей вероятностью будете использовать RollingFileAppender
, который мы рассмотрим позже.
${LOG_PATH}/log.log %d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n
В этом нет ничего особенного, он имеет ту же структуру, что и ConsoleAppender
, с добавлением имени файла, в который сохраняются сообщения журнала. Стоит отметить, что я удалил цвета, которые были добавлены к шаблону кодировщика при сохранении в файл, так как он будет содержать символы, которые не предназначены для отображения и будут загромождать файл журнала. Затем на это приложение можно ссылаться так же, как на стандартный вывод
приложение, показанное ранее, что позволяет его фактически использовать. Это будет показано ниже, и в следующих фрагментах кода будет использоваться тот же код.
Следуя из предыдущего application.properties
фрагмента, где ведение журнала.был задан путь
, что фактически приводит к тому, что журналы выводятся в файл (а также в консоль), если другие настройки не были сильно изменены. Поэтому вы могли бы остановиться на этом, но шаблон, записанный в файл, и имя файла не находятся под вашим контролем, если это сделано таким образом. В приведенном ниже примере будет продемонстрирована конфигурация, аналогичная приведенному выше приложению СОХРАНИТЬ В ФАЙЛ
.
logging.pattern.console= logging.path=logs logging.file=${logging.path}/log.log logging.pattern.file=%d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n
Где это отличается от конфигурации XML, так это то, что в примере показано приложение, на которое ссылается регистратор для MyServiceImpl
но приведенный выше фрагмент application.properties
также будет включать корневой регистратор и, следовательно, выводить все сообщения журнала в файл. logging.pattern.console
был добавлен, чтобы остановить его вывод на консоль, чтобы он соответствовал приведенному выше XML-коду (это не кажется хорошим способом сделать это но я не видел другого решения).
RollingFileAppender
сохранит журналы в разные файлы в зависимости от их политики переноса. Это удобно, так как позволяет разделить вывод журнала на различные формы, которые вы можете контролировать. Например, вы можете разделить файлы журналов по дате, чтобы вы могли просматривать ошибки, которые произошли в прошлом в определенные даты, отдельно по размеру файла, чтобы вам не нужно было искать в огромном бесконечном файле или делать и то, и другое, и разделять по дате и размеру. Ниже приведены некоторые фрагменты кода, демонстрирующие политику, о которой мы только что говорили.
Timebasedrolling Policy
создаст новый файл на основе даты. Приведенный ниже код будет создавать новый файл каждый день и добавлять данные к имени файла журнала с помощью обозначения %d
. Формат обозначения %d
важен, так как из него выводится период времени переноса. Приведенный ниже пример будет опрокидываться каждый день, но для опрокидывания ежемесячно вместо этого может использоваться другой шаблон %d{ММ-гггг}
, который исключает дневную часть даты. Различные периоды опрокидывания могут использоваться не только ежедневно или ежемесячно из-за предполагаемого периода, если формат внутри обозначения %d
соответствует тому, что позволяет SimpleDateFormat
.
${LOG_PATH}/log.log %d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n ${LOG_PATH}/archived/log_%d{dd-MM-yyyy}.log 10 100MB
Я включил некоторые свойства, доступные для timebasedrolling Policy
в приведенном выше примере. максимальная история
указывает, как долго будут храниться архивированные файлы журнала, прежде чем они будут автоматически удалены. Время, в течение которого они хранятся, зависит от периода времени переноса, указанного в имени файла, поэтому в приведенном выше примере период переноса составляет ежедневно, что позволяет хранить архивированные журналы максимум за 10 дней до их удаления.
ограничение общего размера
ограничивает максимальный размер всех архивированных файлов журналов, для этого требуется, чтобы свойство maxHistory
было установлено с maxHistory
, имеющим приоритет над
Эта конфигурация выходит за рамки того, что можно сделать внутри файла application.properties
, то же самое можно сказать и о следующих примерах. Хотя конфигурация по умолчанию позволяет переносить файл журнала, когда он достигает 10 МБ, и позволяет хранить до 7 архивированных файлов журнала.
Для переноса только на размер файла политика переноса Необходимо использовать политику Fixedwindowrolling
и политику запуска SizeBasedTriggeringPolicy
. В предыдущем примере журналы были сохранены в папке архива при переносе, но для этой политики я не сохранил их как таковые, поскольку разделение журналов в основном предназначено для облегчения их просмотра из-за меньших размеров файлов.
${LOG_PATH}/log.log %d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n ${LOG_PATH}/log_%i.log 2 3 1KB
Необязательные свойства minIndex
и maxIndex
найдено в fixedwindowrollingполитике
укажите минимальное и максимальное значение, которое %i
может принимать имена файлов журнала. Поэтому в приведенном выше примере, когда журналы переворачиваются, они могут принимать имя Поэтому в приведенном выше примере, когда журналы переворачиваются, они могут принимать имя
и log_3.log
(хотя начинать с 2 странно и включено только для ясности, обычно это начинается с 1). Процесс создания файлов журнала выглядит следующим образом (используя приведенный выше фрагмент кода в качестве примера); файл log.log
будет принимать все новые входные данные журнала и при достижении максимального размера файла
/log.log переименовывается в архивированный файл
log_2.log и создается новый
log.log файл, когда
log_2.log также достигнет максимального размера, все файлы журналов переименовываются и сдвигаются вместе с новым
log.log файлом, который создается снова. Этот процесс будет продолжаться, если
maxIndex не задан, но когда это файл журнала с указанным максимальным индексом удаляется (он содержит самые старые сообщения) в момент, когда должен быть создан другой архивный файл. Следуя тому же примеру выше, это означает, что
log_4.log должен быть создан
log_3.log вместо этого удаляется, а все остальные журналы соответственно переименовываются.
Если вас смущает то, что я написал выше о том, как переносятся файлы, не волнуйтесь, так как даже я думаю, что после написания этого объяснения это можно было бы сделать лучше. Поэтому ниже я предпринял вторую попытку проиллюстрировать, как это работает (что, надеюсь, легче понять).
log.log
достигнут максимальный размер файла ->log.log
переименован вlog_2.log
и новыйжурнал.журнал
создаетсяlog_2.log
достигнут максимальный размер файла ->log_2.log переименован в log_3.log
,log.log
назван вlog_2.log
и новыйжурнал.журнал
создаетсяlog_3.log
достигнут максимальный размер файла ->log_3.log
достигнут максимальный размер файла ->удаляется,
log_2.log
переименован вlog_3.log
,log.log
переименован вlog_2.log
и новый
Если я все еще плохо справился с объяснением вам этого процесса, то посмотрите Документы FixedWindowRollingPolicy , которые, надеюсь, помогут вам в этом, если я потерпел неудачу…
Политика изменения размера и времени
использует части обоих приведенных выше примеров, позволяя ей изменять размер и время. Обратите внимание, что он использует как %d
и % i
обозначение для включения данных и номера журнала соответственно в имя файла.
${LOG_PATH}/log.log %d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n ${LOG_PATH}/archived/log_%d{dd-MM-yyyy}_%i.log 10MB 10 100MB
Как вы можете видеть, он содержит Максимальный размер файла
, Максимальная история
и ограничение общего размера
обеспечение ИТ-контроля над размером отдельных файлов, а также над коллекцией файлов. Поэтому в приведенном выше примере 10-дневная история будет разделена на файлы размером 10 МБ, и когда общий размер всех файлов достигнет 100 МБ, самые старые файлы будут удалены.
Теперь, когда мы рассмотрели, как определить несколько приложений, которые могут выводиться в консоль или файл, мы можем объединить их для вывода в обе формы одновременно. Просто путем ссылки на несколько приложений в регистраторе.
Итак, теперь этот регистратор будет выводить данные на консоль благодаря STDOUT
, а также в файл с помощью приложения СОХРАНИТЬ В ФАЙЛ
.
Аналогичная конфигурация может быть достигнута с помощью application.properties
. Если вы вернетесь на страницу, вы, возможно, сможете понять, как это сделать самостоятельно, так как в предыдущем примере была добавлена одна дополнительная строка, чтобы предотвратить ее печать на консоль и в файл. Опять же, это будет содержать сообщения журнала от корневого регистратора, а не только MyServiceImpl
, как в приведенном выше фрагменте.
logging.path=logs logging.file=${logging.path}/log.log logging.pattern.file=%d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n
Полезной функцией, предоставляемой Spring Boot при использовании обратной регистрации, является возможность разделения конфигурации между средами. Поэтому, если вы хотите сохранить в файл и распечатать на консоли в своей среде разработки, но печатать в файл только в рабочей среде, это может быть достигнуто с легкостью.
Первый шаг, чтобы заставить это работать, – переименовать logback.xml файл в logback-spring.xml разрешение использования тега Профиль пружины
. В этом теге может быть указано имя, которое можно задать с помощью свойств, переменных среды или параметров виртуальной машины. Ниже показано, как вы можете установить для профиля spring
имя dev
, которое использовалось для представления среды разработки.
Для установки в application.properties или в качестве переменной среды
spring.profiles.active=dev
Или в качестве опции виртуальной машины
-Dspring.profiles.active=dev
Теперь при запуске приложения будет использоваться профиль spring
для dev
, в результате чего журналы будут выводиться на консоль и в файл. Если это было затем запущено в производство, для свойства необходимо установить значение prod
, которое изменит конфигурацию на то, что считается подходящим, например, только запись журналов в файл и, возможно, изменение уровня ведения журнала всех или определенных классов/пакетов.
Аналогичная конфигурация также может быть предоставлена через application.properties
. Ну, на самом деле нет application.properties
но вместо этого из application-dev.свойства
и приложение-prod.свойства
которые являются отдельными файлами свойств для каждой среды. В соответствии с соглашением об именовании приложения-{среда}.свойства
где {среда}
заменяется именем среды. В зависимости от параметров вашей виртуальной машины или переменных среды один из них может быть выбран точно так же, как при использовании springProfile
в logback-spring.xml . Ниже приведены эквивалентные конфигурации для приведенного выше фрагмента кода.
Ниже приведены эквивалентные конфигурации для приведенного выше фрагмента кода.
logging.level.root=info logging.level.com.lankydan.service=debug logging.path=logs logging.file=${logging.path}/log.log logging.pattern.file=%d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n logging.pattern.console=%d{dd-MM-yyyy HH:mm:ss.SSS} %magenta([%thread]) %highlight(%-5level) %logger.%M - %msg%n
Ниже приведены эквивалентные конфигурации для приведенного выше фрагмента кода.
logging.level.root=info logging.level.com.lankydan.service=error logging.path=logs logging.file=${logging.path}/log.log logging.pattern.file=%d{dd-MM-yyyy HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%M - %msg%n logging.pattern.console=
Я думаю, что мне следует завершить этот пост на этом этапе, так как он был намного длиннее, чем я первоначально ожидал. При этом, как говорится, с помощью Logback и Spring Boot можно сделать гораздо больше, о чем я здесь не рассказывал. В заключение этого урока вы должны были понять, как использовать обратную связь с Spring Boot, в том числе как использовать файлы свойств для изменения настроек по умолчанию, предоставляемых Spring Boot, и как пойти еще дальше и создать свои собственные конфигурации с помощью обратной связи через logback.xml и logback-spring.xml . А также иметь представление об ограничениях, которые может предоставить конфигурация внутри файлов свойств, чтобы вы знали, когда пришло время переключиться на прямое использование Logback, чтобы добраться до финиша.
Код, используемый в этих примерах, можно найти на моем GitHub .
Чтобы быть в курсе моих новых постов, вы можете следить за мной по адресу @Lankydandev .
Первоначально опубликовано 31 августа 2017 года и обновлено 9 января 2019 года.
Оригинал: “https://dev.to/lankydandev/configuring-logback-with-spring-boot-4lnk”