Обзор
Первый источник это Wikipedia, в ней мы сможем почерпнуть достаточно скудную информацию лишь о компании разработчике и целях проекта.
1. http://ru.wikipedia.org/wiki/Log4j
Log4j — библиотека журналирования Java программ, часть общего проекта «Apache Logging Project».
| Apache log4j | |
| Тип | Журналирование |
| Разработчик | Apache Software Foundation |
| Написана на | Java |
| ОС | Кроссплатформенное ПО |
| Версия | 1.2.15 — 29 сентября 2007 |
| Тестовая версия | 2.0 |
| Лицензия | Apache License 2.0 |
| Сайт | http://logging.apache.org/log4j(англ.) |
Log4j первоначально развивался в рамках зонтичного Apache Jakarta Project, ответственного за все Java-проекты Apache, но впоследствии выделился в отдельный, очень популярный проект журналирования (стандарт де-факто).
2. http://www.log4j.ru/res/articles/HelloWorld.html
Ознакомившись с двумя предыдущими источниками подключить и выполнить примеры Вы конечно же сможете, но что это нам даст, спросите Вы, вряд ли кто то будет выкладывать у себя примеры со сложными реализациями. Да примеры и должны быть простыми, сразу возразят поклонники простоты и лаконичности. Но как это обычно бывает, все примитивные примеры носят мало практической пользы и любой «шаг вправо или шаг влево» повлечет за собой поиски новых источников с описанием настроек и подводных камней. Принимая во внимание все выше сказанное, начнем сначала.
Итак – Log4J – фреймворк для скрытия реализации рутинных операций по логированию (журналированию) некоторых событий, которые происходят во время работы Вашего приложения написанного на Java (есть портированные библиотеки и для других языков). Что значит рутинных – все кто, когда либо имел желание сохранить какую либо информацию во время работы приложения такую как - трассировка выполнения конструкторов, методов, блоков обработки критических ситуаций, сталкивался с одними и теми же операциями, а именно:
- Выбор хранилища – консоль, файл, СУБД;
- Конфигурация хранилища – именование хранилища, объем хранилища, путь к хранилищу, сохранение конфигурации;
- Форматирование записей журнала – дата/время, класс/метод и т.д., гибкое форматирование.
Таким образом, от пользователей фрейворка необходимо лишь:
- загрузить библиотеку фреймворка;
- создать конфигурационный файл с параметрами журналирования;
- создать объект журнала (лога) в своем приложении;
- воспользоваться методами для записи в журнал.
Каким образом хранить конфигурацию журналирования?
Как конфигурировать файл log4j.properties или log4j.xml?
log4j.properties
Формат файла «properties» т.е. ключ = значение, все строки начинающееся с символа # и до конца строки (символы \n\r) означают комментарий. Формат ключей в файле конфигураций следующий:- log4j.rootLogger –
глобальный объект журнала, значением
этого ключа, необходимо указать приоритет логируемой информации.
Приоритетов может быть ШЕСТЬ (log4j1.2.*):
- FATAL - произошла фатальная ошибка - у этого сообщения наивысший приоритет
- ERROR - в программе произошла ошибка
- WARN - предупреждение в программе что-то не так
- INFO - информация.
- DEBUG - детальная информация для отладки
- TRACE– трассировка всех сообщений в указанный аппендер
- OFF<
TRACE<
DEBUG<
INFO<
WARN<
ERROR<
FATAL<
ALL
- Приоритеты приоритетами, но буквально все зависит от Вас, не стоит надеяться, что подключив фреймворк, ваш код станет волшебным образом писать в логи все сам, нет! На самом деле это не приоритеты настоящих сообщений – это приоритеты для самого объекта, что бы он мог их различать. Ничто Вам не помешает сделать запись в журнал из блока обработки критических ситуаций с пометкой INFO.
- log4j.logger.имя_аппендера
– собственно из названия можно понять
что, это именованный объект позволяющий
выполнять добавление чего-то к чему-то,
в нашем случае добавляет данные в
журнал. Выгода от использования
именованного объекта существенна, так
как именно Вы выбираете имя! В качестве
имени может быть:
- любой литерал;
- имя класса,
что позволяет инициализировать объект
Logger следующим образом
:
- Logger.getLogger(имя_класса.class.getClass()) – дання конструкция позволяет обратиться к аппендеру по полному имени т.е. имя_пакета.имя_класса;
- Logger.getLogger(имя_класса.class. getSimpleName()) – данная конструкция позволяет обращаться по простому имени т.е. вы должны быть уверены что не возникнет конфликта с одноименными классами, иначе запись в журнал выполнена НЕ БУДЕТ!;
- log4j.appender.имя_аппендера
– значение данного ключа это тип
аппендера и от него будут зависеть
остальные ключи и значения для данного
типа. Вот пример нескольких:
- org.apache.log4j.ConsoleAppender
- log4j.appender.имя_аппендера.Target – собственно, куда выводить информацию, например это может быть стандартный(Java) System.out or System.err
- org.apache.log4j.FileAppender
- org.apache.log4j.DailyRollingFileAppender
- org.apache.log4j.RollingFileAppender
- org.apache.log4j.net.SMTPAppender
- org.apache.log4j.AsyncAppender
- org.apache.log4j.nt.NTEventLogAppender
- org.apache.log4j.net.SyslogAppender
- org.apache.log4j.jdbc.JDBCAppender
- org.apache.log4j.lf5.LF5Appender
- org.apache.log4j.net.SocketAppender
- org.apache.log4j.net.SocketHubAppender
- org.apache.log4j.net.TelnetAppender
- org.apache.log4j.net.JMSAppender - посылает JMS сообщения
- org.apache.log4j.varia.ExternallyRolledFileAppender
- org.apache.log4j.ConsoleAppender
- log4j.appender.
имя_аппендера.layout – шаблон форматирования
строки журнала,
org.apache.log4j.SimpleLayout – самый примитивный.- log4j.appender.имя_аппендера.layout.ConversionPattern – шаблон
форматирования, в строку можно добавить некоторую полезную информацию:
- %p – имя приоритета, с которым было выполнена запись данных
- %F – имя файла, из которого был вызван метод записи в журнал
- %M – имя метода, из которого был вызван метод записи в журнал
- %L – номер строки, в файле из которого был вызван метод записи в журнал
- %c – имя категории (т.е. аппендера, логера)
- %d – дата
записи, в журнал по данным JVM из которой был запущен процесс
- В скобках можно указать формат вывода, о нем можно прочитать в хэлпе к стандартному классу SimpleDateFormat.
- %m – собственно само сообщение, которое Вы посылаете для записи в журнал
- %n – символ окончания строки
- log4j.appender.имя_аппендера.layout.ConversionPattern – шаблон
форматирования, в строку можно добавить некоторую полезную информацию:
- для более конкретных приемников логирования используйте конкретные типы аппендеров.
Примеры файлов конфигурации :
|
1 log4j.rootCategory=DEBUG, console 2 log4j.appender.console=org.apache.log4j.ConsoleAppender 3 log4j.appender.console.layout=org.apache.log4j.PatternLayout 4 log4j.appender.console.layout.ConversionPattern= %p %c: %m%n скачать исходный код
|
<?xml
version="1.0"
encoding="UTF-8"
?> |
Советы по использованию Log4J
- Определите События, подлежащие записи в журнал;
- Определите ключевые Параметры, для каждого События;
- Определите Источники Событий – это самое важное, иначе ваша система логирования превратится в свалку не нужных Событий с таким же перечнем ненужных Параметров;
- Не старайтесь разделить системы логирования по приоритетам (FATAL or INFO), разделите по функциональным модулям или ключевым Объектам системы;
- Выполняйте логирование жизненного цикла Объекта;
Журналирование
событий это лишь часть функциональности
Вашего приложения, не стоит ставить
приоритет этой задачи выше приоритета
основной функциональности.
Как использовать Log4J в Java приложениях?
Для использования всех функциональных возможностей фрейворка необходимо создать объект журнала.
Пример для Java Application.
|
Файл Main.java | Файл log4j.properties ,располагается в корневой папке проекта, иначе укажите корректный путь в файле класса |
|
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import java.util.Properties; import org.apache.log4j.*; /** * @author generator */ public class Main { public static void main(String[] args) throws FileNotFoundException, IOException { 1:String nameFile = "log4j.properties"; 2:PropertyConfigurator.configure(nameFile); /*1*/Logger LOG = Logger.getRootLogger(); /*2*/Logger localLog2 = Logger.getLogger("logfile"); /*3*/Enumeration append = LOG.getAllAppenders(); /*3*/while (append.hasMoreElements()) { /*3*/LOG.info("Available appender " + append.nextElement()); /*3*/} LOG.info("Hi Logger info!"); localLog2.warn("logfile write!"); System.out.println("LOG.equals(localLog) is " + LOG.equals(localLog)); } } |
# это секция комментариев с описанием форматирования, которую я # добавил в шаблон генерации файлов, в IDENetBeans. # %F - file name (example Main.java)avoided unless execution speed is not an issue. # %M – method (avoided unless execution speed is not an issue.) # %L - line number in file (avoided unless execution speed is not an issue.) # %C – class name (avoided unless execution speed is not an issue.) # %p - priority name # %c - category name i.e. stdout, console, logfile and etc # %d - date write record # %d{} - date write record , options {hh:mm:ss} or {HH:MM:SS} or combination # %m - message # %n - end line \n working in any way # OFF< TRACE< DEBUG< INFO< WARN< ERROR< FATAL< ALLlog4j.rootLogger = TRACE, console log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%p] %d{hh:mm:ss} (%F:%M:%L)%m %n%n #все выводится на консоль log4j.appender.console.Target=System.out # overview log file log4j.logger.logfile=WARN, logfilelog4j.appender.logfile=org.apache.log4j.RollingFileAppender log4j.appender.logfile.File=D:\\warnlog.txt log4j.appender.logfile.MaxBackupIndex=10 # если индекс выше 10 файл с первым индексом перезаписывается log4j.appender.logfile.MaxFileSize=2048KB # если размер больше то начнется запись следующего по индексу файлаlog4j.appender.logfile.layout=org.apache.log4j.PatternLayout log4j.appender.logfile.layout.ConversionPattern=%d [%t] %-5p %c - %m%n |
|
Вывод на консоль: 1:[INFO] 10:07:06 (Main.java:main:43)Available appender org.apache.log4j.ConsoleAppender@10b4199 2:[INFO] 10:07:06 (Main.java:main:45)Hi Logger info! 3:[DEBUG] 10:07:06 (Main.java:main:47)osdebug write! 4:[WARN] 10:07:06 (Main.java:main:48)logfile write! Вывод в лог-файл D:\infolog.txt: 2009-09-15 10:07:06,811 [main] WARN logfile - logfile write! Вывод в лог-файл D:\debuglog.txt: 2009-09-15 10:07:06,811 [main] DEBUG osdebug - osdebug write!
|
В строке 1 перечислены типы категорий, которые были указаны в глобальном объекте. Строка 2 форматированная строка сообщения, вызванная методом LOG.info("Hi Logger info!"); Строка 3 форматированная строка сообщения, вызванная методом localLog.debug("osdebug write!"); Строка 4 форматированная строка сообщения, вызванная методом localLog2.warn("logfile write!"); Вывод перенаправленный в лог файл, форматируется по отдельным правилам , которые указаны в категории log4.j.logger.logfile=INFO, logfile , а строка форматируется на основании шаблона log4j.appender.logfile.layout.ConversionPattern=%d [%t] %-5p %c - %m%n таким образом Вы в состоянии управлять форматированием для разных типов сообщений и для разных категорий. В файле конфигураций указана категория log4.j.logger.logfile=INFO, logfile но вывод не будет направлен, даже если Вы замените строку вызова Logger localLog = Logger.getLogger("logfile"); потому что данная категория не указана в глобальном объекте, никаких сообщений о ошибках выводится не будет! |
В примере создано два объекта журнала, строки помечены как /*1*/ и /*2*/ , оба они ссылаются на разные объекты! В строках /*3*/ создаётся объект enumeration ? если вы вдруг просто слили чей-то конфиг и хотите посмотреть что и как внутри , он выведет все доступные(описанные) в файле конфигураций объекты appender.
Будьте внимательны при указании приоритета в вызове объекта журнала, при указании приоритета ,который не соответствует описанному в конфигурации, данные не будут записаны в журнал. Пример такого описания:
|
Фрагмент в файле Main.java |
Фрагмент в файле log4j.properties |
|
Logger localLog2 = Logger.getLogger("logfile"); localLog2.debug("logfile write!"); |
log4j.logger.logfile=WARN,
logfile
|
|
В результате такого вызова данные не будут записаны в журнал! |
|
Как использовать Log4J с JavaEE/Сервлетами
Использование фреймворка в приложениях Enterprise Edition имеет свои подводные камни, а именно несколько вариантов получения ссылки на объект журнала. Первый вариант – процедура аналогичная Java Application, т.е создание экземпляра объекта. Однако этот вариант в виду специфики веб-приложений, оказывается мало пригодным к использованию, так как контейнер будет многократно создавать объект журналирования (Logger) с помощью которого будет выполняться запись в журнал. Второй вариант – это создание объекта отдельно и сохранение ссылки на него в контексте приложения. Рассмотрим этот вариант подробнее, что нам понадобится:- Файл конфигурации журналирования log4j.properties;
- Файл
Log4jInit.java, в
котором будет отдельный класс (этот
класс наследуется от HttpServlet
, чтобы в
последствии, воспользоваться, возможностью
WEB-контейнера загрузить этот класс) в
этом классе описан процесс:
- получения ссылки на файл конфигурации;
- создание атрибута в объекте ServletContext;
- сохранение в атрибуте ссылки на объект, ассоциированный с файлом конфигурации;
- файл web.xml ,
в котором необходимо:
- выполнить маппинг сервлета Log4jInit ;
- добавить параметр инициализации для сервлета Log4jInit,
- указать что Log4jInit надо загрузить обязательно первым;
- Фрагмент кода, который продемонстрирует, как подключить логирование в Вашем веб-приложении.
|
Файл Log4jInit.java |
Файл web.xml (фрагмент) |
Заметки |
|
package apigw; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; /** * * @author generator */public class Log4jInit extends HttpServlet { @Override public void init(){ String logfilename = getInitParameter("logfile"); /* logfilename - получает имя файла конфигурации из пар-ра инициализации сервлета*/ String pref ="";pref = getServletContext().getRealPath("/"); /*pref – указывает на путь к файлу конфигурации*/PropertyConfigurator.configure(pref+logfilename); Logger globallog = Logger.getRootLogger(); getServletContext().setAttribute(new String("log4"), globallog);getServletContext().setAttribute("logfilename", logfilename); globallog.info("Load-onstart-up Servlet");
} protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); try { String pref = getServletContext().getRealPath("/"); String logfile = (String) getServletContext().getAttribute("logfilename"); /* TODO output your page here */ out.println("<html><head>"); out.println("<title>Servlet Log4jInit</title>"); out.println("</head><body>"); out.println("<h2>File properties Log4jInit " + pref + logfile + "</h2>"); out.println("</body></html>"); } finally { out.close(); }} |
…………. <servlet> <servlet-name>Log4jInit</servlet-name> <servlet-class>apigw.Log4jInit</servlet-class> <init-param> <param-name>logfile</param-name><param-value>WEB-INF/log4j.properties</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Log4jInit</servlet-name> <url-pattern>/Log4jInit</url-pattern> </servlet-mapping>……………. |
Параметр
<load-on-startup>1</load-on-startup> сообщает контейнеру о том что необходимо загрузить servlet первым номером. Если не загрузить этот сервлет первым может возникнуть конфликтная ситуация при попытке обратиться к объектам Logger из других сервлетов, объект может быть не создан.Для сервлета создан параметр инициализации logfile в котором , хранится путь к файлу конфигурации журнала. |
|
Файл вашего
сервлета (фрагмент подключения)
|
| |
|
………….. Logger log = (Logger)getServletContext().getAttribute("log4");log.info("Enter to Servlet"); ……………….. log.info("Exit to Servlet"); |
Обратите внимание, что Вы получаете ссылку на объект через контекст приложения. | |
|
Файл log4j.properties |
| |
|
log4j.rootLogger=INFO, logfile log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=[%5p] %t %d{mm:ss} (%F:%M:%L)%n%m%n%n log4j.appender.stdout.Target=System.out # overview log file log4j.appender.logfile=org.apache.log4j.RollingFileAppender log4j.appender.logfile.File=D:\\LOG\\listGW.loglog4j.appender.logfile.MaxFileSize=2048KB log4j.appender.logfile.layout=org.apache.log4j.PatternLayout log4j.appender.logfile.layout.ConversionPattern=%d{dd/MM/yyyy h:mm} [%t] %p - <%m>%n |
| |
|
Вывод в файл: 1:14/09/2009 4:29 [http-8080-Processor24] INFO - <Load-onstart-up Servlet> 14/09/2009 4:30 [http-8080-Processor23] INFO - <Enter to Servlet> 14/09/2009 4:30 [http-8080-Processor23] INFO - <Exit to Servlet> |
Строка 1,
запись добавлена сервлетом, который
получил ссылку на объект журнала и
добавляется в единственном экземпляре
Строки 2 и 3 добавлены произвольным сервлетом приложения, сервлет выполняет запись 2 раза и будет выполнять каждый раз при запросе страницы браузером, а сервлет загруженный первым запись выполнять не будет. |
|
Используя
единую точку инициализации объекта
Logger, он создается всего один раз, поэтому
не тратятся время и ресурсы на создание
объекта и получение ссылки на файл при
выполнении каждого сервлета из Вашего
веб приложения, в котором
есть необходимость журналирования
событий. Вы сможете использовать один
и тот же объект для логирования всего
приложения, получая необходимо ссылку
через контекст приложения.
Советы и приемы Log4J
Log4J для исключений?!
В самых простых примерах, которых много в сети, объект Logger используется для логирования любых событий.
Оригинальным способом использования именования объектов аппендеров может быть использование в качестве имени аппендера имя класса java.lang.Exception:
|
Фрагмент пользовательского кода |
Фрагмент файла конфигурации журналирования |
|
1:String
nameFile = "log4j.properties";
2:PropertyConfigurator.configure(nameFile);
………………………. try { 3: throw new Exception("Try Me!");} catch (Exception ex) { 4:Logger.getLogger(ex.getClass()).log(Level.ERROR, "Catch Exception!!!", ex); } ……………………….
|
1:log4j.logger.java.lang.Exception=ALL,
java.lang.Exception
log4j.appender.java.lang.Exception=org.apache.log4j.RollingFileAppender log4j.appender.java.lang.Exception.File=D:\\Exceptionlog.txt log4j.appender.java.lang.Exception.MaxFileSize=2048KB log4j.appender.java.lang.Exception.layout=org.apache.log4j.PatternLayout log4j.appender.java.lang.Exception.layout.ConversionPattern=%d [%t] %-5p %c - %m%n |
|
Строки 1,2 инициализируют файл конфигурации |
Строка
1 определяет объект аппендера именем
класса исключений java.lang.Exception
далее определяется тип аппендера и остальные параметры относящееся к типу. |
Строка
3 в блоке try catch эмулируется вызов
Exception с передачей ему входной строки
параметров
Строка
4 собственно создание объекта Logger
Поскольку
ранее объект Logger
не был создан, у нас нет имени аппендера
поэтому методом getLogger() мы получаем
ссылку на этот объект передав в качестве
параметра - ex.getClass()
метод
log() принимает в данном случае
|
Вывод в файл журнала: 2009-09-16 10:13:02,018 [main] ERROR java.lang.Exception - Catch Exception!!! java.lang.Exception: Try Me! at testlog4j.Main.main(Main.java:53) |
Таким образом, Вы получили возможность записывать все исключительные ситуации, перехваченные от объекта Exception в один файл одной и той же конструкцией вызова.
При использовании этого механизма логирования для любого источника исключительной ситуации, придется немного изменить конструкцию вызова:
|
Фрагмент пользовательского кода |
Фрагмент файла конфигурации журналирования |
|
…………………………………. try { throw new ArithmeticException(“Try Me ArithmeticException !”); } catch (ArithmeticException ex) { Logger.getLogger(Exception.class.getName()).log(Level.ERROR, “Catch ArithmeticException !!!”, ex);} try { throw new Exception(“Try Me!!”); } catch (Exception ex) { Logger.getLogger(ex.getClass()).log(Level.ERROR, “Catch Exception!!!”, ex); } ………………………………….. |
1:log4j.logger.java.lang.Exception=ALL,
java.lang.Exception
log4j.appender.java.lang.Exception=org.apache.log4j.RollingFileAppender log4j.appender.java.lang.Exception.File=D:\\Exceptionlog.txt log4j.appender.java.lang.Exception.MaxFileSize=2048KB log4j.appender.java.lang.Exception.layout=org.apache.log4j.PatternLayout log4j.appender.java.lang.Exception.layout.ConversionPattern=%d [%t] %-5p %c - %m%n |
|
Изменения
заключаются в следующем, если для перехвата исключительной ситуации
использовать объект ArithmeticException
и конструкцию вызова идентичную
первому примеру то запись в журнал не
будет произведена, так как не будет
найден одноименный аппендер. Поэтому
для использования записи в тот же файл
иных исключительных ситуаций конструкция
вызова будет кметь
следующий вид:
Logger.getLogger(Exception.class.getName()) или Logger.getLogger(«java.lang.Exception») – объект логгера необходимо определить однозначно не принимая во внимание информацию о классе вызвавшего исключительную ситуацию, это сделано для того что бы объект логгера совпал с объявленным объектом аппендера с файле конфигурации. |
Вывод в файл журнала:
2009-09-16 11:25:30,447 [main] ERROR java.lang.Exception – Catch ArithmeticException !!! java.lang.ArithmeticException: Try Me ArithmeticException ! at testlog4j.Main.main(Main.java:54) 2009-09-16 11:25:30,447 [main] ERROR java.lang.Exception – Catch Exception!!! Java.lang.Exception: Try Me!! At testlog4j.Main.main(Main.java:59) |
Эффективное использование Log4J или как снизить расходы на вычисление приоритета
Расходы на запись данных в журнал при различных конфигурациях могут отличаться, однако вычисление приоритета (LEVEL.INFO & etc) тоже является достаточно дорогой процедурой во время исполнения процесса.
Как же минимизировать расходы на вычислении приоритета?
Для этой задачи в log4j есть статические методы позволяющие выполнять вычисление приоритета:
isEnabledFor(LEVEL.XXX);
isDebugEnabled();
isXXXEnabled() – гдеХХХприоритет (DEBUG, WARN & etc).
- %С – вычисление имени класса;
- %F – вычисление имени файла;
- %M – вычисление имени метода;
- %L
– вычисление номера строки.
Как вариант, эти данные можно вычислить самостоятельно и передать в теле сообщения.