Модераторы: LSD, AntonSaburov
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Логгирование с помощью Log4j 
:(
    Опции темы
LSD
Дата 6.6.2008, 18:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

Репутация: 210
Всего: 538



Предположим вы пишете, или уже написали и отлаживаете, свое приложение. Пока приложение маленькое вам вполне хватет возможностей System.out.println(), но по мере увеличения и усложнения приложения его возможностей уже нехватает. В частности что нельзя (или затруднительно) сделать с помощью System.out.println():
1) управление логгированием: отключить логгирование одних сообщений, и включить логгирование других
2) запись логов в файл, БД и т.п.
3) вывод дополнительной информации о сообщении: время события, место события, контекст
4) форматирование вывода
для решения всех этих проблем и были созданы системы логгирования. Две наиболее популярные это Log4j от Apache и Java Logging API входящее в состав JDK 1.4 и старше. Надо еще упомянуть про Commons Logging, которая тоже часто используется. Commons Logging не является системой логгирования как таковой, это просто надстройка которая предоставляет единый интерфейс к системам логгирования (Log4j и Java Logging API). Из этих двух систем я предпочитаю Log4j, т.к. это готовая система логгирования, а Java Logging API для своего использования требует "доработки напильником".

Итак приступаем. Идем на сайт Log4j и скачиваем последнюю версию (на момент написания статьи последняя стабильная версия 1.2), распаковываем и подключаем log4j-X.X.XX.jar к проекту.

Создаем новый класс и пишем:
Код

package ru.vingrad.log;

import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;

public class Test
{
  private static Logger logger = Logger.getLogger(Test.class);

  public static void main(String[] args) throws Exception
  {
    BasicConfigurator.configure();
    logger.info("Hello world!");
    logger.error("Error!", new Exception("An exception"));
  }
}

запускаем и получаем:
Код

0 [main] INFO ru.vingrad.log.Test  - Hello world!
0 [main] ERROR ru.vingrad.log.Test  - Error!
java.lang.Exception: An exception
    at ru.vingrad.log.Test.main(Test.java:14)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:623)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:90)


Итак, что же тут произошло. 

Базовым классом через которое и будет идти все логгирование является org.apache.log4j.Logger. Для получения логгера используется статический метод getLogger() куда в качестве параметра надо передать имя этого логгера. Также есть перегруженный метод куда можно передать класс, и он вернет логгер с именем этого класса (в примере это ru.vingrad.log.Test).  Каждый логгер имеет уникальное имя, и вызов getLogger() с одним и тем же именем вернет один и тот же логгер. Логгер синхронизирован и его можно свободно использовать в многопоточной среде.

Для именования логгеров принято использовать правила именования пакаджей Java. Для логгеров существует иерархия аналогичная иерархии пакаджей, т.е. для ru.vingrad.log.Test родительским будет логгер ru.vingrad.log, для ru.vingrad.log - ru.vingrad и т.д. Плюс существует корневой логгер, являющийся родительским для всех остальных, получит его можно: Logger.getRootLogger(). Важным фактом является то, что логгер  если логгер не сконфигурирован индивидуально, то он будет использовать настройки своего родительского логгера. Т.е. мы можем сконфигурировать только корневой логгер, а все логгеры будут использовать его настройки. Так же логгер использует аппендеры родительского логгера (об аппендерах далее), это можно отключается свойством additivity.

Логгер может писать сообщения разных уровней (org.apache.log4j.Level), уровень очень важное понятие для управления логгированием. Уровни упорядочены по старшинству (в порядке убывания): FATAL, ERROR, WARN, INFO, DEBUG, TRACE. Уровни FATAL, ERROR, WARN предназначенны для сообщений об ошибках, разной степени серьезности, INFO информационные сообщения, DEBUG - отладочные, TRACE - очень подробная отладочная информация. Когда мы конфигурируем систему логгирования мы указываем сообщения какого уровня надо писать в лог. В лог будут писаться сообщения этого уровня и выше, т.е если мы настроили систему на уровень INFO то будут писаться сообщения с уровнями FATAL, ERROR, WARN, INFO. Плюс еще есть спец уровни ALL и OFF, которые предназначены для включения/выключения записи всех сообщений.

Следующая важная составляющая системы логгирования это - аппендер (org.apache.log4j.Appender). Аппендер это класс который занимается непосредственно записью сообщений. org.apache.log4j.Appender это интерфейс, но у него есть реализации для записи сообщений в консоль, файл, БД, e-mail и т.д. Так же у аппендера можно установить фильтры (org.apache.log4j.spi.Filter) для фильтрации сообщений. И форматер сообщений (org.apache.log4j.Layout) для форматирования выводимых сообщений.

