【问题标题】:How can you combine log4j default initialization with configureAndWatch()?如何将 log4j 默认初始化与 configureAndWatch() 结合起来?
【发布时间】:2012-08-28 15:04:50
【问题描述】:

一些 Log4j 配置器有一个configureAndWatch() 方法,启动一个线程 来观察用于配置的文件(XML 文件或properties 文件)和如果文件更改触发重新配置。

但是,如果您依赖Log4j 默认初始化 过程,您将永远没有机会调用configureAndWatch()。您甚至不知道用于配置的文件(它甚至可能不是文件。)

有没有一种获得configureAndWatch() 风格行为的好方法,允许即时更改日志配置,同时仍使用默认初始化 过程?我假设您的配置 URL 最终会解析为可以观看的文件,因为另一台服务器上的 URL 可能不是您希望每 60 秒提取一次的东西。

(我看到configureAndWatch() 方法在Java EE 环境中是不安全的,因为它是单独的线程,我看到一些应用服务器有自己的机制来观看log4j配置文件,但我目前正在处理的程序没有在 Java EE 中运行。)

【问题讨论】:

    标签: java logging log4j


    【解决方案1】:

    我之前已经放弃了答案中的所有内容,现在重新开始,以便我更好地了解您想要什么。

    以下段落描述了 log4j 用来确定 log4j 配置文件名的真实顺序。此代码尝试遵循这些规则。

    http://logging.apache.org/log4j/1.2/manual.html#defaultInit

    它确定要使用的 log4j 配置文件,然后使用它找到的文件设置 PropertyConfigurator。这仅在文件位于文件系统上时才有效。如果文件在 jar 中或位于远程 HTTP URL 上,它将不起作用。

        String prop = System.getProperty("log4j.configuration");
        if (prop == null) prop = "log4j.properties";
        URL log4jConfig = Loader.getResource(prop);
        if (log4jConfig.getProtocol().equalsIgnoreCase("file")) {
            PropertyConfigurator.configureAndWatch(log4jConfig.getFile().substring(1), 10000);
        }
        else {
            // cannot monitor if file changed because URL is not a file
        }
    

    【讨论】:

    • 鉴于 log4j 目前的限制,我认为这个解决方案是最好的。我看到的唯一问题是,如果您自己获取属性,您可能会错过检查 log4j 在默认初始化中检查的所有内容。
    • 重要:不要在 linux 上使用 substring(1),否则会搞砸路径。
    • 我还必须在 OSX 上运行的 Windows 上删除 substring(1),因为文件的路径以 \\psf 开头(从 OSX 文件系统挂载)
    • configureAndWatch 创建新线程,不适合 jee 系统
    【解决方案2】:

    但是,如果您依赖 Log4j 默认初始化过程,您将永远没有机会调用 configureAndWatch()。您甚至不知道用于配置的文件(它甚至可能不是文件。)

    您已经发布了一些我已经在[1][2] 中回答的相关问题,所以这个答案是建立在其他人的解释之上的。同样,我正在使用 AspectJ:

    示例应用程序类:

    import org.apache.log4j.Logger;
    
    public class Log4jDemo {
        private static Logger logger = Logger.getLogger("scrum-master.de");
    
        public static void main(String[] args) throws InterruptedException {
            while (true) {
                logger.info("Log message");
                Thread.sleep(2000);
            }
        }
    }
    

    Aspect 捕获默认 URL 并将其交给configureAndWatch

    import java.io.File;
    import java.net.URISyntaxException;
    import java.net.URL;
    import org.apache.log4j.helpers.OptionConverter;
    import org.apache.log4j.LogManager;
    import org.apache.log4j.PropertyConfigurator;
    import org.aspectj.lang.SoftException;
    
    public aspect Log4jAspect {
        after(URL defaultURL) returning :
            within(LogManager) &&
            cflow(staticinitialization(LogManager)) &&
            call(* OptionConverter.selectAndConfigure(URL, ..)) &&
            args(defaultURL, ..)
        {
            try {
                PropertyConfigurator.configureAndWatch(new File(defaultURL.toURI()).getAbsolutePath(), 2000);
            } catch (URISyntaxException e) {
                throw new SoftException(e);
            }
        }
    }
    

    我用 Eclipse 的-Dlog4j.configDebug=true 对其进行了测试,在 log4j.properties 中将输出通道从System.log(Eclipse 控制台中的黑色)更改为System.err(红色,此处不可见) ,效果很好。在日志输出中,您还可以看到检测到文件更改后重新配置的调试信息:

    log4j: Trying to find [log4j.xml] using context classloader sun.misc.Launcher$AppClassLoader@f4a24a.
    log4j: Trying to find [log4j.xml] using sun.misc.Launcher$AppClassLoader@f4a24a class loader.
    log4j: Trying to find [log4j.xml] using ClassLoader.getSystemResource().
    log4j: Trying to find [log4j.properties] using context classloader sun.misc.Launcher$AppClassLoader@f4a24a.
    log4j: Using URL [file:/C:/Dokumente%20und%20Einstellungen/Robin/Eigene%20Dateien/java-src/dummy2/bin/log4j.properties] for automatic log4j configuration.
    log4j: Reading configuration from URL file:/C:/Dokumente%20und%20Einstellungen/Robin/Eigene%20Dateien/java-src/dummy2/bin/log4j.properties
    log4j: Parsing for [root] with value=[debug, stdout].
    log4j: Level token is [debug].
    log4j: Category root set to DEBUG
    log4j: Parsing appender named "stdout".
    log4j: Parsing layout options for "stdout".
    log4j: Setting property [conversionPattern] to [%d{ABSOLUTE} %5p %c{1}:%L - %m%n].
    log4j: End of parsing for "stdout".
    log4j: Setting property [target] to [System.err].
    log4j: Parsed "stdout" options.
    log4j: Finished configuring.
    log4j: Parsing for [root] with value=[debug, stdout].
    log4j: Level token is [debug].
    log4j: Category root set to DEBUG
    log4j: Parsing appender named "stdout".
    log4j: Parsing layout options for "stdout".
    log4j: Setting property [conversionPattern] to [%d{ABSOLUTE} %5p %c{1}:%L - %m%n].
    log4j: End of parsing for "stdout".
    log4j: Setting property [target] to [System.err].
    log4j: Parsed "stdout" options.
    log4j: Finished configuring.
    17:48:20,944  INFO de:10 - Log message
    17:48:22,944  INFO de:10 - Log message
    17:48:24,944  INFO de:10 - Log message
    17:48:26,944  INFO de:10 - Log message
    log4j: Parsing for [root] with value=[debug, stdout].
    log4j: Level token is [debug].
    log4j: Category root set to DEBUG
    log4j: Parsing appender named "stdout".
    log4j: Parsing layout options for "stdout".
    log4j: Setting property [conversionPattern] to [%d{ABSOLUTE} %5p %c{1}:%L - %m%n].
    log4j: End of parsing for "stdout".
    log4j: Setting property [target] to [System.out].
    log4j: Parsed "stdout" options.
    log4j: Finished configuring.
    17:48:28,944  INFO de:10 - Log message
    17:48:30,944  INFO de:10 - Log message
    17:48:32,944  INFO de:10 - Log message
    17:48:34,944  INFO de:10 - Log message
    log4j: Parsing for [root] with value=[debug, stdout].
    log4j: Level token is [debug].
    log4j: Category root set to DEBUG
    log4j: Parsing appender named "stdout".
    log4j: Parsing layout options for "stdout".
    log4j: Setting property [conversionPattern] to [%d{ABSOLUTE} %5p %c{1}:%L - %m%n].
    log4j: End of parsing for "stdout".
    log4j: Setting property [target] to [System.err].
    log4j: Parsed "stdout" options.
    log4j: Finished configuring.
    17:48:36,944  INFO de:10 - Log message
    17:48:38,944  INFO de:10 - Log message
    

    【讨论】:

    • 谢谢大佬,我也有这个想法,可以在不修改代码的情况下修改当前的日志系统,我改用 Spring AOP。
    • 那你肯定是换了一种方式,因为Spring AOP既不支持cflow()也不支持call()。那么您是如何实施解决方案的呢?
    【解决方案3】:

    在使用默认记录器和附加程序之前,我已经完成了这项工作。您将不得不编写一个单独的处理程序来观察属性文件的修改。当发生更改时,只需重置您的 logger 并重新添加 appender 设置:

    /**
     * Setup log4j appender.
     */
    public static void setupLog4jAppender(Class appender) {
        getLogger();
    
        String rootLogger=(String)props.get("log4j.rootLogger");
        rootLogger+=",appname";
        getProps().setProperty("log4j.rootLogger", rootLogger);
    
        getProps().put("log4j.appender.appname", appender.getName());  // Our custom appender
        getProps().put("log4j.appender.appname.layout", "org.apache.log4j.PatternLayout"); 
        //See: http://logging.apache.org/log4j/docs/api/org/apache/log4j/PatternLayout.html
        getProps().put("log4j.appender.appname.layout.ConversionPattern", "%d{ABSOLUTE} %5p %c{1}:%L - %m%n");
    
        LogManager.resetConfiguration();
        PropertyConfigurator.configure(getProps());
    }
    

    【讨论】:

      猜你喜欢
      • 2011-06-08
      • 2014-02-19
      • 1970-01-01
      • 2015-03-22
      • 2013-01-17
      • 2015-06-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多