【问题标题】:How can you find what URL was used in log4j default initialization?如何找到 log4j 默认初始化中使用的 URL?
【发布时间】:2012-08-28 15:16:55
【问题描述】:

Log4j default initialization 通过一个过程来查找和使用 URL 进行配置。之后,您如何找出最终使用的 URL,而不必自己编写相同的程序? (如果您必须自己编写代码,您可能无法得到与 log4j 完全相同的代码,而且它可能会在未来的版本中发生变化。)

【问题讨论】:

    标签: java logging log4j


    【解决方案1】:

    如果你愿意使用 AspectJ LTW(加载时编织),你可以看看 Ian Roberts 提到的 LogManager 的静态初始化。在 log4j 1.2.14 中看起来像这样:

    static {
        // (...)
        // if there is no default init override, then get the resource
        // specified by the user or the default config file.
        if (override == null || "false".equalsIgnoreCase(override)) {
            // (...)
            URL url = null;
    
            // (...)    
            // If we have a non-null url, then delegate the rest of the
            // configuration to the OptionConverter.selectAndConfigure method.
            if (url != null) {
                LogLog.debug("Using URL [" + url + "] for automatic log4j configuration.");
                OptionConverter.selectAndConfigure(
                    url, configuratorClassName, LogManager.getLoggerRepository()
                );
            } else {
                LogLog.debug("Could not find resource: [" + configurationOptionStr + "].");
            }
        }
    }
    

    显然,如果可以确定默认 URL,那么将在静态块内的某一点调用 OptionConverter.selectAndConfigure(URL, ..),以便使用该 URL 初始化 log4j

    通过AspectJ,捕获该方法调用非常简单:

    import java.net.URL;
    import org.apache.log4j.helpers.OptionConverter;
    import org.apache.log4j.LogManager;
    
    public aspect Log4jAspect {
        before(URL defaultURL) :
            within(LogManager) &&
            cflow(staticinitialization(LogManager)) &&
            call(* OptionConverter.selectAndConfigure(URL, ..)) &&
            args(defaultURL, ..)
        {
            System.out.println("log4j default URL = " + defaultURL);
        }
    }
    

    这段代码的意思是:

    • 如果我们在 LogManager 类中并且
    • 在静态类初始化和控制流程中
    • OptionConverter.selectAndConfigure被调用,
    • 然后捕获第一个参数(URL)和
    • 将其打印到控制台(您也可以执行其他操作)。

    如果没有默认 URL,则不会打印任何内容。您可以将其分配给任何类的静态成员或任何您喜欢的东西,而不是打印 URL。

    这是您的问题的解决方案,我对其进行了测试,并且可以正常工作。我很乐意收到回答您问题的赏金,即使该解决方案可能使用了您没有想到的技术。但它解决了问题。 :-)


    编辑: 在没有找到默认 URL 的情况下,也可以显式拦截日志调用,尽管我认为没有必要。我只是想提一下。

    【讨论】:

    • 您的回答非常有用。谢谢你。一种处理显然无法完全按照我想要的方式完成的事情的好方法。
    • >每天 1000 次。令人印象深刻:P
    【解决方案2】:

    使用的过程在LogManager 的静态初始化块中硬编码,因此似乎没有办法挂钩。它告诉你发生了什么的唯一地方是

    LogLog.debug("Using URL ["+url+"] for automatic log4j configuration.");
    

    但是LogLog 本身是硬编码的,可以将System.out.println 用于这些消息,所以我能看到的唯一可能性是打开调试(-Dlog4j.debug=true)并在 log4j 初始化之前以某种方式连接到System.setOut,然后解析调试日志消息。但这可能比自己编写配置过程还要脆弱。

    即便如此,在默认配置过程之后可能已经应用了其他编程配置(例如 Spring Log4jConfigListener) - 不一定只有一个配置 URL。

    可能值得放入 log4j 功能请求以将配置文件搜索代码分解为一个静态方法,您可以从其他地方调用该方法,但是当您可能不得不处理较早版本的 log4j 时,这将无济于事。

    【讨论】:

      【解决方案3】:

      如果您可以设置自己的配置器,则可以执行以下操作:

      设置 JAVA 系统属性:-Dlog4j.configuratorClass=MyConfigurator 然后让您的配置器实例拦截 doConfigure 调用。

      public class MyConfigurator implements Configurator
      {
          public static URL url;
      
          @Override
          public void doConfigure(URL url, LoggerRepository repository)
          {
              this.url = url;
              new PropertyConfigurator().doConfigure(url, repository);
          }
      }
      

      【讨论】:

      • 这提出了另一个聪明的想法:通过在类路径中插入你自己的来覆盖配置器......
      【解决方案4】:

      将此插入到您的 java 调用中:

      -Dlog4j.configDebug=true
      

      就是这样。

      【讨论】:

      • 我正在寻找一种方法让我的程序找到这些信息。
      猜你喜欢
      • 1970-01-01
      • 2011-06-08
      • 1970-01-01
      • 1970-01-01
      • 2017-11-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-29
      相关资源
      最近更新 更多