【发布时间】:2012-08-28 15:16:55
【问题描述】:
Log4j default initialization 通过一个过程来查找和使用 URL 进行配置。之后,您如何找出最终使用的 URL,而不必自己编写相同的程序? (如果您必须自己编写代码,您可能无法得到与 log4j 完全相同的代码,而且它可能会在未来的版本中发生变化。)
【问题讨论】:
Log4j default initialization 通过一个过程来查找和使用 URL 进行配置。之后,您如何找出最终使用的 URL,而不必自己编写相同的程序? (如果您必须自己编写代码,您可能无法得到与 log4j 完全相同的代码,而且它可能会在未来的版本中发生变化。)
【问题讨论】:
如果你愿意使用 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);
}
}
这段代码的意思是:
OptionConverter.selectAndConfigure被调用,如果没有默认 URL,则不会打印任何内容。您可以将其分配给任何类的静态成员或任何您喜欢的东西,而不是打印 URL。
这是您的问题的解决方案,我对其进行了测试,并且可以正常工作。我很乐意收到回答您问题的赏金,即使该解决方案可能使用了您没有想到的技术。但它解决了问题。 :-)
编辑: 在没有找到默认 URL 的情况下,也可以显式拦截日志调用,尽管我认为没有必要。我只是想提一下。
【讨论】:
使用的过程在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 时,这将无济于事。
【讨论】:
如果您可以设置自己的配置器,则可以执行以下操作:
设置 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);
}
}
【讨论】:
将此插入到您的 java 调用中:
-Dlog4j.configDebug=true
就是这样。
【讨论】: