Запутанная система регистрации Java

Apache Log4j

Компонент журнала является наиболее часто используемым компонентом в разработке, но он также является одним из компонентов, которые чаще всего упускают из виду.Я сам сталкивался со многими проблемами, когда приложение не может быть запущено из-за сообщения об ошибках Log4j.Ниже приводится прочесывание, ссылка и Ссылка.Опыт предшественников, и добавил некоторые мои собственные понимания, относительно легко понять некоторые~

1. Общая структура ведения журналов

В настоящее время общие структуры журналов и фасады Java (кажется, что китайский язык не очень легко перевести) следующие:

  • 1. лог4дж

  • 2. войти в систему

  • 3. SLF4J

  • 4. Commons-логирование

  • 5. j.u.l (например, java.util.logging)

1-3 написаны одним и тем же автором (Ceki). 4 используется во многих проектах с открытым исходным кодом, а 5 — это собственная библиотека Java (замененная аббревиатурой jul ниже), но она была представлена ​​только в Java 1.4. С таким количеством библиотек журналов вы можете найти структуру, которая больше подходит для вашего проекта, понимая их плюсы и минусы и их взаимосвязь.

Отношения между фреймворками

Как показано на рисунке ниже, common-logging и slf4j относятся к одному и тому же фасаду журнала, а нижний слой может быть подключен к определенному слою каркаса журнала.

common-logging: разработчики могут использовать его для совместимости с jul и log4j, что эквивалентно среднему уровню. Следует отметить, что совместимость конфигурации common-logging для jul и log4j не так хороша, как ожидалось. Что еще хуже, в common-logging На ранней стадии выпуска ведения журналов использование common-logging может привести к проблемам с загрузкой классов, что приведет к

Появляется ошибка NoClassDefFoundError;

slf4j: может более гибко подключать несколько базовых фреймворков;

3. Введение в log4j

1. Регистратор (ограниченный уровень ведения журнала)

Порядок уровней следующий: DEBUG

Правило уровня log4j: выводить только информацию журнала, уровень которой не ниже установленного уровня, например, если уровень регистраторов INFO, то будет выводиться информация журналов уровней INFO, WARN, ERROR и FATAL, а DEBUG чей уровень ниже INFO не будет выводиться.

2. Appender (назначение вывода журнала)

Appender позволяет выводить журналы в разные места по конфигурации, такие как консоль (Console), файлы (Files) и т. д., может генерировать новые файлы в зависимости от количества дней или размера файла и может отправляться в другие места в форма ручьев;

Общие классы конфигурации:

org.apache.log4j.ConsoleAppender (консоль)

org.apache.log4j.FileAppender(файл)

org.apache.log4j.DailyRollingFileAppender (создает файл журнала каждый день)

org.apache.log4j.RollingFileAppender (создает новый файл, когда размер файла достигает указанного размера) org.apache.log4j.WriterAppender (отправляет информацию журнала в любое указанное место в потоковом формате)

3. Выход из системы (формат вывода журнала)

Пользователи могут форматировать вывод журнала в соответствии со своими потребностями, а log4j может добавлять макеты после завершения Appenders;

Layouts предоставляет четыре формата вывода журнала: в соответствии со стилем HTML, произвольно заданный стиль, стиль, содержащий уровень журнала и информацию, стиль, содержащий время журнала/поток/категорию и другую информацию;

org.apache.log4j.HTMLLayout (макет в формате таблицы HTML)

org.apache.log4j.PatternLayout (шаблон макета можно указать гибко)

org.apache.log4j.SimpleLayout (содержит уровень сообщения журнала и строку сообщения)

org.apache.log4j.TTCCLayout (включая время создания журнала, поток, категорию и т. д.)

В-четвертых, организационная структура регистратора

В этих трех классах логгер получается через Logger.getLogger(XXX.class);:


Организационная структура регистратора выглядит следующим образом, а отношения родитель-потомок реализованы с помощью «.»:


Регистраторы являются именованными объектами.Имена регистраторов чувствительны к регистру и соответствуют правилам иерархического именования. Регистратор с именем "com.mobile" является отцом регистратора с именем "com.mobile.log". Точно так же регистратор с именем «java» является отцом регистратора с именем «java.util» и предком регистратора с именем «java.util.Vector».
Корневой регистратор находится на вершине всей системы наследования регистраторов.По сравнению с обычными регистраторами он имеет две особенности:

