Log4J2 - 在运行时分配用户特定的文件appender文件名

时间:2014-04-25 21:57:13

标签: xml eclipse logging log4j log4j2

我想使用LogJ4 2中的XML配置文件来登录我的projects文件夹中的某个文件。这是一个共享项目,所以我不能只编写到所需位置的完整路径。 根据编译程序的机器,路径必须是动态的,以便始终正确生成日志文件。

我已经在这里阅读了多种方法,但它们都没有为我工作。

这是我的整体结构: src:log.class config文件夹:log4j2.xml logs:Id就像要写在这里的日志文件

XML源代码如下所示:

<properties>
    <property name="logFilePosition">${sys:logFilename}</property>
</properties>

<File name="OutputFile" fileName="${logFilePosition}" immediateFlush="true">
        <PatternLayout>
            <Pattern>%d{HH:mm:ss} %m; %n</Pattern>
        </PatternLayout>
</File>

<Root level="trace">
        <AppenderRef ref="Console" level="trace" />
        <AppenderRef ref="OutputFile" level="trace" />
</Root>

我也尝试使用fileName="${sys:logFilename}",但也无效。 此XML不完整,但会显示相关部分。

我的Log类看起来像这样:

private static Logger logger = LogManager.getLogger(Log.class.getName());
LoggerContext ctx = (LoggerContext)LogManager.getContext(false);

// Returns current location based on individual location
private static URL location = Log.class.getProtectionDomain().getCodeSource().getLocation();
private static String final_location = location.toString().substring(6, location.toString().length());

    Log(){
        // Sets system property required for XML configuration file in order to reference desired output location
        // of log file by the use of "logFilename"
        System.setProperty("logFilename", final_location+"logs/mylog.log");

        //LoggerContext ctx =   (LoggerContext)LogManager.getContext(false);
        ctx.reconfigure();

如果我只做System.out.println(final_location+"logs/mylog.log");,则会显示正确的路径。如果我只是复制此路径并将其直接插入XML文件,则会生成一个日志文件。

问题似乎是在Log()构造函数之外,就像在此类的其他方法中一样,属性“logFilename”为null。我怎么能绕过这个?

我在这里缺少什么?我非常感谢任何帮助。

谢谢, 亚历

1 个答案:

答案 0 :(得分:0)

虽然很久以前就提出这个问题,但我现在正在阅读它,而且我有一个有效的解决方案。似乎问题在于定义系统属性的时间。您在设置系统属性之前通过在类的顶部执行以下操作来调用log4j2 API:

private static Logger logger = LogManager.getLogger(Log.class.getName());
LoggerContext ctx = (LoggerContext)LogManager.getContext(false);

如果您在设置系统属性之前调用logger上的方法(在调用构造函数之前 - 可能在静态方法中),那么您当然会看到该属性为null。

解决方案是确保在开始使用log4j之前定义系统属性。这是一个有效的例子:

package example;

import java.net.URL;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LoggerContext;

public class Log4j2JvmProp {

    //This static block will run before anything else and ensure that the system property is
    // set before log4j is initialized or used.
    static{
        URL location = Log4j2JvmProp.class.getProtectionDomain().getCodeSource().getLocation();
        String final_location = location.toString().substring(6, location.toString().length());
        System.setProperty("logFilename", final_location+"logs/mylog.log");
    }

    private static Logger logger = LogManager.getLogger();
    static LoggerContext ctx = (LoggerContext)LogManager.getContext(false);

    public static void main(String[] args) {
        logger.info("Hello world!");
    }
}

这是log4j2.xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <File name="logFile" fileName="${sys:logFilename}" immediateFlush="false"
            append="true">
            <PatternLayout
                pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
        </File>
    </Appenders>

    <Loggers>
        <Root level="trace">
            <AppenderRef ref="logFile" />
        </Root>
    </Loggers>
</Configuration>

作为代码状态中的注释,当运行此注释时,静态块将首先执行并在发生任何其他事件之前设置系统属性。即使你从其他类调用此类的静态方法,静态块仍然首先执行,因为它在类本身被加载时运行。