А теперь со всей этой фигней на борту мы попробуем взлететь. Как же происходит запись логов? 
1. Вызываем logger.info("Hello world!")
2. Происходит проверка уровня логгирования для данного логгера, если данному логгеру не был явно задан уровень, то тогда проверка осуществляется для его родителей. Если уровень логгера выше уровня сообщения, то оно отбрасывается и запись его не происходит. В нашем случае корневой аппендер сконфигурирован на уровешь DEBUG а у сообщения уровень INFO, т.е. уровень логгера ниже уровня сообщения и оно проходит проверку.
3. Далее сообщение передается аппендерам установленным для данного логгера (если есть).
4. Если свойство additivity установлено в true (по умолчанию оно установлено), то сообщение так же передается родительским логгерам.
5. Аппендер получив сообщение проверяет его установленными фильтрами (если есть). Если сообщение прошло фильтры оно форматируется и записывается в лог.

Это все конечно хорошо, но как же конфигурировать логгеры, не кодом же в main() добавлять аппендеры и логгеры?
Для конфигурирования Log4j используются файлы конфигурации log4j.xml и log4j.properties, можно загружать конфигурацию вручную, но проще положить один из этих файлов в CLASSPATH и тогда при запуске Log4j сам загрузит из него конфигурацию. Какой из конфигов использовать дело вкуса, мне больше нравится xml, да и возможностей у него побольше, так что расскажу как конфигурировать с его помощью.

Итак создаем в папке с исходниками файл log4j.xml и пишем в него:
Код

<?xml version='1.0' encoding='ISO-8859-1' ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration>
</log4j:configuration>

(файл log4j.dtd можно найти внутри log4j.jar)

Для начала добавим аппендеры. Консоль:
Код

  <appender name="CONSOLE-DEBUG" class="org.apache.log4j.ConsoleAppender">
    <param name="target" value="System.out"/>
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{ISO8601} [%5p] %m at %l%n"/>
    </layout>
    <filter class="org.apache.log4j.varia.LevelRangeFilter">
      <param name="LevelMin" value="ALL"/>
      <param name="LevelMax" value="INFO"/>
    </filter>
  </appender>

CONSOLE-DEBUG имя аппендера, по этому имени мы будем его упоминать в этом конфиге. 
org.apache.log4j.ConsoleAppender - имя класса аппендера
<param name="target" value="System.out"/> - устанавливаем параметр аппендера который определяет куда писать лог
<layout - форматер, подробнее о формате шаблона можно почитать в Java doc по классу форматера (org.apache.log4j.PatternLayout)
<filter - фильтр сообщений, в данные аппендер будут писаться все сообщения уровней INFO, DEBUG и TRACE

Аналогично конфигурируем аппендер для ошибок:
Код

  <appender name="CONSOLE-WARN" class="org.apache.log4j.ConsoleAppender">
    <param name="target" value="System.err"/>
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{ISO8601} [%5p] %m at %l%n"/>
    </layout>
    <filter class="org.apache.log4j.varia.LevelRangeFilter">
      <param name="LevelMin" value="WARN"/>
    </filter>
  </appender>

разница в том, ошибки будут писаться в System.err.

Конфигурируем запись в файл
Код

  <appender name="LOG-FILE-APPENDER" class="org.apache.log4j.FileAppender">
    <param name="file" value="app.log"/>
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{ISO8601} [%5p] %c %m at %l%n"/>
    </layout>
  </appender>

тут все аналогично, параметр file задает имя файла.

Теперь сконфигурируем логгеры. 

Предположим что мы используем Hibernate и хотим видеть только сообщения об ошибках и писать их только в  консоль но не файл.
Код

  <category name="org.hibernate" additivity="false">
    <priority value="WARN"/>
    <appender-ref ref="CONSOLE-WARN"/>
    <appender-ref ref="CONSOLE-DEBUG"/>
  </category>

но в то же самое время, мы хотим видеть отладочные сообщения в org.hibernate.SQL (где пишутся выполняемые Hibernate SQL запросы).
Код

  <category name="org.hibernate.SQL">
    <priority value="DEBUG"/>
  </category>


Для всех остальных категорий включаем уровень DEBUG и запись в консоль и файл
Код

  <root>
    <priority value="DEBIG"/>
    <appender-ref ref="CONSOLE-WARN"/>
    <appender-ref ref="CONSOLE-DEBUG"/>
    <appender-ref ref="LOG-FILE-APPENDER"/>
  </root>


В итоге мы получаем следующий файл:
Код

