Шаблон подстановочного знака для RoutingAppender Log4j2

Я пытаюсь использовать новый RoutingAppender Log4j2 для маршрутизации различных журналов на основе MDC (ThreadContext в Log4j2). Я хочу сделать следующее:

  • Если карта MDC имеет $contextId -> Append to $contextId appender (конкретный журнал)
  • Если у MDC нет $contextId -> Добавить к основному приложению (общий журнал)

Я хочу добиться этого с помощью шаблона подстановочного знака в теге, а затем отфильтровать, используя ключевой параметр в for contextId ($ {ctx:contextId}) и используя значение по умолчанию (без ключевого параметра) для основного приложения, однако я не знать, какое значение является подстановочным знаком.

Любая помощь приветствуется, может быть, я подхожу к этому с неправильного пути. Я читал о фильтрах, но, похоже, они не работают так, как я хочу.

Спасибо!


person hveiga    schedule 16.08.2013    source источник


Ответы (4)


Отвечает ли это на ваш вопрос? https://issues.apache.org/jira/browse/LOG4J2-326

Ремко

person Remko Popma    schedule 17.08.2013

Я был недоволен решением определить запасной маршрут с помощью трюка, описанного в https://issues.apache.org/jira/browse/LOG4J2-326 и http://logging.apache.org/log4j/2.x/faq.html#separate_log_files, потому что это заставляет меня дублировать конфигурацию добавления, содержащуюся в маршрутах. Мне не нужна другая конфигурация приложения для маршрута по умолчанию, а просто правильное имя файла для общего журнала.

Учитывая, что карта свойств по умолчанию ищет свойство, которое не определено в своем контексте, см. https://logging.apache.org/log4j/2.x/manual/configuration.html#PropertySubstitution, я думаю, что самый простой способ - просто определить значение по умолчанию, например

<Properties>
    <Property name="fruits">any_fruit</Property>
</Properties>

и в случае, если в контексте потока нет ${ctx:fruits}, берется "any_fruit".

person Torsten Küpper    schedule 14.09.2016

Спасибо за ссылку, Ремко, я нашел временное решение, пока эта функция не будет улучшена ребятами из Log4j2. Решение использует как RoutingAppender, так и фильтры. Вот как выглядит моя конфигурация log4j2 (у меня определены свойства, но я их здесь не показываю):

<appenders>
    <appender name="applicationAppender" type="RollingFile" fileName="${logFileName}" filePattern="${logFileNamePattern}" bufferedIO="true" immediateFlush="true" append="true">
        <layout type="PatternLayout" pattern="${logPattern}" />
        <Policies>
            <TimeBasedTriggeringPolicy />
            <SizeBasedTriggeringPolicy size="${logFileSize}" />
        </Policies>
        <DefaultRolloverStrategy max="${logFileCount}" />
    </appender>

    <Routing name="contextSpecificAppender">
        <Routes pattern="$${ctx:contextId}">
            <Route>
                <appender name="Rolling-${ctx:contextId}" type="RollingFile" fileName="logs/${ctx:contextId}.log" filePattern="${logFileNamePattern}" bufferedIO="true" immediateFlush="true" append="true">
                    <layout type="PatternLayout" pattern="${logPattern}" />
                    <Policies>
                        <TimeBasedTriggeringPolicy />
                        <SizeBasedTriggeringPolicy size="${logFileSize}" />
                    </Policies>
                    <DefaultRolloverStrategy max="${logFileCount}" />
                </appender>
            </Route>
        </Routes>
    </Routing>
</appenders>

<loggers>
    <root level="info">
        <appender-ref ref="contextSpecificAppender">
            <ThreadContextMapFilter onMatch="DENY" onMismatch="ACCEPT">
                <KeyValuePair key="contextId" value="" />
            </ThreadContextMapFilter>
        </appender-ref>
        <appender-ref ref="applicationAppender">
            <ThreadContextMapFilter onMatch="ACCEPT" onMismatch="DENY">
                <KeyValuePair key="contextId" value="" />
            </ThreadContextMapFilter>
        </appender-ref>
    </root>
</loggers>

Что я делаю, так это вызываю ThreadContext.put("contextId", "") или ThreadContext.put("contextId", "something") в зависимости от того, какой аппендер я хочу регистрировать. Я надеюсь, что функция wildward будет реализована в ближайшее время, но пока этого решения мне достаточно.

Спасибо!

person hveiga    schedule 19.08.2013
comment
Я нашел решение, благодаря которому вам не нужно использовать фильтр в сочетании с переключением ключа ThreadContext с заполненного на пустой) с использованием двух маршрутов. Как в этом примере объясняется logging.apache.org/log4j/2.x/ faq.html#separate_log_files Вы можете использовать атрибут ключа на маршруте, чтобы создать маршрут, который выбирается, если ThreadContext не имеет значения для ключа. Затем я использую этот маршрут для ссылки на мой основной аппендикс. Если вы настроите этот второй маршрут, вам вообще не нужно использовать ThreadContextMapFilter. Кроме того, вам понадобится только один appender-ref в вашем корневом регистраторе. - person Alex; 14.06.2014

Спасибо, hveiga, за продолжение и публикацию вашего решения, это было полезно. Я хотел сказать, что вы можете избежать своего решения для фильтра, добавив второй «маршрут», который направляет все сообщения без значения для вашего ключа маршрутизации, как описано здесь: http://logging.apache.org/log4j/2.x/faq.html#separate_log_files

Таким образом, ваша обновленная конфигурация log4j будет выглядеть так.

<appenders>
    <appender name="applicationAppender" type="RollingFile" fileName="${logFileName}" filePattern="${logFileNamePattern}" bufferedIO="true" immediateFlush="true" append="true">
        <layout type="PatternLayout" pattern="${logPattern}" />
        <Policies>
            <TimeBasedTriggeringPolicy />
            <SizeBasedTriggeringPolicy size="${logFileSize}" />
        </Policies>
        <DefaultRolloverStrategy max="${logFileCount}" />
    </appender>

    <Routing name="contextSpecificAppender">
        <Routes pattern="$${ctx:contextId}">
            <Route>
                <appender name="Rolling-${ctx:contextId}" type="RollingFile" fileName="logs/${ctx:contextId}.log" filePattern="${logFileNamePattern}" bufferedIO="true" immediateFlush="true" append="true">
                    <layout type="PatternLayout" pattern="${logPattern}" />
                    <Policies>
                        <TimeBasedTriggeringPolicy />
                        <SizeBasedTriggeringPolicy size="${logFileSize}" />
                    </Policies>
                    <DefaultRolloverStrategy max="${logFileCount}" />
                </appender>
            </Route>
            <Route ref="applicationAppender" key="$${ctx:contextId}">
            </Route>
        </Routes>
    </Routing>
</appenders>

<loggers>
    <root level="info">
        <appender-ref ref="contextSpecificAppender"/>
    </root>
</loggers>

И в вашем приложении вы можете просто установить ThreadContext, вызвав ThreadContext.put("contextId", "something") и очистить его, когда закончите, вызвав ThreadContext.clear() ИЛИ ThreadContext.remove("contextId")

Наконец, я использовал

<RollingFile>

элемент (например, приведенные выше примеры) вместо

<appender type="RollingFile"> 

элемент, который вы использовали. Я считаю, что это предпочтительнее при переходе на log4j2 с log4j.

person Alex    schedule 13.06.2014