How can you find what URL was used in log4j default initialization?

后端 未结 4 411
执笔经年
执笔经年 2021-01-03 06:44

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

相关标签:
4条回答
  • 2021-01-03 07:12

    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.

    0 讨论(0)
  • 2021-01-03 07:13

    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);
        }
    }
    
    0 讨论(0)
  • 2021-01-03 07:16

    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.

    0 讨论(0)
  • 2021-01-03 07:22

    Insert this into your java call:

    -Dlog4j.configDebug=true
    

    Thats all.

    0 讨论(0)
提交回复
热议问题