it-swarm-ru.tech

Java logger, который автоматически определяет имя класса вызывающего

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

33
yanchenko

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

15
Daan

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

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

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

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

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

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

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

22
erickson

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

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

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

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

17
Cowan

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

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

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

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

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

4
18Rabbit

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

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
Alaa Murad

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

3
Ed Sarrazin

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

2
Philip Helger

Конечно, вы можете просто использовать Log4J с соответствующим макетом шаблона:

Например, для имени класса «org.Apache.xyz.SomeClass» шаблон% C {1} выведет «SomeClass». 

http://logging.Apache.org/log4j/1.2/apidocs/org/Apache/log4j/PatternLayout.html

2
Ian

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

2
ykaganovich

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

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

0
Bill Michell

Хорошей альтернативой является использование (одной из) аннотаций lombok logs: https://projectlombok.org/features/Log.html

Создает соответствующий лог-оператор с текущим классом.

0
user2189998

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

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

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

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

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

0
James Mudd

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

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

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

0
muttonUp

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

final Logger logger = LoggerFactory.getLogger(getClass());
0
Asgeir S. Nilsen

API логирования Google Flogger поддерживает это, например,.

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

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

0
James Mudd

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

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

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

0
yegor256

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

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")
0
Mario Ortegón

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

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

    импортировать статические 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's 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);
}

}
0
joseaio