[Из песочницы] Java logging. Hello World

Вступление Думаю, ни для кого не секрет, что такое логгеры и для чего они нужны. За время существования java было создано немало фреймворков логгирования. Среди самых известных можно выделить: JUL — java.util.logging log4j JCL — jakarta commons logging Logback SLF4J — simple logging facade for java В данной статье будет рассмотрен каждый из указанных выше фреймворков на уровне «hello world». Будут приведены простые примеры использования основного функционала и конфигурирования. Статья не преследует цель сравнения логгеров между собой и выявление лучшего из них, эту возможность автор оставляет за вами, уважаемые читатели. В конце статьи будут приведены источники, где можно получить более детальную информацию по каждому фреймворку. Также перед прочтением данной статьи рекомендую ознакомиться с публикацией «Java Logging: история кошмара», где описана история развития систем логгирования в Java.System.err.println Первым и самым примитивным способом логгирования был метод System.err.println. Думаю, комментарии излишние, достаточно взглянуть на приведенный ниже код: // Определяем файл в который будем писать лог System.setErr (new PrintStream (new File («log.txt»))); // Выводим сообщения System.err.println («Сообщение 1»); System.err.println («Сообщение 2»); // Выводим сообщение об ошибке try { throw new Exception («Сообщение об ошибке»); } catch (Exception e) { e.printStackTrace (); } Java.util.logging Данный фреймворк включен в стандарт и поставляется вместе с JDK, поэтому ничего дополнительно скачивать и подключать вам не надо. JUL имеет следующие уровни логгирования по возрастанию: FINEST, FINER, FINE, CONFIG, INFO, WARNING, SEVERE, а так же ALL и OFF, включающий и отключающий все уровни соответственно.Логгер создается вызовом одного из статических методов класса java.util.logging.Logger: Logger log = Logger.getLogger (LoggingJul.class.getName ()); Методы логгера могут принимать в качестве аргументов строковые сообщения, шаблоны сообщений, исключения, ресурсы локализованных текстовок сообщений, а также, начиная с Java 8, поставщиков строковых сообщений: // Строковое сообщение String stringMessage = «Сообщение»; // Строковое сообщение с параметрами String stringMessageFormat =«Сообщение {0}»; // Исключение Throwable throwable = new Throwable (); // ResourceBundle хранящий сообщения ResourceBundle resourceBundle = ResourceBundle.getBundle («logging.jul.bundle»); // Поставщик сообщений Supplier stringMessageSupplier = ()→«Сообщение»; Выделяется две группы методов: название которых соответствует уровню логгирования и методы log, loggp, logrb, принимающие уровень логгирования в качестве параметра с типом Level. Первая группа содержит методы двух типов: принимающих строковое сообщение или поставщика строковых сообщений: log.info (stringMessage); log.info (stringMessageSupplier); Вторая группа методов имеет следующие вариации: // Вывести сообщение с указанием уровня логгирования log.log (new LogRecord (Level.INFO, stringMessage)); log.log (Level.INFO, stringMessage); log.log (Level.INFO, stringMessageSupplier); log.log (Level.INFO, stringMessageFormat, args); log.log (Level.INFO, stringMessage, throwable); log.log (Level.INFO, throwable, stringMessageSupplier); // Вывести сообщение с указанием уровня логгирования, класса и метода log.logp (Level.INFO, «ClassName», «MethodName», stringMessage); log.logp (Level.INFO, «ClassName», «MethodName», stringMessageSupplier); log.logp (Level.INFO, «ClassName», «MethodName», stringMessageFormat, args); log.logp (Level.INFO, «ClassName», «MethodName», stringMessage, throwable); log.logp (Level.INFO, «ClassName», «MethodName», throwable, stringMessageSupplier); // Вывести сообщение с указанием уровня логгирования, класса, // метода и resourceBundle, хранящего сообщения log.logrb (Level.INFO, «ClassName», «MethodName», resourceBundle, «messageId»); log.logrb (Level.INFO, «ClassName», «MethodName», resourceBundle, «messageId», throwable); // Вывести сообщение об ошибке log.throwing («ClassName», «MethodName», throwable); Теперь обратимся к конфигурации фреймворка. По умолчанию JUL будет выводить сообщения на консоль, однако можно задать конфигурацию в файле свойств. Для задания способа вывода сообщений необходимо для вашего логгера указать какие хендлеры он будет использовать. Существует следующие классы хендлеров: FileHandler, ConsoleHandler, StreamHandler, SocketHandler, MemoryHandler. Особенностью JUL является то, что настройки хендлеров задаются в целом для всего класса, а не для конкретного экземпляра, что может порождать не мало проблем, например если вам потребуется сообщения различных логгеров выводить в различные файлы или с различным форматированием. Рассмотрим простой пример конфигурационного файла: # Настройки глобального логгера handlers =java.util.logging. FileHandler .level=ALL # Конфигурация файлового хендлера java.util.logging.FileHandler.level =ALL java.util.logging.FileHandler.formatter =java.util.logging.SimpleFormatter java.util.logging.FileHandler.limit = 1000000 java.util.logging.FileHandler.pattern = log.txt # Конфигурация консольного хендлера java.util.logging.ConsoleHandler.level = ALL java.util.logging.ConsoleHandler.pattern = log.log java.util.logging.ConsoleHandler.formatter =java.util.logging.SimpleFormatter Для того что бы JUL применил данную конфигурацию нужно передать параметр -Djava.util.logging.config.file = , либо при старте приложения выполнить код: LogManager.getLogManager ().readConfiguration (<ваш класс>.class.getResourceAsStream («logging.properties»)); Log4j Данный фреймворк на текущий момент имеет уже вторую версию, которая увы не совместима с первой. Поскольку первая версия log4j существует достаточно давно и, в виду ее большой популярности, существует не мало статей на просторах интернета, сегодня мы рассмотрим вторую. Для использования log4j2 вам необходимо подключить библиотеки log4j-api-2.x и log4j-core-2.x. Log4j имеет несколько отличное от JUL именование уровней логгирования: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, а так же ALL и OFF включающий и отключающий все уровни соответственно.Логгер создается вызовом статического метода класса org.apache.logging.log4j.Logger: Logger log = LogManager.getLogger (LoggingLog4j.class); // или Logger log = LogManager.getLogger («name»); Логгер умеет принимать по мимо привычных нам String, Object и Throwable еще два новых типа — MapMessage и Marker: // Карта сообщений (напечатается как msg1=«Сообщение 1» msg2=«Сообщение 2») MapMessage mapMessage = new MapMessage (); mapMessage.put («msg1», «Сообщение 1»); mapMessage.put («msg2», «Сообщение 2»); // Маркер, объект по которому можно фильтровать сообщения Marker marker = MarkerManager.getMarker («fileonly»); // Строковое сообщение String stringMessage = «Сообщение»; // Строковое сообщение с параметрами String stringMessageFormat = «Сообщение {}, от {}»; // Исключение Throwable throwable = new Throwable (); // Объект Object object = new Object (); В классическом для логгеров стиле методы делятся на два типа: совпадающие с названием уровня логгирования и методы log, принимающие уровень логгирования в качестве параметра. Первые имеют вид: log.info (mapMessage); log.info (object); log.info (stringMessage); log.info (marker, mapMessage); log.info (marker, object); log.info (marker, stringMessage); log.info (object, throwable); log.info (stringMessage, throwable); log.info (stringMessageFormat, args); log.info (marker, mapMessage, throwable); log.info (marker, object, throwable); log.info (marker, stringMessageFormat, args); log.info (marker, stringMessage, throwable); log.throwing (throwable); Методы log в log4j2 выглядят так: log.log (Level.INFO, mapMessage); log.log (Level.INFO, object); log.log (Level.INFO, stringMessage); log.log (Level.INFO, marker, mapMessage); log.log (Level.INFO, marker, object); log.log (Level.INFO, marker, stringMessage); log.log (Level.INFO, object, throwable); log.log (Level.INFO, stringMessageFormat, args); log.log (Level.INFO, stringMessage, throwable); log.log (Level.INFO, marker, mapMessage, throwable); log.log (Level.INFO, marker, object, throwable); log.log (Level.INFO, marker, stringMessageFormat, args); log.log (Level.INFO, marker, stringMessage, throwable); log.throwing (Level.INFO, throwable); Если не определить конфигурацию, то при запуске log4j2 выдаст гневное сообщение, о том, что конфигурация не задана и будет печатать ваши сообщения на консоль уровнем не ниже ERROR. Конфигурация log4j2 задается несколькими вариантами: xml, json, yaml. Стоит отметить, что со второй версии нет поддержки конфигурации из property файла. Файл с конфигурацией автоматически ищется classpath, должен иметь название log4j2 и располагаться в пакете по умолчанию.Конфигурация log4j2 состоит из описания логгеров, аппендеров и фильтров. Для более детального изучения обратитесь к документации, сейчас же лишь отметим пару ключевых моментов. Во-первых, есть различные вкусности в виде фильтров, в том числе и по маркерам: BurstFilter CompositeFilter DynamicThresholdFilter MapFilter MarkerFilter RegexFilter StructuredDataFilter ThreadContextMapFilter ThresholdFilter TimeFilter Во-вторых, имеется широкий круг классов аппендеров, в том числе асинхронные аппендеры и аппендеры оборачивающие группу других аппендеров: AsyncAppender ConsoleAppender FailoverAppender FileAppender FlumeAppender JDBCAppender JMSAppender JPAAppender MemoryMappedFileAppender NoSQLAppender OutputStreamAppender RandomAccessFileAppender RewriteAppender RollingFileAppender RollingRandomAccessFileAppender RoutingAppender SMTPAppender SocketAppender SyslogAppender Стоит также заметить, что log4j может создавать множество различающихся аппендеров одного и того же класса, например несколько файловых аппендеров, которые пишут в разные файлы.Рассмотрим пример конфигурации, в которой объявлены два логгера (корневой и для нашего класса), первый из которых пишет в файл log.log, а второй пишет в log2.log с использованием фильтрации по маркеру: %d %p %c{1.} [%t] %m %ex%n %d %p %c{1.} [%t] %m %ex%n Commons-logging Довольно старый проект, который представляет собой обертку над JUL и log4j, не привносящая никакого дополнительного функционала. Уровни логгирования у JCL совпадают с log4j, а в случае взаимодействия с JUL происходит следующее сопоставление: fatal = Level.SEVERE error = Level.SEVERE warn = Level.WARNING info = Level.INFO debug = Level.FINE trace = Level.FINEST Для использования JCL подключаем commons-logging-1.x.jar. Создаем логгер вызовом метода фабрики: Log log = LogFactory.getLog (LoggingCl.class); // или Log log = LogFactory.getLog («name»); Методы JCL очень простые, совпадают с названием уровней логгирования, принимают только объекты и исключения и имеют две вариации: Object object = «Сообщение»; Throwable throwable = new Throwable (); log.info (object); log.info (object, throwable); Конфигурация JCL содержит отдельные блоки для log4j, JUL и собственной реализации. Если не задать конфигурацию, то используется собственная реализация, именуемая SimpleLog, которая выводит сообщения на консоль. Рассмотрим пример конфигурационного файла: #Log4j org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger log4j.configuration=log4j.properties #JUL org.apache.commons.logging.Log=org.apache.commons.logging.impl.Jdk14Logger handlers=java.util.logging.FileHandler, java.util.logging.ConsoleHandler .level=INFO java.util.logging.FileHandler.pattern=jul.log java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter java.util.logging.FileHandler.limit=50000 java.util.logging.FileHandler.count=1 #SimpleLog org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog org.apache.commons.logging.simplelog.defaultlog=fatal org.apache.commons.logging.simplelog.showlogname=true org.apache.commons.logging.simplelog.showShortLogname=true org.apache.commons.logging.simplelog.showdatetime=true Указать файл конфигурации JCL можно следующим образом: java -Djava.util.logging.config.file=/absolute/path/to/your/config/file/commons-logging.properties -jar /absolute/path/to/your/jar/file/MyClass.jar Logback Данный фреймворк используется только в связке с оберткой SLF4J, которую мы будем рассматривать позднее. Для начала работы вам необходимы logback-core-1.x.jar и logback-classic-1.x.x.jar, а также slf4j-api-1.x.x.jar.Взаимодействие с логгером мы будем осуществлять через API предоставляемый оберткой SLF4J. Уровни логгирования совпадают с log4j. Создание логгера в таком случае выглядит следующим образом: org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger (LoggingLogback.class); // или org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger («name»); API позволяет выводить строковые сообщения, шаблоны строковых сообщений, исключения, а также использовать маркеры: // Строковое сообщение String stringMessage = «Сообщение»; // Шаблон сообщения String stringMessageFormat = «Сообщение {} {}»; // Ошибка Throwable throwable = new Throwable (); // Маркер Marker marker = MarkerFactory.getMarker («marker»); Названия методов совпадают с уровнями логгирования и имеют вид: log.info (stringMessage); log.info (stringMessageFormat, args); log.info (stringMessage, throwable); log.info (marker, stringMessage); log.info (marker, stringMessage, throwable); log.info (marker, stringMessageFormat, args); Теперь рассмотрим непосредственны функционал logback. Конфигурация ищется в classpath в следующем порядке: Пытается найти logback.groovy Иначе пытается найти logback-test.xml Иначе пытается найти logback.xml Иначе использует базовую конфигурацию — выводим сообщения на консоль Основными элементами конфигурации являются логгеры, аппендеры, лайауты, и фильтры.Имеются следующие фильтры: Regular filters LevelFilter ThresholdFilter EvaluatorFilter Matchers TurboFilters CountingFilter Имеются следующие аппендеры: OutputStreamAppender ConsoleAppender FileAppender RollingFileAppender SocketAppender and SSLSocketAppender ServerSocketAppender and SSLServerSocketAppender SMTPAppender SyslogAppender SiftingAppender AsyncAppender О том что такое Layouts и Encoders в logback предлагаю прочитать подробно в документации, а сейчас лишь приведу простой пример файла logback.xml: log.log %date %level [%thread] %logger{10} [%file:%line] %msg%n %d{HH: mm: ss.SSS} [%thread] %-5level %logger{36} — %msg%n marker DENY SLF4J Как уже говорилось ранее SLF4J является оберткой над logback, а также над JUL, log4j, или JCL, а также над любым логгером, который реализует ее интерфейс. Для работы с SLF4J нужны библиотека slf4j-api-1.x.x.jar и реализация одного из логгеров либо заглушка. Как правило реализации всех логгеров (кроме logback) поставляются вместе с SLF4J и имеют названия на подобии slf4j-jcl-1.x.jar, slf4j-log4j12–1.x.jar, slf4j-nop-1.x.jar и т.п. Если в classpath не будет найдена реализация логгера (или заглушка nop) SLF4J гневно ругнется и работать откажется. Конфигурация соответственно будет искаться в зависимости от положенной в classpath реализации.API SLF4J мы рассмотрели в предыдущем пункте, поэтому давайте рассмотрим еще одну возможность обертки. В идеальном мире мы должны выводить сообщения через интерфейс обертки, и тогда у нас все будет хорошо, но реальный жестокий мир говорит о том, что всем нам приходится взаимодействовать со сторонними библиотеками или кодом, в которых используются другие логгеры и которые знать не знают о SLF4J. Что бы не подстраиваться под каждый логгер, а пустить все сообщения через одну реализацию интерфейса SLF4J, можно использовать bridging. В поставке обертки содержаться библиотеки jcl-over-slf4j.jar, log4j-over-slf4j.jar и jul-to-slf4j.jar, которые переопределяют поведение соответствующих логгеров и перенаправляют сообщения в обертку.Что бы стало понятнее выше сказанное, рассмотрим пример. Допустим у нас имеются следующие логгеры: java.util.logging.Logger julLog = java.util.logging.Logger.getLogger («julLog»); java.util.logging.Logger log4jLog = java.util.logging.Logger.getLogger («log4jLog»); org.slf4j.Logger slf4jLog = org.slf4j.LoggerFactory.getLogger (LoggingSlf4j.class);