1), корневой регистратор существует всегда.

2) Корневой регистратор не может быть получен по имени.

Объект корневого регистратора можно получить, вызвав статический метод getRootLogger класса Logger. Экземпляры других распространенных регистраторов можно получить с помощью другого статического метода getLogger класса Logger. Метод getLogger принимает параметр в качестве имени регистратора.

Пять, отношения наследования уровня

Отношения наследования узла логгера отражены на уровне См. следующие примеры:

Пример 1: В этом примере только корневому регистратору присвоено значение уровня Proot, и Proot будет унаследован другими дочерними регистраторами: x, x.y, x.y.z


Пример 2: В этом примере всем регистраторам присвоено значение уровня, поэтому нет необходимости наследовать уровень


Пример 3: В этом примере трем регистраторам root, x и x.y.z присвоены три значения уровня Proroot, Px и Pxyz соответственно, а регистратор x.y наследует значение уровня от своего родителя;


Пример 4: В этом примере регистраторам root и x присвоены значения уровня Proroot и Px соответственно. Два регистратора x.y и x.y.z наследуют значение уровня от своего ближайшего предка x;


Шесть, отношения наследования Appender

Аддитивность регистратора указывает, наследует ли дочерний регистратор источник вывода (Appender) родительского регистратора, то есть дочерний регистратор наследует Appender родительского регистратора по умолчанию (дочерний регистратор будет выводить в Appender родительского регистратора). вручную установив для флага аддитивности значение false. Дочерний регистратор будет выводить только в своем собственном Appender, а не в родительском Appender.

Как показано ниже:


Семь, два способа использования slf4j

Существует два способа использования slf4j: один — с конкретными привязками, а другой — с устаревшими мостами.

1. Бетон-связки

Режим конкретных привязок означает, что в новом проекте разработчик напрямую использует sl4j api для печати лога, а нижний слой привязывается к любому фреймворку лога, например logback, log4j, jul и т.д. По принципу реализации , в основном существует два типа гибридных форм связывания соответственно.С адаптеромсвязывание инет адаптерапривязка.

Гибридная привязка с адаптером означает, что нижний уровень не реализует интерфейс slf4j, а напрямую вызывает Logger базовой среды ведения журнала через адаптер.Привязка без адаптера не требует вызова Logger других сред ведения журнала, и он реализует все интерфейсы самого slf4j.

Несколько смешанных пакетов привязки:

  • slf4j-log4j12-1.7.21.jar (адаптер, привязка log4j, Logger предоставляется log4j-1.2.17.jar)

  • slf4j-jdk14-1.7.21.jar (адаптер, привязка l.u.l, Logger предоставляется средой выполнения JVM, т.е. библиотека j.u.l)

  • logback-classic-1.0.13.jar (без адаптера, собственная реализация slf4j)

  • slf4j-simple-1.7.21.jar (без адаптера, простая реализация slf4j, печатает только INFO и сообщения более высокого уровня, весь вывод перенаправляется в System.err, подходит для небольших приложений)

Вышеупомянутые привязки можно легко переключать без изменения внутреннего кода.Независимо от того, какая привязка, это зависит от slf4j-api.jar.

Кроме того, для привязки адаптера требуется специальная структура ведения журналов, например привязка log4j slf4j-log4j12-1.7.21.jar зависит от log4j.jar, привязка jul slf4j-jdk14-1.7.21.jar зависит от jul (предоставляется средой выполнения Java) ; Прямая реализация без адаптеров, logback-classic опирается на logback-core для предоставления низкоуровневых функций, а slf4j-simple не зависит от других библиотек.

Примерная диаграмма вышеупомянутых четырех привязок выглядит следующим образом:


Что касается адаптера, начинается обычное использование slf4j для получения логгера из LoggerFactory.getLogger. Внутри getLogger сначала будет получен ILoggerFactory через StaticLoggerBinder. Например, обратитесь к этим четырем slf4j-api.jar, log4j-core- 2.3.jar, log4j-api-2.3.jar, log4j-slf4j-impl.jar (перенаправить slf4j в log4j2):


Проанализируем использование двух типовых привязок log4j (с адаптером) и logback (без адаптера).

1) привязка адаптера log4j (slf4j-log4j12)
<!--pom.xml-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>${slf4j-log4j12.version}</version>
</dependency>


