自动确定调用方类名的 Java 记录器

2022-09-01 02:48:21
public static Logger getLogger() {
    final Throwable t = new Throwable();
    final StackTraceElement methodCaller = t.getStackTrace()[1];
    final Logger logger = Logger.getLogger(methodCaller.getClassName());
    logger.setLevel(ResourceManager.LOGLEVEL);
    return logger;
}

此方法将返回一个记录器,该记录器知道它所记录的类。有什么想法反对它吗?

许多年后:https://github.com/yanchenko/droidparts/blob/master/droidparts/src/org/droidparts/util/L.java


答案 1

MethodHandles 类(从 Java 7 开始)包括一个 Lookup 类,该类可以从静态上下文中查找并返回当前类的名称。请考虑以下示例:

import java.lang.invoke.MethodHandles;

public class Main {
  private static final Class clazz = MethodHandles.lookup().lookupClass();
  private static final String CLASSNAME = clazz.getSimpleName();

  public static void main( String args[] ) {
    System.out.println( CLASSNAME );
  }
}

运行时,这将产生:

Main

对于记录器,您可以使用:

private static Logger LOGGER = 
  Logger.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());

答案 2

创建堆栈跟踪是一个相对较慢的操作。您的调用方已经知道它所在的类和方法,因此浪费了精力。解决方案的这一方面效率低下。

即使使用静态类信息,也不应为每条消息再次获取记录器。来自Log4j的作者,Ceki Gülcü:

包装类中最常见的错误是在每个日志请求上调用 Logger.getLogger 方法。这肯定会对应用程序的性能造成严重破坏。真!!!

这是在类初始化期间获取记录器的传统有效习语:

private static final Logger log = Logger.getLogger(MyClass.class);

请注意,这为层次结构中的每个类型提供了一个单独的记录器。如果您提出了一个在实例上调用的方法,您将看到由基类型记录的消息显示在子类型的记录器下。也许这在某些情况下是可取的,但我发现它令人困惑(无论如何,我倾向于支持组合而不是继承)。getClass()

显然,使用动态类型 via 将要求您为每个实例至少获取一次记录器,而不是像使用静态类型信息的推荐成语那样为每个类获取一次。getClass()


推荐