Логирование в Java
> как в Java правильно оформить логирование в оффлайн приложениях, на tomcat и в других случаях Используя библиотеку для логгирования, например: — [Logback](logback.qos.ch) — [Log4j](logging.apache.org/log4j/2.x/) — java.util.logging на худой конец
2 июл 2014 в 14:06
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
- «Команды записи в лог» зависят не от приложения, а от используемой библиотеки для логирования.
- Уровни логирования (приоритеты сообщений) выбираются исходя из здравого смысла. Вообще имена самих уровней логирования довольно говорящие. Например на уровне ERROR — пишем ошибки, из-за которых невозможно дальнейшая работа программы, а на уровне TRACE — пишем подробную отладочную информацию.
- Стоит. Одно дело приложение в продакшне, и тогда избыточное логирование не нужно, и совсем другое дело разработка и тестирование. Разница между девелоперским и продакшн окружением при этом будет состоять в конфигурации логера.
Это не единственные вопросы, на которые вы должны обратить внимание. Неизбежно всплывут вопросы совместимости логеров используемых в разных библиотеках, а также проблемы производительности (логирование не бесплатно).
Отслеживать
ответ дан 2 июл 2014 в 14:00
13.2k 1 1 золотой знак 29 29 серебряных знаков 28 28 бронзовых знаков
можно лишь добавить, что некоторые программные комплексы(например, ORM) работают с обобщенным интерфейсом логирования. Я имею ввиду т.н. фасады, например SLF4J. Они позволяют легко установить вашу любимую систему логирования
2 июл 2014 в 14:50
Промышленным стандартом де-факто логгирования в Java является Log4J. Все остальные системы логгирования — это от лукавого, включая java.util.logging , Apache commons-loggins и проч. новомодные LogBack (кстати, последний все равно совместим с Log4J)
Практически все системы поддерживают логгирование в стиле Log4J (я имею ввиду настоящие Java системы, а не Android, который не совсем Java). Если они не поддерживают Log4J — то это скорее всего неправильные системы 🙂
В нормальном приложении управление логами обычно вынесено во внешний конфигурационный файл log4j.properties , так что обычно управление логами: а именно, что выводить и куда выводить сводится к шаманству с файлом log4j.properties . В не самом сложном варианте это может выглядеть так:
# 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=%d %-5p %c:%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 %-5p %c:%L - %m%n
Касаемо вопроса по-поводу уровней логгирования. То негласные договоренности такие:
- Уровень INFO — просто информирование о неком событии
- Уровень DEBUG — используется при отладке
- Уровень WARN — сообщение об ошибке или нестандартной ситуации, которая потенциально опасна
- Уровень ERROR — сообщение об ошибке, после которой работа программы все еще возможна
- Уровень FATAL — сообщение об ошибке, после которой нормальная работа программы невозможна. Обычно после этого работа программы прекращается.
Update
1) Как уже было сказано в комментариях — я видимо слегка погорячился (слегка). Тем не менее пром. стандарт это логгинг в стиле Log4J — это сомнению не подвергается. Просто надо помнить что Log4J имеет несколько форков: один старый добрый классический Log4J, второй Log4J v.2, далее есть новомодные LogBack и SLF4J.
2) Часто возникает вопрос (скорее даже не вопрос, а холивар): насколько логгирование влияет на производительность. Я лично, решаю этот вопрос для себя так:
Где-то объявляю константу (допустим в классе MyMain ):
public final static boolean DEBUG=true; //false при выходе на продакшн
и далее в местах, где идет отладочное логгирование пишу:
if(MyMain.DEBUG) logger.debug(…);
При выходе на продакшн DEBUG устанавливаем false и все. Компилятор сам уже уберет из кода логи обернутые if(DEBUG) — все довольны.
Удобное логирование в SpringBoot + Log4j2 + Maven
Изложенный в статье материал предназначен для новичков и, возможно, как и мне сэкономит несколько часов поисков на StackOverFlow и других сайтах с целью получить удобную систему логирования, которая сама поймет куда логировать — в консоль, файл или logstash.
На старте проекта всегда возникает задача правильно настроить логирование, при этом на локальном окружении логи должны выводиться в консоль и в файл для удобства в процессе отладки, а на удаленном сервере выводить логи в консоль крайне не желательно, но вместо этого их надо писать в файл, logstash или в БД. А если у вас локально Windows, а на удаленной машине Linux, то отличается и адрес расположения этого файла с логами.
Таким образом возникает немало ситуаций, в которых нужно руками постоянно править конфигурацию системы логирования в зависимости от внешних обстоятельств.
Постоянно помнить и комментировать appender’ы в своё время мне надоело и я выработал способ настройки Log4j2 так, чтобы в зависимости от выбранного Maven-профиля автоматически включались только нужные appender’ы.
Ниже приведена инструкция по настройке проекта с использованием Spring Boot + Maven + Log4j2, результатом выполнения которой будет настроенная система логирования и два appender’а: CONSOLE и SOCKET.
В первую очередь внесём изменения в конфигурацию Maven (pom.xml):
В переменные на уровне всего pom.xml добавляем адрес host для logstash-appender:
logstashcsm.example.ru
Добавляем необходимые для работы зависимости:
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-logging org.springframework.boot spring-boot-starter-log4j2
Обратите внимание, что из spring-boot-starter-web мы исключаем зависимость spring-boot-starter-logging.
Настроим Maven-профили, чтобы динамически управлять подключенными appender’ами. На уровне каждого профиля задаем переменные logstash.port, logger.console.tresholdFilter, logger.socket.tresholdFilter .
local 10000 ALL OFF dev 10001 OFF ALL
logstash.port — порт, на который нужно отправлять логи.
logger.console.tresholdFilter — значение задаёт уровень фильтрации логов, выводимых на консоль. В нашем случае ALL означает, что лог-записи всех уровней будут выводиться в консольный аппендер.
logger.socket.tresholdFilter — значение задает уровень фильтрации логов, которые отправляются в logstash. OFF — означает, что никакие записи отправлены в этот аппендер не пройдут.
Теперь нам необходимо внести изменения в application.properties, чтобы из файла Log4j2.xml можно было получить доступ к значению переменных, указанных в pom.xml:
logstash.host=@logstash.host@
logstash.port=@logstash.port@
logger.console.tresholdFilter=@logger.console.tresholdFilter@
logger.socket.tresholdFilter=@logger.socket.tresholdFilter@
И, наконец, настраиваем конфигурацию самого Log4j2 в файле log4j2.xml:
$ $ $ $ "/> - %m%n"/> "/>
Теперь, чтобы вызвать логер в своём классе необходимо создать его экземпляр:
private static Logger logger = LoggerFactory.getLogger(YourClass.class);
и обратиться к нему:
logger.info("Эта запись будет залогирована");
Вот и всё. Теперь логирование будет работать с учетом Maven-профиля, активного в проекте.
Обратите внимание, что в листинге файла log4j2.xml определено два логера — ru.example и root , и у них разные уровни логирования событий. Первый будет работать на все события, порожденные классами из пакета ru.example.* и логировать всё от уровня DEBUG, а root логер будет фиксировать события из абсолютно всех классов, но с уровня ERROR.
При этом, чтобы записи не дублировались в логах, используется настройка additivity=»false» .
Логирование Java: терминология, уровни логирования, log-файлы
Логирование Java — это процесс , при котором программа на Java-языке записывает сведения о своем исполнении в некий файл или базу данных. Логирование дает возможность отслеживать ход исполнения программы и конкретно кода.
Иногда с вашим программным продуктом происходит что-то непонятное , и он начинает себя вести не так , как задумано. Первое , что приходит на ум в этом случае , — в программе есть наличие ошибок. Самый п р остой способ это проверить — использовать логи программы, так как это возможность посмотреть, что происходит «внутри» самой программы.
Логирование Java напоминает процесс работы «черного ящика» в самолете — в случае возникновения критических ситуаций оно способно «рассказать», что не так работает и на что обратить внимание.
Термин «лог» — что это такое?
Логирование — это процесс, который неразрывно связан с термином «лог». Лог с английского можно перевести как «бортовой журнал».
В программировании лог — это специальный файл, который выполняет функцию «бортового журнала» программы. Именно в этот файл, а точнее , в лог программа производит записи о своей работе. Лог-файлы программа может создавать самостоятельно, чтобы вносить туда текстовые пометки.
Лог-файлы помогают «следить» за действиями программы, например, что она функционирует в конкретный момент времени или как она реагирует на действия пользователя.
У одного программного продукта лог-файлы могут быть разные. Например , может быть лог — файл типа:
- «access_log», в котором фиксируются действия программы при ее взаимодействии с пользователями;
- «error_log», в котором фиксируются все ошибки, произош едшие в результате работы программы;
- и др.
В одном лог-файле может быть множество записей, где каждая строчка будет содержать отдельные результаты для каждого взаимодействия с программой. То есть в каждой записи будет информация о том , что происходило с программным продуктом в конкретный момент времени.
Отметим различия между «логированием» и «логом»:
- логирование — это процесс, при котором программа прописывает какие-то записи в лог-файлы;
- лог — это сам файл или то место , куда программа производит необходимые записи.
Уровни логирования
Мы выяснили, что такое логи и что такое логирование Java. Не трудно догадаться, что если в лог-фай л записывать все действия программы, то там будет большое количество различных сведений. В некоторых ситуациях лог-файлы могут генерироваться очень быстро и в огромных размерах. В этом случае найти нужную информацию в логах будет очень не легко. Поэтому, чтобы контролировать объемы записываемой информации, придумали различные уровни логирования.
Уровни логирования применяются в программах на различных языках программирования, в том числе и на Java. Различают несколько основных уровней:
- debug — выводится информация, которая пригодится для отладки программы;
- info — обычные и стандартные сообщения;
- warning — нефатальное предупреждение;
- error — записи ошибок;
- fatal — фатальная ошибка;
- off — сообщения не выводятся;
- trace — информация для точной отладки;
- all — выводится вся информация.
«Поддержать» уровни логирования в Java можно двумя способами:
- Внутри программы можно расставить вызов нужной библиотеки в соответствии с заданным уровнем. Например, если произойдет ошибка, она будет логироваться как «error» и т. д.
- В момент запуска программы нужно указать уровень логирования для конкретной ситуации. Если ничего не указывать, то для программы будет применяться уровень «info», где буд у т записываться все ключевые и важные события, происходящие с программой, в том числе «warning» и «error». Если указать «error», будут записываться только ошибки.
Логирование Java: термины
Библиотеки логирования Java включают в себя 3 основных термина:
- Logger — это некий объект, который отвечает за запись информации в лог-файлы, опираясь на заданные уровни логирования. Основная задача логгера — не пропустить событие, которое нужно записать в лог-файл.
- Appender — это конечная точка, куда «приходит» информация для логирования. В качестве appender мо гут выступать: файл, база данных, консоль, сокет и др. У appender нет каких-либо ограничений, куда записывать сообщения. Все ограничивается только вашими способностями. Если Logger — это начальная точка в логировании, то Appender — это конечная точка. При этом один «логгер» может содержать несколько «аппендеров» и наоборот.
- Layout — это формат, в котором выводятся сообщения. Форматирование сообщений напрямую зависит от используемой библиотеки при логировании.
Библиотеки логирования Java
Библиотеки логирования Java — это набор инструментов, который применяют при логировании программ. Различают несколько популярных инструментов логирования:
- Apache log4j. Это первый набор инструментов для логирования Java, который появился еще в 1999-м году. Внутри себя имеет различны е способы вывода логов, несколько форматов логирования и мн. др. Раньше данная библиотека активно применялась, но уже долгое время этот проект не развивается.
- JUL. Имеет множество уровней логирования, например , только для отладки у этого инструмента есть в арсенале 3 отладочных уровня вместо одного стандартного.
- SLF4J. Этот инструмент является оберткой над многими популярными логгерами, например: logback, log4j, jul и др., поэтому его рекомендуется использовать в паре с полноценной библиотекой для логирования.
- Logback был создан как альтернатива умирающему log4j, поэтому он вобрал в себя все лучшее из этого инструмента, при этом усовершенствовал некоторые показатели.
Заключение
Дочитав статью, вы уже точно знаете , что такое логи, лог-файлы и логирование в Java. Также мы познакомились с основными терминами и инструментами в логиров а нии Java. Подробнее на каждом из них мы остановимся в следующих наших статьях, потому что проблему логирования Java нужно разбирать постепенно. А проблема есть, так как присутствует огромное количество разнообразного инструмента логирования, при этом нет четких стандартов самого логирования.
Мы будем очень благодарны
если под понравившемся материалом Вы нажмёте одну из кнопок социальных сетей и поделитесь с друзьями.
Логирование в Java / quick start
В ходе моей работы в компании DataArt я, в числе прочего, занимаюсь менторской деятельностью. В частности это включает в себя проверку учебных заданий сделанных практикантами. В последнее время в заданиях наметилась тенденция «странного» использования логеров. Мы с коллегами решили включить в текст задания ссылку на статью с описанием java logging best practices, но оказалось, что такой статьи в которой бы просто и без лишних деталей на практике объяснялось бы как надо писать в лог на Java, вот так вот с ходу не находится.
Данная статья не содержит каких-то откровений, в ней не рассматриваются тонкости какого либо из многочисленных java logging frameworks. Здесь рассказываю как записать в лог так, чтобы это не вызвало удивления у Ваших коллег, основная цель написания включить ее в список обязательного чтения для практикантов. Если все еще интересно, читайте дальше
- Весь код примеров использует java.util.logging framework. Вопрос «Какой из фреймворков логирования ниболее кошерен» я оставлю за кадром. Скажу только что до java.util.logging проще всего дотянуться ибо он уже идет вместе с JRE и на самом деле рассказанное в данной статье с минимальными косметическими правками верно для подавляющего большинства систем логирования.
- В целом рецепты приведенные в данной статье не являются единственно верными, есть моменты о которых можно поспорить, но в целом эти рецепты используются многие годы, многими разработчиками, во многих проектах и они достаточно хороши чтобы им следовать если у Вас нет каких-то совсем уже серьезных возражений.
- В статье не рассматриваются такие «продвинутые» топики как:
- Конфигурирование уровней для отдельных логеров
- Форматирования логов
- Асинхронное логирование
- Создание собственных уровней логирования в Log4J
- Контекстное логирование
- И многое другое
Пример №1
Хорошо
public class SomeClass < private static Logger log = Logger.getLogger(SomeClass.class.getName()); public void someMethod() < log.info("Some message"); >.
- Логер это статическое поле класса инициализируемое при загрузке класса, имеет простое, короткое имя, важно чтобы во всех Ваших классах переменная логера называлась одинаково (это диктуется общим правилом, одинаковые вещи в программе должны делаться одинаковым образом).
- В качестве имени логера я использую имя класса, на самом деле это не единственный способ, можно пытаться организовать какую-то свою иерархию логирования (например transport layer/app layer для подсистем имеющих дело с обменом данными), но как показывает практика выдумывать и главное потом неукоснительно следовать такой иерархии крайне сложно, а вариант с именами логеров совпадающими с именами классов весьма хорош и используется в 99% проектов
- Здесь для записи в лог я использую короткий метод .info, а не более общий метод .log, так много лаконичнее
- Имя логера берется как SomeClass.class.getName(), а не как «com.dataart.demo.java.logging.SomeClass», оба способа по идее одинаковы, но первый защищает Вас от сюрпризов при рефакторинге имени/пакета класса
Плохо
public class SomeClass < public void someMethod() < Logger.getLogger("com.dataart.demo.java.logging.SomeClass").log(Level.INFO,"Some message"); >.
По сути тоже самое но букв больше и читается не так легко.
Замечание между примерами
Вы наверное обратили внимание, что все сообщения в примерах на английском языке. Это не случайно. Дело в том, что даже если все-все кто работает и будет работать с Вашим кодом говорят по русски, есть вероятность, что Вам придется просматривать лог сообщения на удаленном компьютере например через ssh при этом в большом количестве случаев Вы увидите примерно такое сообщение «. . . » (я безусловно знаю что через ssh можно протащить русские буквы, но вот почему-то далеко не всегда все оказывается настроенным должным образом).
Или даже на локальной машине в cmd вы можете увидеть что вот такое:
INFO: ╨Ъ╨░╨║╨╛╨╡-╤В╨╛ ╤Б╨╛╨╛╨▒╤Й╨╡╨╜╨╕╨╡ ╨▓ ╨╗╨╛╨│С этим безусловно тоже можно бороться. Но не всегда легко объяснить заказчику на том конце телефонной трубки, как сделать так чтобы вместо крякозябр были видны русские буквы.
Совет: Пишите лог сообщения на английском языке, ну или в крайнем случае латинскими буквами.Пример №2
Хорошо
try < throw new Exception("Some exception"); >catch (Exception ex) < log.log(Level.SEVERE, "Exception: ", ex); >//В стандартной лог конфигурации вы это сообщение не увидите log.fine("some minor, debug message"); /* Иногда вывод лог сообщений требует достаточно больших ресурсов (например дамп какого-то пакета данных и т.п.). В таких случаях стоит проверить выведется ли в лог сообщение для этого уровня логирования */ if (log.isLoggable(Level.FINE))
- Если Вам необходимо залогировать исключение, для этого служит метод .log(level,message,exception)
- Если вы специально не настроили конфигурацию лог системы, сообщения с уровнем ниже info, например fine выводиться не будут. Но писать их по крайней мере для важных частей системы стоит. Когда что-то пойдет не так, Вы настроите более подробный уровень логирования и увидите много интересного.
- Слишком много лог сообщений, даже если они физически не пишутся в лог файл из-за своего слишком маленького уровня, могут существенно замедлить выполнение программы. Особенно если для подготовки самого сообщения надо потратить много ресурсов. Для этого есть метод .isLoggable(level) — он позволяет узнать пропустит ли текущая конфигурация логера данное сообщение
Плохо
try < throw new Exception("Some exception"); >catch (Exception ex) < log.severe("Exception: " + ex.toString() ); >log.fine("Some CPU consuming message: " + itTakes500MillisecondsToPrepageThisMessage());
Если логировать только ex.toString(), то потом Вы не сможете понять в какой строке изначально сработало исключение.
Пример №3
Логер надо конфигурировать. Есть конфигурация по умолчанию она выводит в консоль все сообщения с уровнем INFO и выше. Она достаточно хороша, для разработки из IDE, но для реального приложения ее обычно неплохо бы подправить.
Какие тут есть варианты
По умолчанию: Файл logging.properties для уровня INFO, вывод в консоль
#Console handler
handlers= java.util.logging.ConsoleHandler
.level=INFOДелаем логирование более подробным выводим еще и сообщения уровня FINE
#Console handler
handlers= java.util.logging.ConsoleHandler
.level=FINE
java.util.logging.ConsoleHandler.level = FINE- Установили уровень FINE для корневого логера, просто чтобы сообщения пролезали внутрь лог системы.
- И сказали что все что пролезет через лог систему надо выводить на консоль от уровня FINE и выше.
Выводим лог сообщения куда-то еще
- Если приложение запускается с помощью javaw Вы вообще ничего не увидите.
- Если вывод идет в консоль и нужное вам сообщение промелькнуло 4 часа назад буфер консоли его уже съел, информация пропала.
- Если вывод консоли направлен в файл java com.yourcompanyname.EntryClass 2>>application_log.txt и приложение работает не останавливаясь несколько недель — файл будет весьма и весьма большим, рискуя занять весь диск.
Чтобы решить эти проблемы был придуман java.util.logging.FileHandler — хэндлер который выводит лог сообщения в файл. При этом он умеет ротировать файлы, т.е. после достижения максимально допустимого размера, он дописывает в файл текщуее лог сообщение и открывает новый файл с инкрементальным префиксом. И так по кругу. Например
handlers= java.util.logging.FileHandler java.util.logging.FileHandler.pattern = application_log.txt java.util.logging.FileHandler.limit = 50 java.util.logging.FileHandler.count = 7 java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
создаст вот такие файлы (последняя колонка — размер в байтах)
application_log.txt.0 │ 0 application_log.txt.1 │ 79 application_log.txt.2 │ 79 application_log.txt.3 │ 676 application_log.txt.4 │ 87 application_log.txt.5 │ 114
Мы указали максимальный размер 50 байтов, в реальной жизни надо скорее указывать не меньше мегабайта, например вот так (я знаю, что 1000000 это чуть меньше мегабайта, но кому охота по памяти писать 1048576, если суть дела это фактически не меняет)
java.util.logging.FileHandler.limit = 1000000
В примере, как мы видим, файлы получились больше 50 байт потому что размер по сути округляется вверх до последнего целого лог сообщения. Т.е. если Вы укажете размер 1 байт и запишете лог сообщение размером в 1000 байт то размер файла станет 1000 байт и после этого лог сообщения файл закроется и откроется следующий.
copy & paste конфиг для реальной жизни, его вполне хватает для большинства service, console и desktop приложений.
handlers= java.util.logging.FileHandler java.util.logging.FileHandler.pattern = application_log.txt java.util.logging.FileHandler.limit = 1000000 java.util.logging.FileHandler.count = 5 java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
Последняя часть магии
- Из командной строки запуска приложения
- В первых строчках кода Вашего приложения
Первый чуть более правильный ибо он декларативный и работает сразу, до того как начал работать код Вашего приложения.
Вот так
java Djava.util.logging.config.file=logging.properties com.dataart.application.ClassName
Но к сожалению менять строку запуска не всегда можно или не всегда удобно. Второй способ тоже неплохо работает.
public static void main(String[] args) < try < LogManager.getLogManager().readConfiguration( MainApplicationEntryClass.class.getResourceAsStream("/logging.properties")); >catch (IOException e) < System.err.println("Could not setup logger configuration: " + e.toString()); >.
- Здесь MainApplicationEntryClass — это класс — точка входа в Ваше приложение, видимо имя класса у Вас будет другое
- Сам файл logging.properties как правило в таких случаях кладется в корень иерархии классов и выглядит это например вот так
Что осталось за кадром
- Log4J
- JULI logger (строго говоря это не вполне самостоятельный фреймворк, а своего рода надстройка над java.util.logging)
- SLF4J
- Commons Logging
- Tomcat
- JBoss
- Resin