以编程方式加载Log4j2配置文件

50

我希望能够通过我的应用程序以编程方式加载Log4j2 XML配置文件。

已尝试:

ConfigurationSource source = new ConfigurationSource();
source.setLocation(logConfigurationFile);
Configurator.initialize(null, source);

还有这个:

ConfigurationSource source = new ConfigurationSource();
source.setLocation(logConfigurationFile);
ConfigurationFactory factory = (ConfigurationFactory) XMLConfigurationFactory.getInstance().getConfiguration(source);
ConfigurationFactory.setConfigurationFactory(factory);

但是目前什么都没起作用。


1
随着log4j2中引入的所有不兼容的更改,指定您正在使用的log4j2 api和core的版本是有意义的... - Yordan Georgiev
9个回答

45

对于log4j的最新版本,以下是用于加载外部 log4j2.xml 文件的方法:

String log4jConfigFile = System.getProperty("user.dir") + File.separator + "log4j2.xml";
ConfigurationSource source = new ConfigurationSource(new FileInputStream(log4jConfigFile));
Configurator.initialize(null, source);

18
你可以简单地使用 Configurator.initialize(null, "fileName"); - Conor Svensson
4
为什么要将null传递给Configurator.initialize()函数? - Gaurav

25

如果您只有一个主入口点,这个代码片段可能会为您节省一些麻烦。必须在创建任何记录器之前触发set属性调用。此方法适用于类路径上的文件。

public class TestProcess {
    static {
        System.setProperty("log4j.configurationFile", "log4j-alternate.xml");
    }

    private static final Logger log = LoggerFactory.getLogger(TestProcess.class);

}

这个很好地解决了,我试图在构造函数上设置属性,但是它没有生效。静态块解决了问题。 - another
我曾经使用了 setProperty() 方法,但是没有在 "static" 块中调用。一旦我将该调用放在 static 块中,它就起作用了。谢谢! - mvsagar
更改系统属性不会影响其他记录器吗? - mtk

18

我自己找到了答案。或许有人会发现它有用。

ConfigurationSource source = new ConfigurationSource();
source.setLocation(logConfigurationFile);
source.setFile(new File(logConfigurationFile));
source.setInputStream(new FileInputStream(logConfigurationFile));
Configurator.initialize(null, source);

你的应用程序是一个Web应用吗?我还应该提到,我也在使用slf4j来抽象log4j2。 - ColinMc
是的,它是一个Web应用程序。我还使用了slf4jjcl日志路由到log4j2 - HashimR
让我们在聊天中继续这个讨论 - HashimR
1
好的,很酷。这个答案基于Servlet 2.5...不管怎样,很棒它能工作! - HashimR
为什么要将null传递给Configurator.initialize()? - Gaurav
显示剩余5条评论

11

以下方法适用于我,使用SLF4J包装器的Log4j2:

解决方案1:

public class MyClass {

    static {
        try {
            InputStream inputStream = new FileInputStream("C:/path/to/log4j2.xml");
            ConfigurationSource source = new ConfigurationSource(inputStream);
            Configurator.initialize(null, source);
        } catch (Exception ex) {
            // Handle here
        }
    }

    private static final Logger LOGGER = LoggerFactory.getLogger(MyClass.class); // LogManager if not using SLF4J

    public void doSomething() {
        LOGGER.info(...)
    }

}

解决方案2:

static {
    File log4j2File = new File("C:/path/to/log4j2.xml");
    System.setProperty("log4j2.configurationFile", log4j2File.toURI().toString());
}

需要使用toURI()方法遵循文件URI方案格式,否则会抛出MalformedURLException异常。 来源:

如果我的文件是 .properties 文件呢? - Jerry
@silver 为什么要将 null 传递给 Configurator.initialize()? - Gaurav

4
如果您正在使用 Servlet 3.0 Web 应用程序,则可以使用 Log4jServletContextListener 并执行以下操作:
编写一个自定义的 LogContextListener,它继承自 Log4jServletContextListener,在您的 web.xml 中设置它并禁用自动初始化。
<listener>
    <listener-class>com.example.LogContextListener</listener-class>
</listener>
<context-param>
    <param-name>isLog4jAutoInitializationDisabled</param-name>
    <param-value>true</param-value>
</context-param>

在您的自定义LogContextListener中,覆盖contextInitialized并设置配置文件位置。
public void contextInitialized(ServletContextEvent event) { 
    /* Some logic to calculate where the config file is saved. For 
     * example you can read an environment variable.
     */
    String pathToConfigFile = ... + "/log4j2.xml";
    Configurator.initialize(null, pathToConfigFile);
    super.contextInitialized(event);
}

优点是,通过在web.xml中直接配置位置的方式,您可以基于其他信息计算路径并访问log4j2.xml文件,即使它在类路径之外也可以。

我使用类似的解决方案来处理log4j 1.2.x,但是无法在2.14.x中生效。原因是我的“LogContextListener”在log4j2已经使用WARN StatusLogger No Log4j 2 configuration file found. Using default configuration...初始化后才创建。我猜log4j v1曾经也是这样的,但是通过PropertyConfigurator.configure(logConfigProperties)重新配置当前的log4j,但是使用Configurator.initialize(null, logConfigSource)不会发生这种情况。我的监听器是web.xml中的第一个,但是在TLD文件中还有其他先被触发的。 - Vojtěch Zavřel

3

为什么要将null传递给Configurator.initialize()? - Gaurav
请检查上面的Javadoc链接。 - Cristian Florescu
第一个参数是类加载器,但Javadoc没有提供更多信息;如果我正确理解了代码,如果为null,则将使用用于加载log4j2的类加载器(更准确地说是Configurator.class.getClassloader())。 - user1075613

3
如果您使用 .properties 文件进行配置:
String propertiesFile = "./test/Configuration/log4j2.properties";  
ConfigurationSource source = new ConfigurationSource(new FileInputStream(propertiesFile), new File(propertiesFile));
Configurator.initialize(null, source);

1
final URL log4j = Resources.getResource("log4j2-test.xml");
LoggerContext.getContext(false)
  .start(new XmlConfiguration(new ConfigurationSource(
    Resources.asByteSource(log4j).openStream(),
    log4j)));

这里提醒一下 - 在测试时要小心使用"log4j2-test.xml"。如果它在log4j2检查的任何文件夹中,它将覆盖"log4j2.xml"并使您认为您的覆盖已经生效,但实际上并没有。我没有尝试过你的代码,但我曾经在以前的代码片段中遇到过这个问题。我当时使用"log4j2-test.xml",认为一切都会在运行时覆盖,结果发现它之所以覆盖是因为log4j2首先寻找"log4j2-test.xml",然后才是"log4j2.xml"。https://logging.apache.org/log4j/2.0/manual/configuration.html#AutomaticConfiguration - adprocas
@TJR,使用new CompositeConfiguration()代替new XmlConfiguration()也可以工作吗? - Gaurav

0

我尝试了所有其他的解决方案,但都没有起作用。

我尝试了扩展Log4jServletContextListenerImpl的Log4jServletContextListener @lonxx所说的方法,可以写日志,但无法从spring上下文中读取环境bean。

因为log4j上下文是在spring上下文初始化之前初始化的。

所以将以下代码放置在InitServlet extends HttpServlet的init(ServletConfig config)中。

LoggerContext context = (LoggerContext) 
LogManager.getContext(false);
File file = new File("relativepath/log4j2/log4j2.xml");
context.setConfigLocation(file.toURI());

现在它正在工作。

使用Spring 5.3.22&log4j2 2.17.2,initservlet已在web.xml版本="2.5"中配置。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接