Marcus Junius Brutus
Marcus Junius Brutus

Reputation: 27276

How to configure logging in base class, based on derived class in Log4J?

I have a base class in a library with a large number of debug-level logging statements. This library is used in different applications and the base class is subclassed. I want to be able to configure the logging level of the base class depending on the particular derived class that's using it. What's a useful idiom for achieving that?

For example, the following is the starting point:

class Base {
    static Logger logger = Logger.getLogger(Base.class);
    protected final void foo() {
        logger.debug("foo");
    }
}

class DerivedA extends Base {
    public void boo() {
        foo();
    }
}

class DerivedB extends Base {
    public void boo() {
        foo();
    }
}

Given the above there's no way to turn on logging for DerivedA but not for DerivedB.

Instead, I have to do something like the following:

abstract class Base {

    abstract protected Class getDerivedType();
    protected final void foo() {
        Logger logger = Logger.getLogger(getDerivedType());        
        logger.debug("foo");
    }
}

class DerivedA extends Base {
    public void boo() {
        foo();
    }
    @Override protected Class getDerivedType() {
        return DerivedA.class;
    }
}

class DerivedB extends Base {
    public void boo() {
        foo();
    }
    @Override protected Class getDerivedType() {
        return DerivedB.class;
    }    
}

… given the above, I can have in my log4j.properties file different log levels depending on the derived class:

log4j.logger.a.DerivedA=debug
log4j.logger.a.DerivedB=info

While the above does work I don't like the fact that my logger object can't be static any more and has to be redefined inside each method. Also, it feels unnatural that I have to define an abstract method for the derived classes to provide their concrete type for no other reason than to be able to customize logging.

Is the above idiomatic and, if not, what would be a better way to achieve the same effect?

In general, is there a way to configure logging based on context? (where context in this case is the derived class but could also be the calling stack, i.e. the method that's calling a particular method where logging occurs). Or is this level of logging configurability not really supported or even a bad idea?

Upvotes: 4

Views: 3023

Answers (1)

DVarga
DVarga

Reputation: 21799

One possible solution with non-static logger:

public class Base {
    protected Logger logger = Logger.getLogger(Base.class);

    protected final void foo() {
        logger.debug("foo");
    }
}

class DerivedA extends Base {
    {
        logger = Logger.getLogger(DerivedA.class);
    }

    public void boo() { foo(); }
}

class DerivedB extends Base {
    {
        logger = Logger.getLogger(DerivedB.class);
    }
    public void boo() { foo(); }
}

Even though the logger is not static, this structure is much less complicated as the original one.


Alternative solution: The subclasses must implement the getLogger() abstract method (they can simply return a static logger), which method is used in the base class for logging. Here the loggers can be static.

public abstract class Base {
    protected abstract Logger getLogger();

    protected final void foo() {
        getLogger().debug("foo");
    }
}

class DerivedA extends Base {
    private static final Logger logger = Logger.getLogger(DerivedA.class);

    public void boo() { foo(); }

    protected Logger getLogger() { return logger; }
}

class DerivedB extends Base {
    private static Logger logger =  Logger.getLogger(DerivedB.class);

    public void boo() { foo();}

    protected Logger getLogger() { return logger; }
}

Both approaches work with your current properties file.

Upvotes: 4

Related Questions