<?xml version='1.0' encoding='ISO-8859-1' ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration>

  <appender name="CONSOLE-DEBUG" class="org.apache.log4j.ConsoleAppender">
    <param name="target" value="System.out"/>
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{ISO8601} [%5p] %m at %l%n"/>
    </layout>
    <filter class="org.apache.log4j.varia.LevelRangeFilter">
      <param name="LevelMin" value="ALL"/>
      <param name="LevelMax" value="INFO"/>
    </filter>
  </appender>

  <appender name="CONSOLE-WARN" class="org.apache.log4j.ConsoleAppender">
    <param name="target" value="System.err"/>
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{ISO8601} [%5p] %m at %l%n"/>
    </layout>
    <filter class="org.apache.log4j.varia.LevelRangeFilter">
      <param name="LevelMin" value="WARN"/>
    </filter>
  </appender>

  <appender name="LOG-FILE-APPENDER" class="org.apache.log4j.FileAppender">
    <param name="file" value="app.log"/>
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d{ISO8601} [%5p] %c %m at %l%n"/>
    </layout>
  </appender>

  <category name="org.hibernate" additivity="false">
    <priority value="WARN"/>
    <appender-ref ref="CONSOLE-WARN"/>
    <appender-ref ref="CONSOLE-DEBUG"/>
  </category>

  <category name="org.hibernate.SQL">
    <priority value="DEBUG"/>
  </category>

  <root>
    <priority value="DEBIG"/>
    <appender-ref ref="CONSOLE-WARN"/>
    <appender-ref ref="CONSOLE-DEBUG"/>
    <appender-ref ref="LOG-FILE-APPENDER"/>
  </root>

</log4j:configuration>


Теперь самое время протестировать, что получилось.
Код

public class Test
{
  private static Logger logger = Logger.getLogger(Test.class);

  public static void main(String[] args) throws Exception
  {
    logger.debug("Inside main()");
    logger.info("Hello world!");
    logger.error("Error!", new Exception("An exception"));

    Logger hibernateGeneral = Logger.getLogger("org.hibernate");
    hibernateGeneral.debug("Starting Hibernate");

    Logger hibernateSql = Logger.getLogger("org.hibernate.SQL");
    hibernateSql.debug("select * from my table");

    hibernateGeneral.error("Hibernate error");
  }
}

на выходе получим:
Код

2008-06-06 18:18:16,253 [DEBUG] Inside main() at ru.vingrad.log.Test.main(Test.java:11)
2008-06-06 18:18:16,253 [ INFO] Hello world! at ru.vingrad.log.Test.main(Test.java:12)
2008-06-06 18:18:16,253 [ERROR] Error! at ru.vingrad.log.Test.main(Test.java:13)
java.lang.Exception: An exception
    at ru.vingrad.log.Test.main(Test.java:13)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:623)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:90)
2008-06-06 18:18:16,253 [DEBUG] select * from my table at ru.vingrad.log.Test.main(Test.java:19)
2008-06-06 18:18:16,268 [ERROR] Hibernate error at ru.vingrad.log.Test.main(Test.java:21)


Автор благодарит алфавит за любезно предоставленные буквы smile


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
Hidrag
Дата 6.6.2008, 20:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 877
Регистрация: 9.4.2005
Где: JDK

Репутация: 3
Всего: 25



А вот пример как прописать настройки лоджера в коде:
Код

    Logger logger = Logger.getLogger(MainForm.class);
    String pattern = "[%d{dd.MM.yyyy HH:mm:ss}] %m %n";
    PatternLayout layout = new PatternLayout(pattern);

    FileAppender appender = new FileAppender(layout, "app.log", false);
    ConsoleAppender appender2 = new ConsoleAppender(layout);
    
    logger.addAppender(appender);
    logger.addAppender(appender2);
    logger.setLevel(Level.WARN);





--------------------
user posted image
PM WWW ICQ   Вверх
The_M
Дата 19.9.2008, 11:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 1
Регистрация: 5.12.2007

Репутация: нет
Всего: нет



А может ктото подсказать как в файле настроек log4j сделать так чтоб эти настройки брались из другого файлика?
например чтобы при смене уровня логирования не нада было бы изменять log4j.xml 
для этого можно просто проимпортить в log4j.xml  нужный файлик и все?
может у когото уже есть примерчик smile
PM MAIL   Вверх
LSD
Дата 19.9.2008, 14:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

Репутация: 210
Всего: 538



Импортировать вроде бы нельзя, но можно определить переменную окружения log4j.configuration в которой и указать путь к файлу с настройками (путь может быть относительным). Если имя файла заканчивается на .xml, то он будет парситься как XML, иначе как properties.


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
Fedrus
Дата 19.9.2008, 14:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 130
Регистрация: 20.9.2007

Репутация: нет
Всего: нет



1: Решил по полной использовать Log4j и почему-то сразу нашел эту статью))
Спасибо за статью.(перешел на xml конфигурацию)
2: The_M  А если не секрет тебе это зачем?
--------------------
Если вы идете через ад, идите не останавливаясь.
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
javastic
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux, javastic.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0770 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.