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

#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 все работает правильно, когда мы повторно развертываем один из наших модулей.