【问题标题】:Log4j2: custom plugin not working after switchign to RoutingLog4j2:切换到路由后自定义插件不起作用
【发布时间】:2020-05-18 23:49:03
【问题描述】:

我正在使用通过 PluginManager 类以编程方式注册的自定义插件来加载地图中的值并根据地图的内容更改我的 RollingFileAppender 的文件名。

然后我将配置更改为使用路由附加程序,以便能够使用 ThreadContext 内容进一步更改文件名(基本上,我希望我的应用程序的每个线程都有一个日志文件)。

但是,自从我开始使用路由附加程序以来,我的自定义插件不再产生所需的结果,尽管我根本没有更改它。基本上,它返回的是空值。

我的 log4j2-configuration.xml 看起来像这样:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug">
    <Appenders>
        <Console name="STDOUT">
            <PatternLayout pattern="%m%n"/>
            <ThresholdFilter level="debug"/>
        </Console>

        <Routing name="contextSpecificAppender">
            <Routes pattern="$${ctx:threadName}">
                <!-- This route is chosen if ThreadContext has no value for key threadName -->
                <Route key="$${ctx:threadName}">
                    <RandomAccessFile
                        name="applicationAppender"
                        fileName="${sys:catalina.base}/logs/${propertymap:warName}.log"
                        filePattern="${sys:catalina.base}/logs/%d{yyyy-MM-dd}/${propertymap:warName}.%i.log"
                        append="true"
                    >
                        <PatternLayout>
                            <Pattern>%d{yyyy-MM-dd HH:mm:ss} %highlight{%p} %style{[%t]}{magenta} %style{%c{1}:%L}{cyan,bold} - %m%n</Pattern>
                        </PatternLayout>
                        <Policies>
                            <TimeBaseTriggeringPolicy />
                            <SizeBasedTriggeringPolicy size="100 MB" />
                        </Policies>
                        <DefaultRolloverStrategy max="1" />
                    </RandomAccessFile>
                </Route>
                <Route>
                    <RandomAccessFile
                        name="${ctx:threadName}"
                        fileName="${sys:catalina.base}/logs/${propertymap:warName}-${ctx:threadName}.log"
                        filePattern="${sys:catalina.base}/logs/%d{yyyy-MM-dd}/${propertymap:warName}-${ctx:threadName}.%i.log"
                        append="true"
                    >
                        <PatternLayout>
                            <Pattern>%d{yyyy-MM-dd HH:mm:ss} %highlight{%p} %style{[%t]}{magenta} %style{%c{1}:%L}{cyan,bold} - %m%n</Pattern>
                        </PatternLayout>
                        <Policies>
                            <TimeBaseTriggeringPolicy />
                            <SizeBasedTriggeringPolicy size="100 MB" />
                        </Policies>
                        <DefaultRolloverStrategy max="1" />
                    </RandomAccessFile>
                </Route>
            </Routes>
        </Routing>
    </Appenders>
    <Loggers>
        <Root level="debug">
            <AppenderRef ref="contextSpecificAppender" />
            <!--<AppenderRef ref="STDOUT"/>-->
        </Root>
        <Logger name="org.apache.axis2.enterprise" level="info">
            <AppenderRef ref="contextSpecificAppender" />
        </Logger>
        <Logger name="de.hunsicker.jalopy.io" level="fatal">
            <AppenderRef ref="contextSpecificAppender" />
        </Logger>
        <Logger name="httpclient.wire.header" level="fatal">
            <AppenderRef ref="contextSpecificAppender" />
        </Logger>
        <Logger name="org.apache.commons.httpclient" level="info">
            <AppenderRef ref="contextSpecificAppender" />
        </Logger>
    </Loggers>
</Configuration>

负责加载地图的类在这里:

package com.my.package;

import java.net.URISyntaxException;
import java.net.URL;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.plugins.util.PluginManager;

/**
 *
 * @author ixm
 */
public class Log4jInitiliazer {
    public static void initialize(String warName, URL log4jProperties) {
        PropertyMapLookup.propertyMap.put("warName", warName);

        PluginManager.addPackage(PropertyMapLookup.class.getPackageName());

        LoggerContext context = (LoggerContext) LogManager.getContext(false);
        try {
            context.setConfigLocation(log4jProperties.toURI());
        } catch (URISyntaxException ex) {
            ex.printStackTrace();
        }
    }
}

它是从 ServletContextListener 调用的:

Log4jInitiliazer.initialize(warName, servletContextEvent.getServletContext().getResource("/WEB-INF/classes/log4j2-configuration.xml"));

最后,插件本身在这里:

package com.my.package;

import java.util.HashMap;
import java.util.Map;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.lookup.StrLookup;

/**
 *
 * @author ixm
 */
@Plugin(name="propertymap", category=StrLookup.CATEGORY)
public class PropertyMapLookup implements StrLookup {
    public static Map<String, String> propertyMap = new HashMap();

    @Override
    public String lookup(String arg0) {
        return propertyMap.getOrDefault(arg0, "");
    }

    @Override
    public String lookup(LogEvent arg0, String arg1) {
        return propertyMap.getOrDefault(arg0, "");
    }

}

生成的日志文件名都是“/var/log/tomcat9/-theNameOfTheThread.log”,而应该是“/var/log/tomcat9/theNameOfTheWar-theNameOfTheThread.log”。

我尝试了一些没有帮助的方法:

目前,我使用 xml 文件在 tomcat 中部署应用程序,该文件包含可使用 ${web:initParam.warName} 访问的上下文参数,但这对我来说并不实际。

是否有一种嵌入式方法可以检索正在部署的战争的名称,而无需使用“自定义”参数方法?

谢谢!

【问题讨论】:

  • 你试过 ${web:contextPath} 吗?虽然不能保证上下文路径与您的战争名称匹配,但它们在 Tomcat 中默认匹配。您从 ServletContextListener 中的哪里获取 warName 的值?
  • @rgoers 实际上,我在发布我的问题几分钟后发现了有关 contextPath 的信息。确实,这解决了全球问题,但我仍然不明白为什么插件不再工作。我会把这个问题留几天,如果没有答案,我会邀请你发表你的评论作为答案:) 谢谢!

标签: java log4j log4j2


【解决方案1】:

当它停止工作时,它正在重新创建一个名为“-threadId.log”的文件,这表明您的查找正在被调用。如果不是,文件名将是“${propertyMap:warName}-thr​​eadId.log”。所以这意味着您存储的warName 是错误的,或者您的查找有问题。我能想到两种可能。

  1. 你没有说明战争名称是如何产生的。那里可能有问题。
  2. 您的 Lookup 可能存在于 2 个不同的类加载器中。如果发生这种情况,您将在 servlet 上下文初始化程序中初始化一个,但在记录时使用另一个。

如果是我,我会调试并确保在添加战争名称和查找它时使用相同的地图。

正如我在上面的评论中所指出的,您还可以将 Web 查找的 contextPath 键用作 ${web:contextPath}。这并不总是与战争名称相同,但在默认的 Tomcat 环境中应该是相同的。

【讨论】:

  • 我会将此标记为答案,因为我确实最终使用了 web.contextPath 变量。战争名称是在别处计算的,我使用系统日志中显示的简单 system.out.println 验证了该值是否正确。但是,部署在同一个 tomcat 中的多个 war 文件使用同一个 Log4jInitializer 类。我仍然不确定类加载器是如何工作的,所以这可能有问题吗?感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 2021-09-04
  • 2016-05-10
  • 1970-01-01
  • 2013-11-04
  • 2014-01-28
  • 2019-01-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多