Подтвердить что ты не робот

Java-журнал, который автоматически определяет имя класса вызывающего абонента

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

4b9b3361

Ответ 1

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

Ответ 2

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

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

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

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

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

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

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

Ответ 3

Класс 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());

Ответ 4

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

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

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

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

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

Ответ 5

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

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

Ответ 6

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

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

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

Ответ 7

Тогда самое лучшее - это сочетание двух.

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 !...");

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

Алаа

Ответ 8

Из прочтения всех других отзывов на этом сайте я создал следующее для использования с 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, указанного выше.

Ответ 10

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

Ответ 11

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

Ответ 12

У меня есть только следующая строка в начале большинства моих классов.

  private static final Logger log = 
     LoggerFactory.getLogger(new Throwable().getStackTrace()[0].getClassName());

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

Ответ 13

Google Loggger API поддерживает это, например:

private static final FluentLogger logger = FluentLogger.forEnclosingClass();

См. Https://github.com/google/flogger для получения дополнительной информации.

Ответ 14

Почему бы и нет?

public static Logger getLogger(Object o) {
  final Logger logger = Logger.getLogger(o.getClass());
  logger.setLevel(ResourceManager.LOGLEVEL);
  return logger;
}

И тогда, когда вам нужен логгер для класса:

getLogger(this).debug("Some log message")

Ответ 15

Этот механизм приносит много дополнительных усилий во время выполнения.

Если вы используете Eclipse в качестве среды IDE, рассмотрите возможность использования Log4e. Этот удобный плагин будет генерировать объявления регистратора для вас, используя вашу любимую структуру ведения журнала. Еще меньше усилий при кодировании, но много меньше работает во время выполнения.

Ответ 16

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

final Logger logger = LoggerFactory.getLogger(getClass());

Ответ 17

Пожалуйста, посмотрите мою статическую реализацию getLogger() (используйте ту же "солнце. *" магия на JDK 7 как по умолчанию java Logger doit)

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

    import static my.pakg.Logger. *;

И их скорость эквивалентна встроенной реализации Java (проверено с 1 миллионом следов журнала)

package my.pkg;

import java.text.MessageFormat;
import java.util.Arrays;
import java.util.IllegalFormatException;
import java.util.logging.Level;
import java.util.logging.LogRecord;

import sun.misc.JavaLangAccess;
import sun.misc.SharedSecrets;