julLog.info («Сообщение от jul»); log4jLog.info («Сообщение от log4j»); slf4jLog.info («Сообщение от slf4j»); Мы хотим, что бы сообщение от JUL записывались в один файл, от log4j в другой, а от slf4j выводились на консоль. В качестве реализации обертки будем использовать logback, конфигурация сего безобразия будет выглядеть следующим образом: log_jul.log %date %level [%thread] %logger{10} [%file:%line] %msg%n log_log4j.log %date %level [%thread] %logger{10} [%file:%line] %msg%n %d{HH: mm: ss.SSS} [%thread] %-5level %logger{36} — %msg%n Для того, что бы мост заработал необходимо выполнить код: SLF4JBridgeHandler.removeHandlersForRootLogger (); SLF4JBridgeHandler.install (); Хочу узнать больше Заключение В заключение хотелось бы вам сказать, что конечный выбор фреймворка логгирования остается всегда за вами, но к этому надо подходить здраво. Выбор должен обуславливаться удовлетворением многих критериев, как высокая производительность, удобный API, наличие нужных способов хранения логгируемых данных, так и спецификой ваших проектов, например, если ваш продукт будет использоваться в других проектах, то не стоит решать за пользователя каким логгером ему придется пользоваться, а в место этого отдать предпочтение обертке.

© Habrahabr.ru