从默认接口方法进行日志记录

2022-09-01 17:43:52

向所有Java大师致敬!

从Java8开始,我们可以在接口中使用默认实现(耶!但是,当您要从默认方法登录时,会出现问题。

我有一种感觉,每次我想在默认方法中记录某些内容时调用.getLogger()是不明智的。

是的,可以在接口中定义静态变量 - 但这对于接口来说不是一个好的做法+它公开了记录器(必须是公共的)。

我目前拥有的解决方案:

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对每个人仍然可见,这没有任何意义,因为它不提供任何方法,它应该是接口的内部。

你们有谁知道更好的解决方案吗?:)

编辑:我使用由Logback支持的SLF4J


答案 1

从 JDK 16 开始,您可以将帮助程序类隐藏在方法中:

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

从 JDK 9 开始,接口允许有方法。为了利用它进行惰性初始化,我们需要能够在本地类中声明一个字段,这是从JDK 16开始允许的。privatestatic

对于较旧的 Java 版本,如果不想向公众公开该类,请不要将其作为 .使其成为成员类几乎没有任何好处,您甚至不需要保存键入,因为无论如何,您都必须使用持有者类的名称来限定字段访问,无论它是成员类还是同一包中的类:LogHolderinterface

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

缺点是同一包装内的可见性。当然,一个包中只能有一个命名的顶级类。LogHolder


答案 2

给你。

记录器是专用接口。除了此接口及其默认方法之外,没有人可以访问 Test2 中的任何内容。没有什么可以扩展Test2类。

没有人建议你这样做...但它有效!这是访问主界面的类记录器的好方法,也可能是做其他事情的聪明方法,并不完全疯狂。

这与OP问题中的LogHolder实际上是相同的,除了Test2类都是私有方法和构造函数私有的,并且类没有标记为静态。

作为额外的奖励,它保持状态,静态和每个实例。(请不要在真正的程序中这样做!

public class TestRunner {
    public static void main(String[] args) {
        Test test = new Test() {
        };
        test.sayHello("Jane");
        System.out.println("Again");
        test.sayHello("Bob");
    }
}
public interface Test {
    default void sayHello(String name) {
        Logger log = Test2.log;
        Test2 ref = Test2.getMine.apply(this);
        int times = ref.getTimes();
        for (int i = 0; i < times; i++) {
            System.out.println(i + ": Hello " + name);
        }
        log.info("just said hello {} times :)",times);
    }
    final class Test2 {
        private static final Logger log = LoggerFactory.getLogger(Test.class);
        private static final Map lookup = new WeakHashMap();
        private static final Function getMine = (obj) -> {
            return lookup.computeIfAbsent(obj, (it) -> new Test2());
        };
        private int calls = 0;
        private Test2() {
        }
        private void setCalls(int calls) {
            this.calls = calls;
        }
        private int getCalls() {return calls;}
        private int getTimes() {return ++calls;}
    }
}