更好的方式以编程方式初始化log4j2(TWO)的方法

4

这是我正在做的加载log4j2.xml文件的方式,从另一个属性文件中指定日志文件位置,以便支持目的保持配置简单:

MyProperties props = MyProperties.getInstance();
System.setProperty(MyConstants.AUDIT_LOG_ENV_VAR,
        props.getAuditLogFileName());
ConfigurationSource source =
        new ConfigurationSource(new FileInputStream(new File(
                System.getProperty(MyConstants.PROPERTIES_FILE_ENV_VAR)
                    + "/log4j2.xml")));
Configurator.initialize(null, source);

log4j2将会使用我的文件名,并且我已经在Appender中使用了环境变量替换指定了它:

<File name="AuditLogger" fileName="${sys:AUDIT_LOG}">
  <PatternLayout pattern="DATETIME  %d{yyyy-MM-dd HH:mm:ss:SSS zzz}%n%msg%n" />
</File>

然而,我不喜欢它,因为在下一行代码中读取文件名值之前,将其推送到系统环境中会有一种气味。
我喜欢使用ConfigurationFactory以编程方式配置log4j2,但似乎没有类似的方法来更多地构建log4j2.xml文件 - 或者还有其他方法吗?

你不使用Java 7+吗? - fge
我没有理解你的意图。如果您不想以编程方式设置属性,那么将其设置为JVM参数是否可行? - Andy Dufresne
@AndyDufresne,你到底在问什么?我很难用更加明确的语言来表达我的问题。最后两段是关键。 - Adam
1个回答

5
通常,log4j2团队不建议采用编程配置方式:API和核心JAR文件之间的分离是为了使开发人员清晰地区分API和实现。编程配置依赖于实现细节,因此可能会在未来的版本中出现问题。
如果有任何无法通过配置完成的事情,请告诉我们,以便我们改进。
对于您特定的用例:看起来您想将系统属性设置为特定值,并在基于该值初始化自己之前。如果这是一个独立的应用程序,避免依赖于log4j2的实现细节的一种方法是在引用任何log4j2类之前设置所有必要的系统属性。
public class MainWrapper {
    public static void main(String[] args) {
        System.setProperty("key", "value");
        // ... 
        ActualMain.main(args); // delegate to the actual start of the application
    }
}

我想提供一个备选方案,那就是创建你自己的自定义查找。通过log4j2 插件,只需几行代码即可完成。这种方法也依赖于一些实现细节,但其结果更加强大和可重用。

package com.mycompany;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.lookup.AbstractLookup;
import org.apache.logging.log4j.core.lookup.StrLookup;

/** Looks up keys in MyProperties singleton. */
@Plugin(name = "MyProperties", category = StrLookup.CATEGORY)
public class MyPropertiesLookup extends AbstractLookup {
    @Override public String lookup(final LogEvent event, final String key) {
        return MyProperties.getInstance().getValue(key);
    }
}

然后,在配置中,您可以使用自定义查找器而不是系统属性:
<File name="AuditLogger" fileName="${MyProperties:AUDIT_LOG}">
  <PatternLayout pattern="DATETIME  %d{yyyy-MM-dd HH:mm:ss:SSS zzz}%n%msg%n" />
</File>

感谢您对插件的出色回答,但这只是其中的一部分。我还在使用MyProperties来获取log4j2.xml的位置,该位置不在类路径上。您建议不要使用log4j2实现 - 那么您会如何处理呢? - Adam
我会使用系统属性指定配置文件的完整路径: -Dlog4j.configurationFile=path/to/log4j2.xml - Remko Popma
我猜你可能会这么说。但是我又回到了之前的同样情况——将一个变量推送到环境中,然后在一行代码后再读取它。这次是log4j2.xml文件位置而不是appender的日志文件。是否有一种插件技术可以告诉log4j2从哪里获取log4j2.xml? - Adam
是的,我理解了 - 我的意思是在从MyProperties获取值后,我必须在代码中设置log4j.configurationFile,然后log4j在下一行(大约)读取它。 - Adam
MyProperties 从哪里获取它的值?我试图理解为什么你需要从 MyProperties 获取 log4j2 配置位置,而不是从系统属性中获取... - Remko Popma
显示剩余3条评论

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