— А, вот ты где! Ты не забыл, что у нас сегодня еще одна лекция?
— Нет, я как раз тебя искал. Почти…
— Отлично, тогда начнем. Сегодня я хочу рассказать тебе про логирование.
Лог – это список произошедших событий. Почти как «морской журнал» или «дневник». Ну, или Твиттер – кому что ближе. А, соответственно, логгер — это объект, с помощью которого можно вести логирование.
В программировании принято логировать практически все. А в Java – так вообще все и даже немного больше.
Дело в том, что Java-программы – это очень часто большие серверные приложения без UI, консоли и т.д. Они обрабатывают одновременно запросы тысяч пользователей, и нередко при этом возникают различные ошибки. Особенно, когда разные нити начинают друг другу мешать.
И, фактически, единственным способом поиска редко воспроизводимых ошибок и сбоев в такой ситуации есть запись в лог/файл всего, что происходит в каждой нити.
Чаще всего в лог пишется информация о параметрах метода, с которыми он был вызван, все перехваченные ошибки, и еще много промежуточной информации.
Чем полнее лог, тем легче восстановить последовательность событий и отследить причины возникновения сбоя или ошибки.
Иногда логи достигают нескольких гигабайт в сутки. Это нормально.
— Нескольких гигабайт? О_о
— Ага. Чаще всего при этом, лог-файлы автоматически архивируются, с указанием дня – за какой день это архив с логом.
— Ничего себе.
— Ага. Изначально в Java не было своего логгера, что привело к написанию нескольких независимых логгеров. Самым распространенным из них стал log4j.
Спустя несколько лет, в Java все же был добавлен свой логгер, но его функциональность была гораздо беднее и большого распространения он не получил.
Факт, как говорится, на лицо – в Java есть официальный логгер, но все сообщество Java-программистов предпочитает пользоваться другим.

На основе log4j потом было написано еще несколько логгеров.
А затем для них всех был написан специальный универсальный логгер slf4j, который сейчас повсеместно используют. Он очень похож на log4j, поэтому я расскажу тебе логирование на его примере.
Весь процесс логирования состоит из трех частей.
Первая часть – это сбор информации.
Вторая часть – это фильтрование собранной информации.
Третья часть – это запись отобранной информации.
Начнем со сбора. Вот типичный пример класса, который ведет лог:
class Manager
{
private static final Logger logger = LoggerFactory.getLogger(Manager.class);
public boolean processTask(Task task)
{
logger.debug("processTask id = " + task.getId());
try
{
task.start();
task.progress();
task.complete();
return true;
}
catch(Exception e)
{
logger.error("Unknown error", e);
return false;
}
}
}
Обрати внимание на слова, выделенные красным.
Строка 3 – создание объекта logger. Такой статический объект создают практически в каждом классе! Ну, разве что кроме классов, которые ничего не делают, а только хранят данные.
LoggerFactory – это специальный класс для создания логгеров, а getLogger – это его статический метод. В него обычно передают текущий класс, хотя возможны различные варианты.
Строка 7 – в логгер пишется информация о вызове метода. Обрати внимание – это первая строчка метода. Только метод вызвался – сразу пишем информацию в лог.
Мы вызываем метод debug, это значит, что важность информации «уровня DEBUG». Этот факт используется на уровне фильтрации. Об этом я расскажу через пару минут.
Строка 17 – мы перехватили исключение и… сразу же записали его в лог! Именно так и нужно делать.
На этот раз мы вызываем метод error, что сразу придает информации статус «ERROR»

