39
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

4

21 回答 21

31

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());
于 2015-08-21T04:21:27.567 回答
24

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

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

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

这是在类初始化期间获取 Logger 的传统、有效的习惯用法:

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

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

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

于 2008-09-19T19:35:43.857 回答
19

我想它为每个班级增加了很多开销。每个班级都必须“查找”。你创建新的 Throwable 对象来做到这一点......这些 throwable 不是免费的。

于 2008-09-17T07:21:05.087 回答
18

我们实际上在 LogUtils 类中有一些非常相似的东西。是的,这有点恶心,但就我而言,这些优势是值得的。我们想确保我们不会因为它被重复调用而产生任何开销,所以我们的(有点骇人听闻)确保它只能从静态初始化上下文中调用,例如:

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

可能永远不必处理从其他地方复制和粘贴该行并忘记更改类名的人,让您处理一个将其所有内容发送到另一个记录器的类。

于 2009-11-10T04:54:49.790 回答
8

假设您要为记录器保留静态引用,这是一个独立的静态单例:

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();
于 2009-08-12T20:44:59.087 回答
5

对于您使用它的每个类,无论如何您都必须查找 Logger,因此您不妨在这些类中使用静态 Logger。

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

然后,您只需在需要处理日志消息时引用该记录器。您的方法与静态 Log4J Logger 所做的相同,那么为什么要重新发明轮子呢?

于 2008-09-17T14:30:57.953 回答
3

那么最好的事情就是混合两个。

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

你得到静态记录器,你可以在每个类中复制和粘贴,没有开销......

阿拉

于 2010-03-08T09:10:06.000 回答
3

通过阅读该站点上的所有其他反馈,我创建了以下用于 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 类中委派它们。

于 2011-06-23T18:47:45.297 回答
2

您当然可以使用具有适当模式布局的 Log4J:

例如,对于类名“org.apache.xyz.SomeClass”,模式 %C{1} 将输出“SomeClass”。

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

于 2008-09-17T07:18:58.820 回答
2

我更喜欢为每个类创建一个(静态)记录器(带有明确的类名)。我比按原样使用记录器。

于 2008-09-17T07:46:49.877 回答
2

您不需要创建新的 Throwable 对象。你可以打电话 Thread.currentThread().getStackTrace()[1]

于 2008-11-19T20:58:52.963 回答
2

一个不错的选择是使用(其中一个)lombok 日志注释: https ://projectlombok.org/features/Log.html

它生成与当前类对应的日志语句。

于 2016-09-12T08:57:55.743 回答
1

我在大多数课程的开头都有以下行。

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

是的,第一次创建该类的对象时会有一些开销,但我主要在 webapps 中工作,因此在 20 秒的启动中添加微秒并不是真正的问题。

于 2015-08-14T12:41:32.173 回答
1

Google Flogger logging API 支持这个,例如

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

有关详细信息,请参阅https://github.com/google/flogger 。

于 2018-10-10T15:59:09.497 回答
1

从 Java 7 开始,一个很好的方法:

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

记录器可以static,那很好。这里它使用 SLF4J API

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

但原则上可以与任何日志框架一起使用。如果记录器需要一个字符串参数添加toString()

于 2019-02-20T13:13:41.973 回答
1

简单而琐碎的 OLD SCHOOL:

只需创建您自己的类并传递类名、方法名+注释(如果类/方法更改,它们会自动重构 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

于 2019-06-20T10:30:12.513 回答
0

为什么不?

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")
于 2008-09-17T07:39:18.630 回答
0

这种机制在运行时会付出很多额外的努力。

如果您使用 Eclipse 作为 IDE,请考虑使用Log4e。这个方便的插件将使用您最喜欢的日志框架为您生成记录器声明。编码时的工作量要多一点,但运行时的工作量要少得多。

于 2008-09-17T09:35:22.390 回答
0

除非你真的需要你的 Logger 是静态的,否则你可以使用

final Logger logger = LoggerFactory.getLogger(getClass());
于 2008-09-17T14:33:14.250 回答
0

请参阅我的静态 getLogger() 实现(在 JDK 7 上使用与默认 java Logger doit 相同的“sun.*”魔法)

  • 注意没有丑陋日志属性的静态日志记录方法(使用静态导入)...

    导入静态 my.pakg.Logger.*;

并且它们的速度相当于原生 Java 实现(检查了 100 万条日志跟踪)

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

}
于 2012-08-13T15:29:07.913 回答
0

看看jcabi-logLogger中的类。它完全符合您的要求,提供一组静态方法。您不再需要将记录器嵌入到类中:

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

Logger将所有日志发送到 SLF4J,您可以在运行时将其重定向到任何其他日志记录工具。

于 2013-02-03T07:58:56.927 回答