public class Logger {
static final int CLASS_NAME = 0;
static final int METHOD_NAME = 1;

// Private method to infer the caller class and method names
protected static String[] getClassName() {
    JavaLangAccess access = SharedSecrets.getJavaLangAccess();
    Throwable throwable = new Throwable();
    int depth = access.getStackTraceDepth(throwable);

    boolean lookingForLogger = true;
    for (int i = 0; i < depth; i++) {
        // Calling getStackTraceElement directly prevents the VM
        // from paying the cost of building the entire stack frame.
        StackTraceElement frame = access.getStackTraceElement(throwable, i);
        String cname = frame.getClassName();
        boolean isLoggerImpl = isLoggerImplFrame(cname);
        if (lookingForLogger) {
            // Skip all frames until we have found the first logger frame.
            if (isLoggerImpl) {
                lookingForLogger = false;
            }
        } else {
            if (!isLoggerImpl) {
                // skip reflection call
                if (!cname.startsWith("java.lang.reflect.") && !cname.startsWith("sun.reflect.")) {
                    // We've found the relevant frame.
                    return new String[] {cname, frame.getMethodName()};
                }
            }
        }
    }
    return new String[] {};
    // We haven't found a suitable frame, so just punt.  This is
    // OK as we are only committed to making a "best effort" here.
}

protected static String[] getClassNameJDK5() {
    // Get the stack trace.
    StackTraceElement stack[] = (new Throwable()).getStackTrace();
    // First, search back to a method in the Logger class.
    int ix = 0;
    while (ix < stack.length) {
        StackTraceElement frame = stack[ix];
        String cname = frame.getClassName();
        if (isLoggerImplFrame(cname)) {
            break;
        }
        ix++;
    }
    // Now search for the first frame before the "Logger" class.
    while (ix < stack.length) {
        StackTraceElement frame = stack[ix];
        String cname = frame.getClassName();
        if (isLoggerImplFrame(cname)) {
            // We've found the relevant frame.
            return new String[] {cname, frame.getMethodName()};
        }
        ix++;
    }
    return new String[] {};
    // We haven't found a suitable frame, so just punt.  This is
    // OK as we are only committed to making a "best effort" here.
}


private static boolean isLoggerImplFrame(String cname) {
    // the log record could be created for a platform logger
    return (
            cname.equals("my.package.Logger") ||
            cname.equals("java.util.logging.Logger") ||
            cname.startsWith("java.util.logging.LoggingProxyImpl") ||
            cname.startsWith("sun.util.logging."));
}

protected static java.util.logging.Logger getLogger(String name) {
    return java.util.logging.Logger.getLogger(name);
}

protected static boolean log(Level level, String msg, Object... args) {
    return log(level, null, msg, args);
}

protected static boolean log(Level level, Throwable thrown, String msg, Object... args) {
    String[] values = getClassName();
    java.util.logging.Logger log = getLogger(values[CLASS_NAME]);
    if (level != null && log.isLoggable(level)) {
        if (msg != null) {
            log.log(getRecord(level, thrown, values[CLASS_NAME], values[METHOD_NAME], msg, args));
        }
        return true;
    }
    return false;
}

protected static LogRecord getRecord(Level level, Throwable thrown, String className, String methodName, String msg, Object... args) {
    LogRecord record = new LogRecord(level, format(msg, args));
    record.setSourceClassName(className);
    record.setSourceMethodName(methodName);
    if (thrown != null) {
        record.setThrown(thrown);
    }
    return record;
}

private static String format(String msg, Object... args) {
    if (msg == null || args == null || args.length == 0) {
        return msg;
    } else if (msg.indexOf('%') >= 0) {
        try {
            return String.format(msg, args);
        } catch (IllegalFormatException esc) {
            // none
        }
    } else if (msg.indexOf('{') >= 0) {
        try {
            return MessageFormat.format(msg, args);
        } catch (IllegalArgumentException exc) {
            // none
        }
    }
    if (args.length == 1) {
        Object param = args[0];
        if (param != null && param.getClass().isArray()) {
            return msg + Arrays.toString((Object[]) param);
        } else if (param instanceof Throwable){
            return msg;
        } else {
            return msg + param;
        }
    } else {
        return msg + Arrays.toString(args);
    }
}

public static void severe(String msg, Object... args) {
    log(Level.SEVERE, msg, args);
}

public static void warning(String msg, Object... args) {
    log(Level.WARNING, msg, args);
}

public static void info(Throwable thrown, String format, Object... args) {
    log(Level.INFO, thrown, format, args);
}

public static void warning(Throwable thrown, String format, Object... args) {
    log(Level.WARNING, thrown, format, args);
}

public static void warning(Throwable thrown) {
    log(Level.WARNING, thrown, thrown.getMessage());
}

public static void severe(Throwable thrown, String format, Object... args) {
    log(Level.SEVERE, thrown, format, args);
}

public static void severe(Throwable thrown) {
    log(Level.SEVERE, thrown, thrown.getMessage());
}

public static void info(String msg, Object... args) {
    log(Level.INFO, msg, args);
}

public static void fine(String msg, Object... args) {
    log(Level.FINE, msg, args);
}

public static void finer(String msg, Object... args) {
    log(Level.FINER, msg, args);
}

public static void finest(String msg, Object... args) {
    log(Level.FINEST, msg, args);
}

public static boolean isLoggableFinest() {
    return isLoggable(Level.FINEST);
}

public static boolean isLoggableFiner() {
    return isLoggable(Level.FINER);
}

public static boolean isLoggableFine() {
    return isLoggable(Level.FINE);
}

public static boolean isLoggableInfo() {
    return isLoggable(Level.INFO);
}

public static boolean isLoggableWarning() {
    return isLoggable(Level.WARNING);
}
public static boolean isLoggableSevere() {
    return isLoggable(Level.SEVERE);
}

private static boolean isLoggable(Level level) {
    return log(level, null);
}

}

Ответ 18

Взгляните на Logger класс из jcabi-log. Он выполняет именно то, что вы ищете, предоставляя коллекцию статических методов. Вам больше не нужно встраивать регистраторы в классы:

import com.jcabi.log.Logger;
class Foo {
  public void bar() {
    Logger.info(this, "doing something...");
  }
}

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

Ответ 19

Хорошей альтернативой является использование (одной из) аннотаций ломбокских журналов: https://projectlombok.org/features/Log.html

Он сгенерирует соответствующий оператор журнала с текущим классом.

Ответ 20

Хороший способ сделать это с Java 7 и далее:

private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

Регистратор может быть static и это нормально. Здесь используется API SLF4J

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Но в принципе может использоваться с любым каркасом регистрации. Если регистратору нужен строковый аргумент, добавьте toString()

Ответ 21

Простая и тривиальная СТАРАЯ ШКОЛА:

Просто создайте свой собственный класс и передайте туда имя класса, имя метода + комментарий (если класс/метод изменился, они автоматически изменяются Shift + F6)

public class MyLogs {    
    public static void LOG(String theClass, String theMethod, String theComment) {
        Log.d("MY_TAG", "class: " + theClass + " meth : " + theMethod + " comm : " + theComment);
    }
}

и просто использовать его в любом месте приложения (без контекста, инициализации, дополнительных библиотек и поиска) - можно использовать для любого языка программирования!

MyLogs.LOG("MainActivity", "onCreate", "Hello world");

это напечатает в вашей консоли:

Класс MY_TAG: MainActivity, мет: onCreate comm: Hello world