предисловие
В реальных проектах разработки ведение журнала всегда является неизбежной темой. В этой серии статей делается попытка использовать системы журналов slf4j и log4j2 в качестве примеров для анализа принципа работы журналирования с точки зрения исходного кода.
Чтобы изучить структуру журналов, вы должны сначала ознакомиться с различными средами журналов.Вот две рекомендуемые статьи, поэтому я не буду их повторять.
Для log4j2 существует несколько типов конфигурационных файлов: properties, xml, json/jsn и yaml/yml, чаще всего мы используем xml.
В обычных условиях мы создадим файл log4j2.xml и поместим его в папку /resources проекта. Большинство проектов, которые используют maven для управления зависимостями, также могут быть настроены средой.Разные среды читают разные файлы log4j2, которые обычно находятся в папке /profiles/${env}/.
Большинству людей следует «позаимствовать» из других проектов, скопировать конфигурацию, а затем возиться с ней. Однако рассматривали ли вы:
- Зачем писать этот файл конфигурации? Что будет, если не написать?
- Существуют ли правила именования этого файла конфигурации? Почему мы обычно видим log4j2.xml вместо других имен?
- Как загружается этот файл конфигурации?
Ответы на поставленные выше вопросы и есть первоначальная цель этой статьи.
намекать
1. В этой статье будет использован метод отладки, в качестве основной линии будет взят процесс загрузки конфигурации log4j2 и описан его рабочий процесс, боковые ветки, которые мало на что влияют, будут проигнорированы, а заинтересованные читатели могут самостоятельно проверить исходный код.
2. Предупреждение о мультиизображении! Смотреть лучше с компа.
3. Попробуйте на практике углубить свое понимание.
Подготовка окружающей среды
Прежде чем читать исходный код, обязательно ознакомьтесь с пакетами зависимостей slf4j и log4j2, а также с пакетами адаптации. Взяв в качестве примера maven, пример программы в этой статье представляет:
<!-- slf4j --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.21</version> </dependency> <!-- bridge --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.7</version> </dependency> <!-- log4j2 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.7</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.7</version> </dependency>
исходный код
Во-первых, мы создаем новый java-файл и ломаем точку, чтобы начать отладку.
Введите метод getLogger. Как видите, конкретная фабрика Logger получается из LoggerFactory.
Введите метод getILoggerFactory.
Забудьте о куче логики здесь, мы закончим на строке 418.
Далее введите реальную ссылку привязки журнала. Поскольку мы представили только log4j2, он будет найден непосредственно здесь, а затем привязан. StaticLoggerBinder находится в пакете log4j2.
Программа переходит к строке 61, и вы можете видеть, что синглтон реализован здесь с использованием голодного метода. Строка 41 создает экземпляр StaticLoggerBinder, который перейдет к строке 53, давайте зайдем и посмотрим подробности.
Видно, что Log4jLoggerFactory наследует абстрактный адаптер журнала AbstractLoggerAdapter. В этом абстрактном адаптере определено несколько методов, не волнуйтесь, они скоро будут упомянуты.
Вернемся к LoggerFactory, через метод getLoggerFactory мы получим только что созданный Log4jLoggerFactory:
Затем мы вводим ссылку getLogger для log4j2.
Вы можете видеть, что getLogger является методом интерфейса и имеет 3 реализации.
Помните Log4jLoggerFactory, которую мы только что получили? AbstractLoggerAdapter — его родительский класс, поэтому мы перейдем к getLogger класса AbstractLoggerAdapter.
getContext — это абстрактный метод AbstractLoggerAdapter, поэтому далее мы перейдем к методу getContext Log4jLoggerFactory.
Здесь мы используем отражение, чтобы найти наш журнал (якорь переводится как «якорь» на китайском языке, что можно понимать как что-то похожее на дескриптор файла). Полученный здесь якорь — «», поэтому он войдет в следующий оператор.
В конце концов мы получим AppClassLoader, и LogManager будет использовать этот загрузчик классов для получения контекста.
Введите getContext, чтобы увидеть:
Обратите внимание: фабрикой здесь является Log4jContextFactory, которая инициализируется в статическом блоке кода в LogManager, подробности будут добавлены позже.
Теперь давайте перейдем к getContext и посмотрим:
Здесь getContext — это метод в интерфейсе ContextSelector, и следующим шагом будет ввод getContext в ClassLoaderContextSelector. Что касается того, как инициализируется селектор, мы поговорим об этом позже.
Следующие шаги являются контекстно-зависимыми операциями, больше не публикуются и в конечном итоге вернутся сюда:
Затем перейдите к методу ctx.start в строке 152, зайдите и посмотрите:
Теперь, наконец, пришло время начать загрузку конфигурации! ! !
Следующие несколько шагов более интуитивны, карта показывает:
Здесь сначала создается экземпляр ConfigurationFactory, а затем получается конфигурация. Что касается создания экземпляра ConfigurationFactory, то здесь это объясняться не будет, но вы можете проверить это сами.
Далее введите метод getConfiguration:
Введите этот метод:
Обратите внимание, что здесь getFactories ясно сообщил нам, что существует 4 фабрики (все они унаследованы от ConfigurationFactory), которые работают с четырьмя типами файлов конфигурации, упомянутыми выше: properties, xml, json/jsn и yaml/yml. Различные суффиксы можно получить, вызвав метод factory.getSupportedTypes(). Возьмите xml в качестве примера:
То же самое справедливо и для других типов файлов.
Что ж, вернемся к способу загрузки конфигурации, вы можете увидеть 426 строк кода, чтобы определить, все ли типы файлов поддерживаются. На самом деле окончательный код ядра — это строки 453–467:
Здесь попробуйте получить конфигурацию с различными условиями.Если конечная конфигурация будет нулевой, будет напечатан журнал ошибок, сообщающий вам, что файл конфигурации не найден. Так как у нас пока нет конфигурации, мы перейдем к строке 466.
Теперь вы можете добавить файл log4j2 в путь /resources, заполнить простую конфигурацию, и вы получите конфигурацию в строке 459. Давайте посмотрим на детали getConfiguration:
Видно, что здесь имя конфигурационного файла, склеенного по разным условиям.
В качестве примера возьмем самый распространенный файл log4j2.xml:
На картинке выше мы получили имя файла конфигурации: log4j2.xml.
При этом видно, что префикс — log4j2, а суффикс — суффикс файла.
Префикс (строка 505) жестко запрограммирован в ConfigurationFactory:
Поэтому имя файла, определенное при его настройке, должно соответствовать спецификации и не может быть названо произвольно.
Теперь, когда у вас есть имя файла конфигурации, вы можете загрузить его:
Внутри метода:
Теперь URL получен. Его значение — «project_path/target/classes/log4j2.xml».
Далее нужно загрузить содержимое из файла (строка 517, что предполагает знание загрузчика классов, проверьте сами).
Затем нужно прочитать содержимое файла xml:
Иди сюда, начни читать файл xml. Эта часть контента будет разбита в следующий раз.
Оставшийся вопрос: как инициализируются фабрика LoggerManager и ее внутренний селектор?
На самом деле перед вызовом LogManager.getContext(cl, false); блок статического кода в LoggerManager будет вызываться заранее. Давайте посмотрим:
Смотрим на 89~100 строчный код:
Введите метод providerutil.getProviders () Внутренний вид:
Вы можете видеть, что провайдер представляет собой синглтон, реализованный лениво (вы обнаружите, что метод ProviderUtil.hasProviders() в 89 строках кода уже был создан, когда он выполняется, поэтому он возвращается прямо здесь. Обратите внимание, что там — деталь в процессе создания, которая будет использована позже), используемая для определения приоритета каждой фабрики.
Давайте сосредоточимся на внутренних деталях 91 строки кода:
В строке 96 класс загружается, а в строке 98 он преобразуется в подкласс LoggerContextFactory (он же Log4jContextFactory).
Итак, вопрос в том, что такое className, почему он указывает Log4jContextFactory?
На самом деле при создании экземпляра Provider ранее конструктор будет читать конфигурационный файл в log4j-core, который содержит атрибуты, соответствующие className:
Таким образом получил className: org.apache.logging.log4j.core.impl.Log4jContextFactory.
Затем спускайтесь вниз:
Вы можете видеть, что экземпляр объекта Log4jContextFactory будет создан путем отражения, и будет вызван конструктор Log4jContextFactory без параметров:
Метод createContextSelector инициализирует селектор:
Последующие детали инициализации не будут расширены.
Закончится здесь:
На этом этапе создается фабрика.
Теперь вы должны быть в состоянии ответить на три вопроса в начале статьи, верно?
Суммировать
В этой статье описывается основная линия загрузки конфигурации журнала log4j2 посредством отладки (без учета многих деталей, таких как настраиваемые пути и т. д.), а в последующих статьях будет дополнительно описан процесс разбора файла конфигурации.
Я надеюсь, что благодаря этой статье читатели смогут глубже понять процесс загрузки конфигурации log4j2.
Наконец, уровень автора ограничен, а ошибки и упущения неизбежны, исправления и обмены приветствуются для общего прогресса.