— Пока вроде все ясно. Ну, насколько это может быть ясно в середине разговора.
— Отлично, тогда перейдем к записи фильтрации.
Обычно, у каждого лог-сообщения есть своя степень важности, и, используя ее можно часть этих сообщений отбрасывать. Вот эти степени важности:
Степень важности | Описание |
---|---|
ALL | Все сообщения |
TRACE | Мелкое сообщение при отладке |
DEBUG | Сообщения важные при отладке |
INFO | Просто сообщение |
WARN | Предупреждение |
ERROR | Ошибка |
FATAL | Фатальная ошибка |
OFF | Нет сообщения |
Эти уровни используются еще и при отсеве сообщений.
Скажем, если выставить уровень логирования в WARN, то все сообщения, менее важные, чем WARN будут отброшены: TRACE, DEBUG, INFO.
Если выставить уровень фильтрации в FATAL, то будут отброшены даже ERROR’ы.
Есть еще два уровня важности, которые используются при фильтрации – это OFF – отбросить все сообщения и ALL – показать все сообщения (не отбрасывать ничего).
— А как настраивать фильтрацию и где?
— Сейчас расскажу.
Обычно настройки логгера log4j задаются в файле log4j.properties.
В этом файле можно задать несколько appender’ов – объектов, в которые будут писаться данные. Есть источники данных, а есть – аппендеры – противоположные по смыслу объекты. Объекты, куда как бы «стекают» данные, если их можно представить в виде воды.
Вот тебе несколько примеров:
# Root logger option
log4j.rootLogger=INFO, stdout
# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}
Строки 1 и 4 – это комментарии
Строка 2 – мы указываем уровень сообщений, которые оставляем. Все менее важные уровни будут отброшены (DEBUG,TRACE)
Там же, через запятую, мы указываем имя объекта (сами придумываем), куда будет писаться лог. В строках 5-8 идут его настройки.
Строка 5 – указываем тип апендера – консоль (ConsoleAppender).
Строка 6 – указываем, куда именно будем писать – System.out.
Строка 7 – задаем класс, который будет управлять шаблонами записей – PatternLayout.
Строка 8 – задаем шаблон для записи, который будет использоваться. В примере выше это дата и время.
А вот как выгладит запись в файл:
# Root logger option
log4j.rootLogger=INFO, file
# Direct log messages to a log file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=C:\\loging.log
log4j.appender.file.MaxFileSize=1MB
log4j.appender.file.MaxBackupIndex=1
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern= %-5p %c{1}:%L - %m%n
Строка 2 задает уровень фильтрации сообщений и имя объекта-апендера (стока).
Строка 5 – указываем тип апендера – файл (RollingFileAppender).
Строка 6 – указываем имя файла – куда писать лог.
Строка 7 – указываем максимальный размер лога. При превышении размера, начнет писаться новый файл.
Строка 8 – указываем количество старых файлов логов, которые надо хранить.
Строки 9-10 – задание шаблона сообщений.
— Я не знаю, что тут происходит, но догадываюсь. Что не может не радовать.
— Это отлично. Тогда вот тебе пример, как писать лог в файл и на консоль:
# Root logger option
log4j.rootLogger=INFO, file, stdout
# Direct log messages to a log file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=C:\\loging.log
log4j.appender.file.MaxFileSize=1MB
log4j.appender.file.MaxBackupIndex=1
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern= %-5p %c{1}:%L - %m%n
# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}
— Ага, оказывается и так можно? Это же отлично!
— Ага. Ты можешь объявить сколько угодно апендеров и настроить каждый из них по-своему.
Более того, каждому апендеру можно очень гибко настроить фильтр его сообщений. Мы можем не только задать каждому апендеру свой уровень фильтрации сообщений, но и отфильтровать их по пакетам! Вот для чего надо указывать класс при создании логгера (я про LoggerFactory.getLogger).
Пример:
# Root logger option
log4j.rootLogger=INFO, file, stdout
# Direct log messages to a log file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.threshold=DEBUG
log4j.appender.file.File=C:\\loging.log
log4j.appender.file.MaxFileSize=1MB
log4j.appender.file.MaxBackupIndex=1
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern= %-5p %c{1}:%L - %m%n
# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.threshold=ERROR
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}
log4j.logger.org.springframework=ERROR
log4j.logger.org.hibernate=ERROR
log4j.logger.com.javarush=DEBUG
log4j.logger.org.apache.cxf=ERROR
Строки 6 и 15 – мы задаем свой уровень фильтрации для каждого апендера.
Строки 20-23 мы указываем имя пакета и тип фильтрации его сообщений. «log4j.logger» — это префикс, имя пакета выделено оранжевым.
— Ничего себе? Даже так можно. Ну, круто!
— Кстати, ни log4j, ни slf4j не входят в JDK, скачивать их надо отдельно. Это можно сделать вот тут. Но есть и второй способ:
Шаг 1. Добавляешь в класс импорты:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Шаг 2. Становишься курсором на эти строчки и нажимаешь Alt+Enter в Intellij IDEA
Шаг 3. Выбираешь пункт File jar on web.
Шаг 4. Выбираешь – slf4j-log4j13.jar
Шаг 5. Указываешь, куда скачать библиотеку (jar)
Шаг 6. Пользуешься нужными тебе классами.
— Ничего себе! Да что же сегодня за день-то такой. Столько нового и столько классного!
— Вот тебе еще хорошая статья по логингу: http://habrahabr.ru/post/113145/
Ладно, все. Иди отдыхай, программист.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