Уровень логирования что это
Уровень логирования что это
Логирование в Java
Хотя правильнее было бы говорить наверное журналирование/протоколирование и вести журнал/протокол соответственно.
Но так никто никогда не говорит, конечно ¯\(ツ)/¯
Поэтому «логи всякие нужны, логи всякие важны».
Все ли нужно логировать?
Если вы вдруг залогируете в общий файл-лога пароль и логин пользователя, то никто такому рад не будет, что подводит нас к мысли, что логировать надо тоже с умом.
Т.е возникает требование управления информацией, которая нам нужна в данный момент, а также форматом ее вывода.
При этом, логично, что изменения этого формата и того, что мы хотим видеть в логе не должны требовать перекомпиляции всего проекта или изменения кода.
Уровень логирования | Описание |
---|---|
ALL | Все сообщения |
TRACE | Сообщение для более точной отладки |
DEBUG | Дебаг-сообщение, для отладки |
INFO | Обычное сообщение |
WARN | Предупреждение, не фатально, но что-то не идеально |
ERROR | Ошибка |
FATAL | Фатальная ошибка, дело совсем плохо |
OFF | Без сообщения |
Если проиллюстрировать это:
Принципы и понятия
Логер создается с помощью фабрики и на этапе создания ему присваивается имя. Имя может быть любым, но по стандарту имя должно быть сопряжено с именем класса, в котором вы собираетесь что-то логировать:
Почему так рекомендуется делать?
Получается выстраивается следующая иерархия логеров:
И каждому логеру можно выставить свой уровень. Установленный логгеру уровень вывода распространяется на все его дочерние логгеры, для которых явно не выставлен уровень.
При этом во главе иерархии логеров всегда стоит некотрый дефолтный рутовый(корневой) логер.
Поэтому у всех логеров будет уровень логирования, даже если явно мы не прописали для ru.aarexer.example.SomeClass его, то он унаследуется от рутового.
Вопрос:
Логер | Назначенный уровень |
---|---|
root | INFO |
ru | Не назначен |
ru.aarexer | DEBUG |
ru.aarexer.example | Не назначен |
Какой у какого логера будет уровень логирования?
Ответ:
Вспоминаем, что, если уровень логирования не назначен для логера, то он унаследует его от родительского, смотрим на иерархию:
Logger | Назначенный уровень | Уровень, который будет |
---|---|---|
root | Все сообщения | INFO |
ru | Не назначен | INFO |
ru.aarexer | DEBUG | DEBUG |
ru.aarexer.example | Не назначен | DEBUG |
Это событие по сути состоит из двух полей:
Аппендер – это та точка, куда события приходят в конечном итоге. Это может быть файл, БД, консоль, сокет и т.д.
У одного логгера может быть несколько аппендеров, а к одному аппендеру может быть привязано несколько логгеров.
Логеры при этому наследуют от родительских не только уровни логирования, но и аппендеры.
Например, если к root-логгеру привязан аппендер A1, а к логгеру ru.aarexer – A2, то вывод в логгер ru.aarexer попадет в A2 и A1, а вывод в ru – только в A1.
Вопрос:
Пусть у нас есть несколько аппендеров и логеров
Logger | Appender |
---|---|
root | А1 |
ru.aarexer | А2 |
ru.aarexer.example.SomeClass | А3 |
В какой аппендер попадет лог-сообщение:
Ответ:
Это говорит о том, что логер-наследник будет свои события передавать логеру-родителю.
Смотрим на иерархию:
Из всего вышесказанного делаем вывод, что событие «hello» с уровнем Level.INFO попадет во все три аппендера.
Но такое наследование аппендеров можно отключить через конфигурацию, для этого стоит посмотреть в сторону выставления флага additivity=»false» на логгерах.
Т.е как лог-сообщения будут отформативарованы, соответственно тут у каждой библиотеки свой набор доступных форматов.
Библиотеки логирования в Java
Однако он не отвечает всем тем требованиям, которые мы сформулировали выше, поэтому рассмотрим альтернативы.
Наиболее популярные библиотеки логирования в Java :
И поэтому появились еще две библиотеки:
Это самая первая библиотека логирования, появилась еще в 1999 году.
Поддерживает большое количество способов вывода логов: от консоли и файла до записи в БД.
Благодаря подобной иерархии лишнее отсекается и поэтому логер работает быстро.
Проект сейчас не развивается и по сути заброшен, с версией Java 9 уже не совместим.
Вклад log4j в мир логирования настолько велик, что многие идеи были взяты в библиотеки логирования для других языков.
Чувствуете насколько все переосложнено?
Так как стандартных средств форматирования логов недостаточно, то все сводилось к тому, что писались свои. Это при том, что log4j предоставлял больший функционал, работал как минимум не медленнее и в целом себя чувствовал хорошо.
Уровни логгирования у JCL совпадают с log4j, а в случае взаимодействия с JUL происходит следующее сопоставление:
JCL | JUL |
---|---|
ALL | Все сообщения |
TRACE | Level.FINEST |
DEBUG | Level.FINE |
INFO | Level.INFO |
WARN | Level.WARNING |
ERROR | Level.SEVERE |
FATAL | Level.SEVERE |
OFF | Без сообщения |
С уверенностю можно сказать сейчас, что в эту сторону даже смотреть не стоит. Пациент мертв.
Лицензия | Apache License Version 2.0 |
Последняя версия | 1.2 |
Дата выпуска последней версии | июль 2014 |
Но добавили много нового, парочка из них:
Правда перестали поддерживать properties конфигурации и конфигурации от log4j на xml надо было переписывать заново.
Лицензия | Apache License Version 2.0 |
Последняя версия | 2.11.2 |
Дата выпуска последней версии | февраль 2019 |
Лицензия | MIT License |
Последняя версия | 2.0.0-alpha0 |
Дата выпуска последней версии | июнь 2019 |
При этом, logback не является частью Apache или еще какой-то компании и независим.
Лицензия | EPL/LGPL 2.1 |
Последняя версия | 1.3.0-alpha4 |
Дата выпуска последней версии | февраль 2018 |
Так как из адаптеров это по сути единственный выбор, да и встречается slf4j все чаще, то стоит рассмотреть его устройство.
Вопрос:
Ответ:
Вроде все проблемы решены, пусть и такими радикальными способоами.
Таким образом, чтобы работать со Spring получается надо сделать CLASSPATH подобным образом:
Если конфигурация сделана программно, то bridge не будет работать.
Бесконтрольно подтягиваемые транзитивные зависимости библиотек, которые вы используете в своем проекте, рано или поздно принесут какие-то свои библиотеки логирования, отчего могут открыться врата прямиком в ад.
При этом, если вы разрабатываете библиотеку, то:
Очень важно также не забывать о том, что такое логирование и для чего оно нужно.
Поэтому нельзя скатываться в бесмысленные записи в лог, вывод личных данных и так далее.
Думайте о том что вы пишите в лог!
По следам происшествия: как логировать эффективно
Людям свойственно ошибаться. Это относится не только к разработчикам, но и к пользователям. В ходе разработки мы контролируем процесс и можем разобраться в неправильном поведении программы простой отладкой. А вот расследовать случай, который произошёл в production-окружении, не всегда просто. В таких ситуациях на помощь приходят журналы. И чтобы от них действительно была польза, их нужно вести правильно.
Логирование — это процесс ведения таких журналов. Помогает обнаружить скрытые ошибки, разобраться в проблемах пользователей и просто понять, что произошло на самом деле. В простейшей реализации такие журналы пишутся в текстовом файле и содержат точное время и описание произошедшего события. В логировании есть множество подходов и давно определены лучшие практики — это хорошо для нас.
В этой статье разберёмся, как правильно организовать ведение журналов в PHP-приложении, как эффективно с ними взаимодействовать и какие библиотеки и инструменты могут быть полезны.
Стандарт PSR-3. Уровни логирования
PSR — это свод рекомендаций для PHP-разработчиков. Он содержит советы по оформлению кода, некоторые интерфейсы и другие рекомендации. Один из его документов (PSR-3) посвящён реализации логера.
Знакомство с этими рекомендациями предлагаю начать с уровней логирования, которые в них предлагаются.
PSR-3 определяет 8 уровней сообщений. Если их использовать правильно, искать ошибки станет проще и получится оперативнее реагировать на инциденты. Давайте разберёмся, как выбрать уровень:
Чтобы использовать эти уровни, достаточно добавлять их название к строке каждой записи журнала. Например:
На уровни ALERT и EMERGENCY часто ставят дополнительное информирование, например по SMS. По INFO можно легко восстановить последовательность действий пользователя, по DEBUG — узнать точные значения переменных, результат работы функции в определённом месте и прочее.
PSR-3. Интерфейс для класса-логера
Помимо класса с уровнями, PSR-3 предлагает нам интерфейс для реализации собственных логеров — LoggerInterface. Соблюдать его очень полезно, так как большинство существующих библиотек его поддерживает. Если вы решите заменить свой логер на другой, просто подключите вместо него новый класс.
LoggerInterface требует реализации методов ведения журнала — и чтобы она учитывала уровни, которые мы разобрали выше. Создадим собственный класс-логер, который будет соответствовать этому интерфейсу и делать записи в файл.
Для начала загрузим код стандарта PSR-3 с помощью Composer.
В загруженном пакете содержится несколько классов, трейтов и интерфейсов. Среди них — LogLevel, который мы разобрали выше, и интересующий нас в данный момент LoggerInterface. Давайте создадим новый класс, реализующий этот интерфейс. Важно: убедитесь, что у вас подключён класс-автозагрузчик (vendor/autoload.php).
Класс мы создали. Но чтобы он удовлетворял требованиям стандарта, нужно написать все методы, описанные в интерфейсе. Самый важный из них — log. В нём будет указана основная логика записи в файл.
Для полного удовлетворения интерфейса LoggerInterface нам осталось написать реализацию для методов emergency, alert, critical, error, warning, notice, info и debug, которые соответствуют уровням (их мы разобрали выше). Их реализация сводится к очень простому принципу: мы вызываем метод log, передав в него необходимый уровень.
Использование логера
Теперь, когда наш класс реализует интерфейс, предложенный стандартом PSR-3, мы можем легко задействовать его в любом месте. Например, в файле index.php:
Или в любом другом классе.
Обратите внимание: в качестве типа аргумента конструктора мы указываем не конечную реализацию (FileLogger), а именно интерфейс стандарта PSR-3. Это удобно, потому что позволяет легко заменять применяемый логер на любой другой, поддерживающий этот интерфейс.
Контекст
Контекст предназначен для передачи вспомогательной и зачастую динамичной информации. Например, если вы делаете отладочную запись (уровень debug), можно передать в контекст значение переменной.
Теперь в любом месте вызова логера мы можем передать вторым аргументом массив дополнительной информации.
В результате мы получим запись следующего вида:
Касательно контекста действует одно простое правило: любая динамическая информация должна передаваться в нём, но не в сообщении. То есть, если вы формируете сообщение в лог с помощью sprintf или конкатенацией строчных переменных, — скорее всего, эту информацию можно вынести в контекст. Соблюдая это правило, проще искать что-то в журнале, потому что не приходится предугадывать (или вычислять) значения переменных.
Библиотека Monolog
Несмотря на всю простоту принципа ведения журналов, в этой области широкий простор для модификаций. Мы могли бы поддержать другие форматы записей, реализовать отправку SMS или элементарно дать возможность менять имя конечного файла логов.
Здорово, что всё это уже реализовано в большинстве библиотек. Одна из самых распространённых – monolog.
Среди весомых преимуществ этого пакета:
Чтобы начать использовать этот прекрасный инструмент, установим его с помощью Composer.
Использование Monolog
Работа библиотеки monolog основывается на обработчиках. Они позволяют задавать конкретное поведение в ответ на события логирования. Например: запись в файл — это специальный обработчик, который называется StreamHandler. Давайте заменим использование нашего класса на загруженную библиотеку.
Если мы запустим этот код, в файле gb.log появится запись следующего вида:
Очень похоже на то, что было у нас ранее, кроме добавления имени канала (gb-demo).
Важная особенность обработчиков monolog: им можно задать уровень, на котором они работают. Например, чтобы писать все ошибки в отдельный файл.
Подключённый на уровень ERROR обработчик будет принимать на себя все записи уровня ERROR и выше. Поэтому вызов метода emergency попадает в оба файла: gb.log и errors.log
Такое простое разделение записей по уровням значительно упрощает для нас реагирование на ошибки. Ведь больше не нужно искать их среди всех записей в журнале. Это простая и полезная функция.
Все записи от одного запроса
Когда мы разрабатываем проект, журналы читаются очень просто, они последовательны и ясны. Но когда продуктом пользуются несколько человек, логи могут перемешиваться и больше запутывать, чем помогать. Для решения этой проблемы есть простой трюк. Вместо имени канала (логера) используйте уникальный идентификатор сессии. Получить его можно с помощью встроенной функции session_id(). При этом сессия должна быть обязательно запущена с помощью session_start()
Рассмотрим пример реализации такого приёма:
Что нам даёт такая простая доработка? Важную возможность — группировать все записи запросам пользователя.
Что дальше?
Monolog поддерживает множество полезных готовых обработчиков, на которые стоит обратить внимание:
Заключение
Логирование поможет исправлять ошибки на ранних этапах разработки и быть уверенными, что ничего не сломалось в новой версии кода. А ещё расследовать случаи ваших пользователей и иметь общее видение проекта.
Главное — помнить простые правила:
Людям свойственно ошибаться. Это относится не только к разработчикам, но и к пользователям. В ходе разработки мы контролируем процесс и можем разобраться в неправильном поведении программы простой отладкой. А вот расследовать случай, который произошёл в production-окружении, не всегда просто. В таких ситуациях на помощь приходят журналы. И чтобы от них действительно была польза, их нужно вести правильно.
Логирование — это процесс ведения таких журналов. Помогает обнаружить скрытые ошибки, разобраться в проблемах пользователей и просто понять, что произошло на самом деле. В простейшей реализации такие журналы пишутся в текстовом файле и содержат точное время и описание произошедшего события. В логировании есть множество подходов и давно определены лучшие практики — это хорошо для нас.
В этой статье разберёмся, как правильно организовать ведение журналов в PHP-приложении, как эффективно с ними взаимодействовать и какие библиотеки и инструменты могут быть полезны.
Стандарт PSR-3. Уровни логирования
PSR — это свод рекомендаций для PHP-разработчиков. Он содержит советы по оформлению кода, некоторые интерфейсы и другие рекомендации. Один из его документов (PSR-3) посвящён реализации логера.
Знакомство с этими рекомендациями предлагаю начать с уровней логирования, которые в них предлагаются.
PSR-3 определяет 8 уровней сообщений. Если их использовать правильно, искать ошибки станет проще и получится оперативнее реагировать на инциденты. Давайте разберёмся, как выбрать уровень:
Чтобы использовать эти уровни, достаточно добавлять их название к строке каждой записи журнала. Например:
На уровни ALERT и EMERGENCY часто ставят дополнительное информирование, например по SMS. По INFO можно легко восстановить последовательность действий пользователя, по DEBUG — узнать точные значения переменных, результат работы функции в определённом месте и прочее.
PSR-3. Интерфейс для класса-логера
Помимо класса с уровнями, PSR-3 предлагает нам интерфейс для реализации собственных логеров — LoggerInterface. Соблюдать его очень полезно, так как большинство существующих библиотек его поддерживает. Если вы решите заменить свой логер на другой, просто подключите вместо него новый класс.
LoggerInterface требует реализации методов ведения журнала — и чтобы она учитывала уровни, которые мы разобрали выше. Создадим собственный класс-логер, который будет соответствовать этому интерфейсу и делать записи в файл.
Для начала загрузим код стандарта PSR-3 с помощью Composer.
В загруженном пакете содержится несколько классов, трейтов и интерфейсов. Среди них — LogLevel, который мы разобрали выше, и интересующий нас в данный момент LoggerInterface. Давайте создадим новый класс, реализующий этот интерфейс. Важно: убедитесь, что у вас подключён класс-автозагрузчик (vendor/autoload.php).
Класс мы создали. Но чтобы он удовлетворял требованиям стандарта, нужно написать все методы, описанные в интерфейсе. Самый важный из них — log. В нём будет указана основная логика записи в файл.
Для полного удовлетворения интерфейса LoggerInterface нам осталось написать реализацию для методов emergency, alert, critical, error, warning, notice, info и debug, которые соответствуют уровням (их мы разобрали выше). Их реализация сводится к очень простому принципу: мы вызываем метод log, передав в него необходимый уровень.
Использование логера
Теперь, когда наш класс реализует интерфейс, предложенный стандартом PSR-3, мы можем легко задействовать его в любом месте. Например, в файле index.php:
Или в любом другом классе.
Обратите внимание: в качестве типа аргумента конструктора мы указываем не конечную реализацию (FileLogger), а именно интерфейс стандарта PSR-3. Это удобно, потому что позволяет легко заменять применяемый логер на любой другой, поддерживающий этот интерфейс.
Контекст
Контекст предназначен для передачи вспомогательной и зачастую динамичной информации. Например, если вы делаете отладочную запись (уровень debug), можно передать в контекст значение переменной.
Теперь в любом месте вызова логера мы можем передать вторым аргументом массив дополнительной информации.
В результате мы получим запись следующего вида:
Касательно контекста действует одно простое правило: любая динамическая информация должна передаваться в нём, но не в сообщении. То есть, если вы формируете сообщение в лог с помощью sprintf или конкатенацией строчных переменных, — скорее всего, эту информацию можно вынести в контекст. Соблюдая это правило, проще искать что-то в журнале, потому что не приходится предугадывать (или вычислять) значения переменных.
Библиотека Monolog
Несмотря на всю простоту принципа ведения журналов, в этой области широкий простор для модификаций. Мы могли бы поддержать другие форматы записей, реализовать отправку SMS или элементарно дать возможность менять имя конечного файла логов.
Здорово, что всё это уже реализовано в большинстве библиотек. Одна из самых распространённых – monolog.
Среди весомых преимуществ этого пакета:
Чтобы начать использовать этот прекрасный инструмент, установим его с помощью Composer.
Использование Monolog
Работа библиотеки monolog основывается на обработчиках. Они позволяют задавать конкретное поведение в ответ на события логирования. Например: запись в файл — это специальный обработчик, который называется StreamHandler. Давайте заменим использование нашего класса на загруженную библиотеку.
Если мы запустим этот код, в файле gb.log появится запись следующего вида:
Очень похоже на то, что было у нас ранее, кроме добавления имени канала (gb-demo).
Важная особенность обработчиков monolog: им можно задать уровень, на котором они работают. Например, чтобы писать все ошибки в отдельный файл.
Подключённый на уровень ERROR обработчик будет принимать на себя все записи уровня ERROR и выше. Поэтому вызов метода emergency попадает в оба файла: gb.log и errors.log
Такое простое разделение записей по уровням значительно упрощает для нас реагирование на ошибки. Ведь больше не нужно искать их среди всех записей в журнале. Это простая и полезная функция.
Все записи от одного запроса
Когда мы разрабатываем проект, журналы читаются очень просто, они последовательны и ясны. Но когда продуктом пользуются несколько человек, логи могут перемешиваться и больше запутывать, чем помогать. Для решения этой проблемы есть простой трюк. Вместо имени канала (логера) используйте уникальный идентификатор сессии. Получить его можно с помощью встроенной функции session_id(). При этом сессия должна быть обязательно запущена с помощью session_start()
Рассмотрим пример реализации такого приёма:
Что нам даёт такая простая доработка? Важную возможность — группировать все записи запросам пользователя.
Что дальше?
Monolog поддерживает множество полезных готовых обработчиков, на которые стоит обратить внимание:
Заключение
Логирование поможет исправлять ошибки на ранних этапах разработки и быть уверенными, что ничего не сломалось в новой версии кода. А ещё расследовать случаи ваших пользователей и иметь общее видение проекта.
Главное — помнить простые правила: