带有路由 Appender 和 ThreadContext 的 Log4J2

问题描述 投票:0回答:1

我对 log4j2 和路由附加程序有一些问题。

我的应用程序生成不同的线程,并且在每个线程中我都会执行类似的操作

ThreadContext.put("threadName", processName); 

processName会根据不同的信息在线程内生成。但这并不重要,唯一重要的信息是,processName在每个线程中都会不同。

现在我有了这个 log4j2.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
    <Appenders>
        <Console name="console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{ABSOLUTE} %-5p [%-40.-40c{1}] %m%n"/>
        </Console>
        <Routing name="RoutingAppender">
            <Routes pattern="${ctx:threadName}">
                <Route>
                    <RollingFile name="Rolling-${ctx:threadName}"
                                 fileName="../log/${ctx:threadName}.log"
                                 filePattern="../log/${ctx:threadName}-%d{yyyy-MM-dd}.log"
                                 createOnDemand="true">
                        <PatternLayout pattern="%d{ABSOLUTE} %-5p [%-40.-40c{1}] %m%n"/>
                        <Policies>
                            <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                        </Policies>
                    </RollingFile>
                </Route>
            </Routes>
        </Routing>
    </Appenders>
    <Loggers>
        <Logger name="de.company" level="INFO" additivity="false">
            <AppenderRef ref="RoutingAppender"/>
            <AppenderRef ref="console"/>
        </Logger>
        <Logger name="com.company" level="INFO" additivity="false">
            <AppenderRef ref="RoutingAppender"/>
            <AppenderRef ref="console"/>
        </Logger>
        <Root level="INFO">
            <AppenderRef ref="RoutingAppender"/>
            <AppenderRef ref="console"/>
        </Root>
    </Loggers>
</Configuration>

log4j 为每个线程创建日志文件,没有任何问题。

不幸的是,log4j 将创建一个名为 ${ctx:threadName}.log 的日志文件。在此日志文件中,将显示来自 JSCH 的日志消息。我猜 JSCH 将创建自己的线程,并且由于 JSCH 不会将 ThredName 放入 ThreadContext,log4j 无法替换该变量并使用配置中的变量作为日志文件名称。

我的目标是将 ThreadContext 中没有线程名的所有日志文件写入单个日志文件。

我在 log4j 的文档中找到了这个链接

https://logging.apache.org/log4j/log4j-2.2/faq.html#separate_log_files

但问题是,在该示例中他们知道 threadName 的值是什么。就我而言,我不知道其价值。

ChatGPT 告诉我,我必须更改元素内的 pattern 并使用像这样的双 $

<Routes pattern="$${ctx:threadName}">

我可以像这样添加第二个元素

<Routing name="RoutingAppender">
            <Routes pattern="$${ctx:threadName}">
                <Route key="$${ctx:threadName}">
                    <RollingFile name="Rolling-${ctx:threadName}"
                                 fileName="../log/${ctx:threadName}.log"
                                 filePattern="../log/${ctx:threadName}.log-%d{yyyy-MM-dd}"
                                 createOnDemand="true">
                        <PatternLayout pattern="%d{ABSOLUTE} %-5p [%-40.-40c{1}] %m%n"/>
                        <Policies>
                            <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                        </Policies>
                    </RollingFile>
                </Route>            
                <Route key="null">
                    <RollingFile name="Rolling-service" 
                                fileName="../log/default-service.log" 
                                filePattern="../log/default-service.log-%d{yyyy-MM-dd}" 
                                createOnDemand="true">
                        <PatternLayout pattern="%d{ABSOLUTE} %-5p [%-40.-40c{1}] %m%n"/>
                        <Policies>
                            <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                        </Policies>
                    </RollingFile>
                </Route>            

            </Routes>
        </Routing>

但这不起作用 - 我猜 log4j 将 null 解释为字符串。第二种方法是像这样从后备路线中删除关键属性

            <Routes pattern="$${ctx:threadName}">
                <Route key="$${ctx:threadName}">
                    <RollingFile name="Rolling-${ctx:threadName}"
                                 fileName="../log/${ctx:threadName}.log"
                                 filePattern="../log/${ctx:threadName}.log-%d{yyyy-MM-dd}"
                                 createOnDemand="true">
                        <PatternLayout pattern="%d{ABSOLUTE} %-5p [%-40.-40c{1}] %m%n"/>
                        <Policies>
                            <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                        </Policies>
                    </RollingFile>
                </Route>
                <Route>
                    <RollingFile name="Rolling-service"
                                fileName="../log/default-service.log"
                                filePattern="../log/default-service.log-%d{yyyy-MM-dd}"
                                createOnDemand="true">
                        <PatternLayout pattern="%d{ABSOLUTE} %-5p [%-40.-40c{1}] %m%n"/>
                        <Policies>
                            <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
                        </Policies>
                    </RollingFile>
                </Route>

            </Routes>

但在这种情况下,所有日志消息都将存储在default-service.log中。

有人知道在这种情况下该怎么办吗?

非常问候并感谢您的帮助!

java log4j2
1个回答
0
投票

路由附加器很棘手,因为除了

<Route>
的内容之外的所有内容都是在编译时插入的,而如果选择该路由,则将评估
<Route>
的内容。因此,如果您有:

<Routing name="RoutingAppender">
  <Routes pattern="$${ctx:threadName}">
    <Route key="$${ctx:threadName}">
      <!-- Content 1 -->
    </Route>
    <Route>
      <!-- Content 2 -->
    </Route>
  </Routes>
</Routing>

它将在配置时进行插值以获得:

<Routing name="RoutingAppender">
  <Routes pattern="${ctx:threadName}">
    <Route key="${ctx:threadName}">
      <!-- Content 1 unchanged -->
    </Route>
    <Route>
      <!-- Content 2 unchanged -->
    </Route>
  </Routes>
</Routing>

在运行时,模式

${ctx:threadName}
将被插值并与非插值键进行匹配。因此:

  • 如果
    ThreadContext.get("threadName")
    null
    ,则表达式
    ${ctx:threadName}
    将计算为自身
    ${ctx:threadName}
    并匹配 第一个(不是第二个)路线的键,
  • 如果
    ThreadContext.get("threadName")
    不是
    null
    ,则不会匹配任何键,并且将选择第二个(默认)路由。

解决方案

您可以:

  • 将第二条路线的内容替换为第一条路线(在您最后的尝试中),反之亦然,
  • 或为
    ${ctx:threadName}
    添加默认值并仅使用单个默认路由:
<Properties>
  <Property name="threadName" value="default-service"/>
</Properties>
<Appenders>
  <Routing name="RoutingAppender">
    <Routes pattern="$${ctx:threadName}">
      <Route>
        <RollingFile name="Rolling-${ctx:threadName}"
                     fileName="../log/${ctx:threadName}.log"
                     filePattern="../log/${ctx:threadName}.log-%d{yyyy-MM-dd}"
                     createOnDemand="true">
        ...
        </RollingFile>
      </Route>
    </Routes>
  </Routing>
</Appenders>
© www.soinside.com 2019 - 2024. All rights reserved.