如何找到log4j默认初始化中使用的URL?[英] How can you find what URL was used in log4j default initialization?

本文是小编为大家收集整理的关于如何找到log4j默认初始化中使用的URL?的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

在url要配置.之后,您如何最终找到最终使用的URL,而不必自己编码相同的过程? (如果您必须自己编码,则可能不会与Log4J完全相同,并且它可能会在以后的版本中改变.)

推荐答案

如果您愿意使用fack j 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 .

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

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的情况下,也可以明确拦截日志调用,即使我认为没有必要.我只是想提一下.

其他推荐答案

所使用的过程在LogManager中的静态初始器块中进行了刻板编码,因此似乎没有一种方法可以钩住它.它告诉您发生的事情的唯一地方是

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

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

即使那样,在默认配置过程之后可能还应用了其他程序化配置(例如,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);
    }
}

本文地址:https://www.itbaoku.cn/post/1574805.html

问题描述

Log4j default initialization goes through a procedure to find and use a URL to configure with. Afterward, how can you find out what URL was ultimately used, without having to code the same procedure yourself? (If you have to code it yourself, you might not get it exactly the same as log4j does, and also it might change in a future release.)

推荐答案

If you are willing to use AspectJ LTW (load-time weaving), you can take a look at the static initialisation of LogManager mentioned by Ian Roberts. In log4j 1.2.14 it looks like this:

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 + "].");
        }
    }
}

Obviously if a default URL could be determined then OptionConverter.selectAndConfigure(URL, ..) will be called at one point within the static block in order to initialise log4j with that URL.

By means of AspectJ it is pretty simple to catch that method invocation:

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);
    }
}

In prose this code means:

  • If we are within class LogManager and
  • within the control flow of the static class initialisation and
  • OptionConverter.selectAndConfigureis called,
  • then capture the first argument (the URL) and
  • print it to the console (you could just as well do something else).

If there is no default URL, nothing will be printed. Instead of printing the URL you could assign it to a static member of any class or whatever you like.

This is a solution for your problem, I tested it and it works. I would be happy to receive the bounty for answering your question, even though maybe the solution uses a technology you did not have in mind. But it solves the problem. :-)


Edit: It is also possible to explicitly intercept the log call in the case that no default URL is found, even though I do not think it is necessary. I just wanted to mention it.

其他推荐答案

The procedure used is hard-coded in a static initializer block in LogManager, so there doesn't appear to be a way to hook into it. The only place where it tells you what's going on is

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

but LogLog itself is hard-coded to use System.out.println for these messages so the only possibility I can see is to switch on debugging (-Dlog4j.debug=true) and somehow hook in to System.setOut before log4j is initialized, then parse the debug log message. But that is probably even more fragile than coding the config procedure yourself.

Even then, there may have been other programmatic configuration applied after the default configuration procedure (e.g. a Spring Log4jConfigListener) - there isn't necessarily a single configuration URL as such.

It might be worth putting in a log4j feature request to factor out the config file search code into a static method that you can call from elsewhere, but that wouldn't help when you might have to cope with earlier versions of log4j.

其他推荐答案

If you can setup your own Configurator you can do something like that:

Setup the JAVA system property : -Dlog4j.configuratorClass=MyConfigurator And then have your configurator instance intercepts the doConfigure call.

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);
    }
}