Уведомление
: после добавления приведенной выше конфигурации привязки адаптера будут автоматически удалены две зависимые библиотеки, а именно slf4j-api-1.7.21.jar и log4j-1.2.17.jar.

основная логика: пользовательский уровень


2) slf4j привязан к logback-classic
<!--pom.xml-->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>${logback-classic.version}</version>
</dependency>


Уведомление
: после добавления приведенной выше конфигурации привязки адаптера будут автоматически удалены две зависимые библиотеки, а именно slf4j-api-1.7.21.jar и logback-core-1.0.13.jar.

logback-classic не имеет уровня адаптера, но напрямую реализует org.slf4j.Logger из slf4j в ch.qos.logback.classic.Logger из logback-classic-1.0.13.jar и сильно зависит от ch.qos.logback.core. Многочисленные базовые классы в:

import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.spi.LocationAwareLogger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.classic.util.LoggerNameUtil;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.spi.AppenderAttachable;
import ch.qos.logback.core.spi.AppenderAttachableImpl;
import ch.qos.logback.core.spi.FilterReply;

public final class Logger implements org.slf4j.Logger, LocationAwareLogger, AppenderAttachable<ILoggingEvent>, Serializable {}

Схема привязки:


2. Мостовое наследие

Связующее унаследованное использование в основном предназначено для исторических унаследованных проектов, независимо от того, написано ли оно на log4j, j.c.l или j.u.l, оно может иметь возможность другой среды ведения журнала без изменения кода. Например, если ваш проект написан с использованием встроенной библиотеки журналов j.u.l, предоставляемой java, и с использованием устаревшего режима slf4j, вы можетебез изменения строки кодаКорпус Instant имеет все возможности log4j. Грубо говоря, код вашего проекта мог быть написан лет 5 назад, в то время из-за отсутствия выбора использовался относительно дрянной фреймворк логирования, в котором были различные дефекты и проблемы, например, невозможность сохранения ежедневно и неуправляемо.Размер, поддерживаемые аппендеры невелики, и они не могут быть сохранены в базе данных.Возможно появление неожиданных ошибок. Итак, как заменить старую структуру ведения журналов и внедрить более совершенные и зрелые среды ведения журналов, такие как log4j и logback, не изменяя код?Режим устаревшего моста slf4j призван решить эту проблему.

slf4j использует slf4j-api в качестве среднего уровня и перенаправляет сообщения старой структуры журнала верхнего уровня в новую структуру журнала, связанную с нижним уровнем.


Приведен пример, иллюстрирующий использование вышеуказанного фасада для простоты понимания. Предположим, у меня есть завершенный проект, использующий старую среду ведения журналов commons-logging, и теперь я хочу заменить ее на log4j для получения дополнительных и лучших функций.
Старая конфигурация проекта maven выглядит следующим образом:

<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>${commons-logging.version}</version>
</dependency>


Код проекта:

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class LogTest {
    private static Log logger = LogFactory.getLog(LogTest.class);

    public static void main(String[] args) throws InterruptedException {
        logger.info("hello,world");
    }
}


Журналы, основанные на общих журналах, напечатанные проектом, отображаются на консоли следующим образом:

十一月 20, 2017 5:52:00 下午 LogTest main
信息: hello,world


Далее трансформируем проект и пересылаем логи фреймворка commons-logging в log4j, трансформация очень простая, удалим зависимость commons-logging, заменим на соответствующий фасад (здесь jcl-over-slf4j. jar), а внизу на фасаде висит 5.1Гибридная привязкаВот и все.

В частности, замените commons-logging.jar на jcl-over-slf4j.jar и добавьте адаптер slf4j-log412.jar (обратите внимание, что добавление slf4j-log412.jar автоматически приведет к удалению двух других пакетов jar), поэтому в конце , просто добавьте в смешанную привязку фасадjcl-over-slf4j.jar и тот же пакет jar slf4j-log412.jar.

Модифицированная конфигурация maven:

<!--facade-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>${jcl-over-slf4j.version}</version>
</dependency>

<!--binding-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>${slf4j-log4j12.version}</version>
</dependency>


Теперь наш старый проект находится вНи одна строка кода не измениласьВ случае наличия всех возможностей log4j выполняются следующие тесты.
Создайте новый файл log4j.properties в папке resources/, чтобы настроить вывод журнала библиотеки commons-logging:

# Root logger option
log4j.rootLogger=INFO, stdout, fout

# Redirect log messages to console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.Threshold = INFO
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

# add a FileAppender to the logger fout
log4j.appender.fout=org.apache.log4j.FileAppender
# create a log file
log4j.appender.fout.File=log-testing.log
log4j.appender.fout.layout=org.apache.log4j.PatternLayout
# use a more detailed message pattern
log4j.appender.fout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n


Перекомпилируйте и запустите, вывод консоли станет таким:

2017-11-20 19:26:15 INFO LogTest:11 - hello,world


При этом в текущем каталоге создается лог-файл:

% cat log-testing.log
INFO    2017-11-20 19:26:15,341    0    LogTest    [main]    hello,world


Видно, что фреймворк ведения журнала на основе фасада вступил в силу, и мы позволили фреймворку ведения журналов общих ресурсов иметь все функции log4j12 без изменения кода.

Восемь, сравнение log4j и log4j2


Конфигурационный файл содержит большое количество содержимого. Вы можете подробно ознакомиться с соответствующими документами при его использовании. По сравнению с log4j, log4j2 имеет более высокую производительность, удобочитаемость кода и поддержку параметризованной печати журнала.

9. Проблемы, с которыми может столкнуться компонент журнала

1. Бесконечный цикл

Две линии одного цвета на рисунке ниже обозначают пакеты, которые не могут сосуществовать.


2. Повторный вывод журнала

Когда приложение log4j.xml:аддитивность установлена ​​в trueа такжеappender-ref настраивает соответствующий appenderКогда есть проблема повторной печати, это трудно понять, просто сказав это, например:

Конфигурация log4j.xml выглядит следующим образом:


В тестовом классе testlog.java:


Результат: одни и те же логи печатаются в root.log и logtest.log соответственно


Когда для аддитивности задано значение false: root.log больше не ведет журнал


3. Значение предупреждений и подсказок об ошибках slf4j

1), SLF4J: не удалось загрузить класс «org.slf4j.impl.StaticLoggerBinder».

SLF4J: See Woohoo. Water Cube 4 находится по адресу .org/codes.HTML#… for further details.

Местом, где сообщается об ошибке, является в основном пакет jar slf4j, а «Не удалось загрузить org.slf4j.impl.StaticLoggerBinder» в коде ошибки означает «не удалось загрузить файл класса org.slf4j.impl.StaticLoggerBinder». ",

Официальное решение, данное Apache:

This error is reported when the org.slf4j.impl.StaticLoggerBinder class could not be loaded into memory. This happens when no appropriate SLF4J binding could be found on the class path. Placing one (and only one) of slf4j-nop.jar, slf4j-simple.jar, slf4j-log4j12.jar, slf4j-jdk14.jar or logback-classic.jar on the class path should solve the problem.

Перевод таков: об этой ошибке сообщается, когда org.slf4j.impl.StaticLoggerBinder не найден, и это происходит, когда в пути к классам не найдено подходящей привязки slf4j. Вы можете разместить один и один из следующих jar-пакетов: slf4j-nop.jar, slf4j-simple.jar, slf4j-log4j12.jar, slf4j-jdk14.jar или logback-classic.jar.

2) в пути к классам было обнаружено несколько привязок.

slf4j — это каркас фасада журнала. Он может одновременно связывать только один и один базовый каркас журнала. Если привязано более одного, slf4j отобразит это приглашение и перечислит конкретные местоположения этих привязок. При наличии нескольких привязок Когда установлено , выберите тот, который вы хотите привязать, а затем удалите остальные, например: вы привязываете

slf4j-simple-1.8.0-beta0.jar и

slf4j-nop-1.8.0-beta0.jar,
То, что вы в конечном итоге хотите использовать, это
slf4j-nop-1.8.0-beta0.jar
, затем просто удалите другой.

Я протестировал его и связал два адаптера в slf4j-api.jar одновременно:


Настоящая привязка — это реализация в пакете slf4j-log4j12.jar:


Поэтому, когда два пакета связаны, метод реализации, в котором окончательно выбран пакет, должен быть в порядке в пути к классам, который должен быть связан с правилами загрузки classLoader.

Дополнительную конкретную информацию можно найти по адресу:Woohoo. Water Cube 4 находится по адресу .org/codes.HTML#… for an explanation

10. Ссылка

сегмент fault.com/ah/119000001…