Skip to content
Advertisement

Logging from default interface methods

Salut to all Java gurus!

Since Java8 we can have default implementations in interfaces (yay!). However problem arises when you want to log from default method.

I have a feeling that it is not wise to call .getLogger() every time I want to log something in a default method.

Yes, one can define static variable in an interface – but that is not a good practice for interfaces anyway + it exposes the logger (must be public).

Solution I have at the moment:

interface WithTimeout<Action> {

    default void onTimeout(Action timedOutAction) {
        LogHolder.LOGGER.info("Action {} time out ignored.", timedOutAction);
    }

    static final class LogHolder {
        private static final Logger LOGGER = getLogger(WithTimeout.class);
    }
}

LogHolder is still visible to everybody which doesn’t really make any sense since it does not provide any methods and it should be internal to the interface.

Does any of you know about better solution? 🙂

EDIT: I use SLF4J backed by Logback

Advertisement

Answer

Starting with JDK 16, you can hide the helper class inside a method:

interface WithTimeout<Action> {

    default void onTimeout(Action timedOutAction) {
        logger().info("Action {} time out ignored.", timedOutAction);
    }

    private static Logger logger() {
        final class LogHolder {
            private static final Logger LOGGER = getLogger(WithTimeout.class);
        }
        return LogHolder.LOGGER;
    }
}

Since JDK 9, interfaces are allowed to have private methods. To utilize this for a lazy initialization, we need to be able to declare a static field in a local class, which is allowed since JDK 16.

For older Java versions, if you don’t want to expose the class LogHolder to the public, don’t make it a member class of the interface. There is almost no benefit in making it a member class, you don’t even save typing as you have to qualify the field access with the name of the holder class anyway, regardless of whether it is a member class or a class within the same package:

public interface WithTimeout<Action> {

    default void onTimeout(Action timedOutAction) {
        LogHolder.LOGGER.info("Action {} time out ignored.", timedOutAction);
    }
}
final class LogHolder { // not public
    static final Logger LOGGER = getLogger(WithTimeout.class);
}

The disadvantage is the visibility within the same package. Of course, there can be only one top level class named LogHolder within a package.

3 People found this is helpful
Advertisement