Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can you combine log4j default initialization with configureAndWatch()?

Some Log4j Configurators have a configureAndWatch() method, that starts a Thread to watch the file used for configuration (XML file or properties file) and trigger a reconfigure if the file changes.

However, if you rely on the Log4j default initialization procedure, you never get a chance to call configureAndWatch(). You don't even know the file that was used to configure with (it might not even be a file.)

Is there a good way to get configureAndWatch() style behavior, allowing changing of the log configuration on the fly, while still using the default initialization procedure? I am assuming that your configuration URL ultimately resolves to a file that can be watched, as a URL on another server would probably not be something you would want to pull every 60 seconds.

(I see that the configureAndWatch() method is not safe in a Java EE environment because of the separate thread, and I see that some app servers have their own mechanism for watching the log4j configuration file, but the program I am working on at the moment is not running in Java EE .)

like image 562
skiphoppy Avatar asked Dec 21 '22 17:12

skiphoppy


2 Answers

I've scrapped everything in the answer before and started over now that I better understand what you want.

The following paragraph describes the real sequence that log4j uses to determine what the log4j config file name is. This code tries to follow those rules.

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

It determines what log4j configuration file to use and then sets up the PropertyConfigurator using the file it found. This will only work if the file is located on the file system. It will not work if the file is inside a jar or at a remote 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
    }
like image 179
Sarel Botha Avatar answered Dec 28 '22 07:12

Sarel Botha


However, if you rely on the Log4j default initialization procedure, you never get a chance to call configureAndWatch(). You don't even know the file that was used to configure with (it might not even be a file.)

You have posted some related questions which I have already answered in [1] and [2], so this answer is building upon the explanations of the others. Again, I am using AspectJ:

Sample application class:

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 capturing the default URL and handing it over to 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);
        }
    }
}

I tested it with -Dlog4j.configDebug=true from Eclipse, changing the output channel back and forth from System.log (black colour in Eclipse console) to System.err (red colour, here not visible) in log4j.properties, and it worked nicely. In the log output you also see the debug info for reconfiguration after a detected file change:

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
like image 37
kriegaex Avatar answered Dec 28 '22 06:12

kriegaex