Java logger, который автоматически определяет имя класса вызывающего - PullRequest
35 голосов
/ 17 сентября 2008
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

Ответы [ 20 ]

22 голосов
/ 19 сентября 2008

Создание трассировки стека является относительно медленной операцией. Ваш абонент уже знает, в каком классе и методе он находится, поэтому усилия будут потрачены впустую. Этот аспект вашего решения неэффективен.

Даже если вы используете статическую информацию о классе, вам не следует извлекать Logger снова для каждого сообщения. От автора из Log4j, Ceki Gülcü:

Самая распространенная ошибка в классах-оболочках - это вызов метода Logger.getLogger при каждом запросе журнала. Это гарантированно повлияет на производительность вашего приложения. В самом деле!!!

Это традиционная, эффективная идиома для получения Logger во время инициализации класса:

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

Обратите внимание, что это дает вам отдельный регистратор для каждого типа в иерархии. Если вы придумали метод, который вызывает getClass() для экземпляра, вы увидите сообщения, зарегистрированные базовым типом, которые отображаются в регистраторе подтипа. Может быть, это желательно в некоторых случаях, но я нахожу это непонятным (и в любом случае я предпочитаю композицию, а не наследование).

Очевидно, что использование динамического типа через getClass() потребует, чтобы вы получали регистратор по крайней мере один раз для каждого экземпляра, а не один раз для класса, как рекомендованная идиома, использующая информацию статического типа.

20 голосов
/ 21 августа 2015

Класс 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());
17 голосов
/ 10 ноября 2009

На самом деле у нас есть нечто очень похожее в классе LogUtils. Да, это немного неприлично, но преимущества того стоят, насколько я понимаю. Мы хотели убедиться, что у нас не было никаких накладных расходов от его повторного вызова, поэтому наш (несколько хакерски) гарантирует, что он может быть вызван ТОЛЬКО из статического инициализирующего контекста, например: 1001 *

private static final Logger LOG = LogUtils.loggerForThisClass();

Сбой, если он вызывается из обычного метода или из инициализатора экземпляра (т. Е. Если выше было указано «static»), снижает риск снижения производительности. Метод:

public static Logger loggerForThisClass() {
    // We use the third stack element; second is this method, first is .getStackTrace()
    StackTraceElement myCaller = Thread.currentThread().getStackTrace()[2];
    Assert.equal("<clinit>", myCaller.getMethodName());
    return Logger.getLogger(myCaller.getClassName());
}

Тот, кто спрашивает, какое преимущество это имеет над

= Logger.getLogger(MyClass.class);

, вероятно, никогда не приходилось иметь дело с кем-то, кто копирует и вставляет эту строку откуда-то еще и забывает изменить имя класса, оставляя вам дело с классом, который отправляет все свои вещи другому регистратору.

15 голосов
/ 17 сентября 2008

Я предполагаю, что это добавляет много накладных расходов для каждого класса. Каждый класс должен быть «просмотрен». Вы создаете новые Throwable объекты, чтобы сделать это ... Эти throwables не приходят бесплатно.

8 голосов
/ 13 августа 2009

Предполагая, что вы сохраняете статические ссылки на регистраторы, вот отдельный статический синглтон:

public class LoggerUtils extends SecurityManager
{
    public static Logger getLogger()
    {
        String className = new LoggerUtils().getClassName();
        Logger logger = Logger.getLogger(className);
        return logger;
    }

    private String getClassName()
    {
        return getClassContext()[2].getName();
    }
}

Использование красиво и чисто:

Logger logger = LoggerUtils.getLogger();
4 голосов
/ 17 сентября 2008

Для каждого класса, с которым вы используете это, вам все равно придется искать Logger, так что вы можете просто использовать статический Logger в этих классах.

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

Тогда вы просто ссылаетесь на этот регистратор, когда вам нужно сделать ваши сообщения журнала. Ваш метод делает то же самое, что статический Log4J Logger, поэтому зачем изобретать велосипед?

3 голосов
/ 08 марта 2010

Тогда лучшая вещь - это смесь двух.

public class LoggerUtil {

    public static Level level=Level.ALL;

    public static java.util.logging.Logger getLogger() {
        final Throwable t = new Throwable();
        final StackTraceElement methodCaller = t.getStackTrace()[1];
        final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(methodCaller.getClassName());
        logger.setLevel(level);

        return logger;
    }
}

А потом в каждом классе:

private static final Logger LOG = LoggerUtil.getLogger();

в коде:

LOG.fine("debug that !...");

Вы получаете статический логгер, который вы можете просто копировать и вставлять в каждый класс без лишних затрат ...

Аля

3 голосов
/ 23 июня 2011

Из прочтения всех других отзывов на этом сайте я создал следующее для использования с Log4j:

package com.edsdev.testapp.util;

import java.util.concurrent.ConcurrentHashMap;

import org.apache.log4j.Level;
import org.apache.log4j.Priority;

public class Logger extends SecurityManager {

private static ConcurrentHashMap<String, org.apache.log4j.Logger> loggerMap = new ConcurrentHashMap<String, org.apache.log4j.Logger>();

public static org.apache.log4j.Logger getLog() {
    String className = new Logger().getClassName();
    if (!loggerMap.containsKey(className)) {
        loggerMap.put(className, org.apache.log4j.Logger.getLogger(className));
    }
    return loggerMap.get(className);
}
public String getClassName() {
    return getClassContext()[3].getName();
}
public static void trace(Object message) {
    getLog().trace(message);
}
public static void trace(Object message, Throwable t) {
    getLog().trace(message, t);
}
public static boolean isTraceEnabled() {
    return getLog().isTraceEnabled();
}
public static void debug(Object message) {
    getLog().debug(message);
}
public static void debug(Object message, Throwable t) {
    getLog().debug(message, t);
}
public static void error(Object message) {
    getLog().error(message);
}
public static void error(Object message, Throwable t) {
    getLog().error(message, t);
}
public static void fatal(Object message) {
    getLog().fatal(message);
}
public static void fatal(Object message, Throwable t) {
    getLog().fatal(message, t);
}
public static void info(Object message) {
    getLog().info(message);
}
public static void info(Object message, Throwable t) {
    getLog().info(message, t);
}
public static boolean isDebugEnabled() {
    return getLog().isDebugEnabled();
}
public static boolean isEnabledFor(Priority level) {
    return getLog().isEnabledFor(level);
}
public static boolean isInfoEnabled() {
    return getLog().isInfoEnabled();
}
public static void setLevel(Level level) {
    getLog().setLevel(level);
}
public static void warn(Object message) {
    getLog().warn(message);
}
public static void warn(Object message, Throwable t) {
    getLog().warn(message, t);
}

}

Теперь в вашем коде все, что вам нужно, это

Logger.debug("This is a test");

или

Logger.error("Look what happened Ma!", e);

Если вам нужно больше узнать о методах log4j, просто делегируйте их из перечисленного выше класса Logger.

2 голосов
/ 19 ноября 2008

Вам не нужно создавать новый объект Throwable. Вы можете просто позвонить Thread.currentThread().getStackTrace()[1]

2 голосов
/ 17 сентября 2008

Я предпочитаю создавать (статический) Logger для каждого класса (с его явным именем класса). Я чем пользуюсь регистратором как есть.

...