#java #log4j2 #tomcat9
Вопрос:
У нас есть общая конфигурация для log4j2.xml
catalina
базового каталога. таким образом, все рассматриваемые модули webapps
используют эту конфигурацию. Также log4j-core
log4j-web
и log4j-api
библиотеки под общей библиотекой tomcat. Когда мы перезапускаем tomcat, все модули записывают свои журналы. Но при повторном развертывании любого из них только этот модуль регистрирует другие, а не другие. Например , у нас есть A
B
и C
модуль в папке webapps. Если мы повторно развернем C
модуль после перезапуска, то увидим только C
журналы модулей. Как я могу решить эту проблему? Я не мог найти никакого объяснения, почему это происходит
Я включил log4j
журналы, чтобы видеть, что происходит. После повторного log4j
развертывания распечатайте журналы такого рода
2021-04-13 17:40:56,930 Catalina-utility-1 DEBUG Registering MBean org.apache.logging.log4j2:type=457e2f02
2021-04-13 17:40:56,930 Catalina-utility-1 DEBUG Registering MBean org.apache.logging.log4j2:type=457e2f02,component=StatusLogger
2021-04-13 17:40:56,930 Catalina-utility-1 DEBUG Registering MBean org.apache.logging.log4j2:type=457e2f02,component=ContextSelector
2021-04-13 17:40:56,930 Catalina-utility-1 DEBUG Registering MBean org.apache.logging.log4j2:type=457e2f02,component=Loggers,name=
457e2f02
это должно быть имя нашего модуля, но зачем log4j
печатать случайный текст
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG" monitoring="10">
<Properties>
<Property name="log.level">ALL</Property>
<Property name="framework.log.level">WARN</Property>
<Property name="file.pattern">%d{DEFAULT}: [%level{WARN=WAR, DEBUG=DBG, ERROR=ERR, TRACE=TRC, INFO=INF, FATAL=FTL}] %-c.%M[%L] - %m%n</Property>
<Property name="console.pattern">%highlight{%d{DEFAULT}: [%level{WARN=WAR, DEBUG=DBG, ERROR=ERR, TRACE=TRC, INFO=INF, FATAL=FTL}] %-c.%M[%L] - %m%n n}{FATAL=red blink, ERROR=red bold, WARN=yellow bold, INFO=green bold, DEBUG=white bold, TRACE=cyan}</Property>
<Property name="logs.dir">logs</Property>
<Property name="app.name">magus</Property>
<Property name="script.test.mode">false</Property>
</Properties>
<Appenders>
<Console name="CONSOLE" target="SYSTEM_OUT">
<PatternLayout pattern="${console.pattern}"/>
</Console>
<Socket name="GELF" protocol="tcp" host="graylog.host" port="12201">
<!-- gelf tcp does not support compression-->
<GelfLayout includeStackTrace="true" host="${hostName}" includeThreadContext="true" includeNullDelimiter="true"
compressionType="OFF">
<KeyValuePair key="host" value="${hostName}"/>
<KeyValuePair key="version" value="1.1"/>
<!--<KeyValuePair key="short_message" value="${event:Message}"/>--><!-- not required -->
<!--<KeyValuePair key="application_name" value="${ctx:application}"/>-->
<KeyValuePair key="thread_id" value="${event:ThreadId}"/>
<KeyValuePair key="thread_name" value="${event:ThreadName}"/>
<KeyValuePair key="timestamp" value="${event:Timestamp}"/>
<!--<KeyValuePair key="level" value="1"/>--><!-- default level type is number so we don't use-->
<KeyValuePair key="log_level" value="${event:Level}"/><!-- for readabilty and filtering-->
</GelfLayout>
</Socket>
<RollingFile name="LOG" fileName="${logs.dir}/${app.name}.log"
filePattern="${logs.dir}/${date:yyyy-MM}/${app.name}-%d{yyyy-MM-dd}.log.gz">
<PatternLayout pattern="${file.pattern}"/>
<Policies>
<TimeBasedTriggeringPolicy/>
</Policies>
<DefaultRolloverStrategy max="3">
<!--
Nested conditions: the inner condition is only evaluated on files
for which the outer conditions are true.
-->
<Delete basePath="${logs.dir}" maxDepth="2" testMode="${script.test.mode}">
<IfFileName glob="*/${app.name}*.log.gz">
<IfLastModified age="90d">
<IfAny>
<IfAccumulatedFileCount exceeds="10"/>
</IfAny>
</IfLastModified>
</IfFileName>
</Delete>
</DefaultRolloverStrategy>
</RollingFile>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="CONSOLE"/>
<AppenderRef ref="LOG"/>
<AppenderRef ref='GELF' />
</Root>
<Logger name="xxx.project" level="${log.level}"/>
<Logger name="org.hibernate" level="${framework.log.level}"/>
<Logger name="com.zaxxer" level="${framework.log.level}"/>
<!--<Logger name="org.hibernate" level="ALL"/>-->
<Logger name="org.reflections" level="OFF"/>
<!-- show executed queries -->
<!--<Logger name="org.hibernate.SQL" level="DEBUG"/>-->
<!-- show query params -->
<!--
<Logger name="org.hibernate.type.descriptor.sql" level="TRACE"/> -->
<!--
<AsyncLogger name="az.sanco.projects.ais.server.filter" additivity="false" level="${log.level}">
<AppenderRef ref="AUDITOR"/>
</AsyncLogger> -->
</Loggers>
</Configuration>
Версия Tomcat: 9.0
Версия Java: 1.8
версия log4j2: 2.14.0
Комментарии:
1. Можете ли вы добавить содержание
log4j2.xml
к своему вопросу? Все ли ваши приложения регистрируются в одном файле?2. @PiotrP.Karwasz на машине разработки да, все модули регистрируются в одном файле. На самом деле я не думаю, что проблема связана с
log4j2.xml
файлом, потому что при запуске tomcat все работает нормально. проблема начнется после повторного развертывания любого из модулей. Но я опубликую содержимое конфигурации3. Я могу эмулировать вашу проблему, удалив все
log4j*
библиотеки из папки приложенийWEB-INF/lib
: в этой ситуации все приложения используют один и тот жеLoggerContext
(с шестнадцатеричным идентификатором, как у вас) иlog4j-web
закрывают его при любой перезагрузке приложения.4. Если вы используете API Log4j2 напрямую, вам, вероятно, следует оставить копию
log4j-api
в каждом приложении (это означает, что каждое приложение будет иметь «свой собственный»LogManager
класс).5. @PiotrP.Karwasz большое вам спасибо. Если вы хотите добавить свой комментарий в качестве ответа, и я принял его как правильное решение
Ответ №1:
Случайное имя 457e2f02
вашего LoggerContext
предполагает, что все ваши приложения используют один контекст. Когда одно из ваших приложений остановлено, stop
метод вызывается в контексте, и никакие дальнейшие сообщения не могут быть зарегистрированы. Тем временем перезагруженное приложение создает контекст для себя.
Это может произойти, если вы используете API Log4j непосредственно в своих приложениях, но не отправляете приложения с копией log4j-api
: в этой ситуации LogManager
класс в общем загрузчике классов Tomcat будет использоваться всеми приложениями.
Попробуйте добавить log4j-api.jar
в свои приложения.
Комментарии:
1. Хотя ваш ответ, вероятно, верен, предполагается, что ClassLoaderContextSelector должен работать не так. Это должно быть определение загрузчика классов класса, вызывающего getLogger, и связывание регистратора с LoggerContext для этого загрузчика классов. Если этого не происходит, следует сообщить об ошибке.
2. Да, я также думал, что
ContextSelector
следует создать другой контекст для каждого загрузчика классов, но на практике это не так. Однако вызовLogManager.getLogger
веб-приложения без добавленияlog4j-api
WEB-INF/lib
, вероятно, не является приемлемым сценарием.3. @PiotrP.Karwasz должен ли я переместить все
log4j-core
log4j-web
иlog4j-api
во все мое приложение? Когда я копирую толькоlog4j-api
в свои приложения и сохраняю другие на общем месте, я получил исключениеCaused by: java.lang.ClassNotFoundException: org.apache.logging.log4j.Logger
. В этом случае должен ли я скопировать всю банку log4j в свое приложение? каков правильный способ реализации log4j в нашем случае. Потому что мы сделали то, что предлагает документация, но, похоже, она работает неправильно4. Я бы сохранил
log4j-api
иlog4j-web
в приложении, в то время как реализация по умолчаниюlog4j-core
может оставаться в общем загрузчике классов.
Ответ №2:
На самом деле ответ @PiotrP.Karwasz решает log4j
проблему. Но не решайте нашу проблему. Если мы применили его ответ, то мы должны переместить все log4j-core
, log4j-api
, и log4j-web
банки в каждую WEB-INFlib
папку наших приложений, что не является эффективным способом. потому что у нас почти 20 модулей, и в этом случае мы копируем банки во все эти модули и занимаем больше ресурсов. Согласно документации по разделению журналов, мы не должны выполнять дальнейшую настройку для этих требований. Селектор контекста Log4j должен выбрать автоматический контекст для каждого war
модуля. Но на практике мы увидели, что это работает неправильно. Может быть, ошибка, а может быть, мы неправильно настроили. Я сделал третий вариант org.apache.logging.log4j.core.selector.JndiContextSelector
из документации. И это решит наши проблемы. Мы сохраняем log4j-core
, log4j-api
, и log4j-web
банки в общем пути, и каждый из наших модулей использует эти банки. Теперь log4j
все работает правильно, когда мы повторно развертываем один из наших модулей.