it-swarm-eu.dev

Java-Logger, der automatisch den Klassennamen des Anrufers ermittelt

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

Diese Methode würde einen Logger zurückgeben, der die Klasse kennt, für die er protokolliert wird. Gibt es Ideen dagegen?

Viele Jahre später: https://github.com/yanchenko/droidparts/blob/master/droidparts/src/org/droidparts/util/L.Java

33
yanchenko

Ich schätze, das bringt für jede Klasse viel zusätzlichen Aufwand. Jede Klasse muss "nachgeschlagen" werden. Sie erstellen neue Throwable-Objekte, um dies zu tun ... Diese Throwables sind nicht kostenlos.

15
Daan

Das Erstellen einer Stack-Ablaufverfolgung ist relativ langsam. Ihr Anrufer weiß bereits, in welcher Klasse und in welcher Methode er sich befindet. Der Aufwand wird also verschwendet. Dieser Aspekt Ihrer Lösung ist ineffizient.

Selbst wenn Sie statische Klasseninformationen verwenden, sollten Sie den Logger nicht erneut für jede Nachricht abrufen. Vom Autor von Log4j, Ceki Gülcü: 

Der häufigste Fehler in Wrapper-Klassen ist der Aufruf der Logger.getLogger-Methode für jede Protokollanforderung. Dies führt garantiert zu einer Beeinträchtigung der Leistung Ihrer Anwendung. Ja wirklich!!! 

Dies ist die übliche, effiziente Sprache, um einen Logger während der Klasseninitialisierung zu erhalten:

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

Beachten Sie, dass Sie dadurch für jeden Typ in einer Hierarchie einen separaten Logger erhalten. Wenn Sie eine Methode aufrufen, die getClass() in einer Instanz aufruft, werden Meldungen, die von einem Basistyp protokolliert werden, unter der Protokollfunktion des Subtyps angezeigt. Vielleicht ist dies in einigen Fällen wünschenswert, aber ich finde es verwirrend (und ich neige dazu, die Zusammensetzung ohnehin zu bevorzugen). 

Wenn Sie den dynamischen Typ über getClass() verwenden, müssen Sie den Logger mindestens einmal pro Instanz und nicht einmal pro Klasse abrufen, wie es der empfohlene Ausdruck mit statischen Typinformationen ist.

22
erickson

Die MethodHandles Klasse (ab Java 7) enthält eine Lookup Klasse, die in einem statischen Kontext den Namen der aktuellen Klasse finden und zurückgeben kann. Betrachten Sie das folgende Beispiel:

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

Wenn ausgeführt, erzeugt dies:

Main

Für einen Logger können Sie Folgendes verwenden:

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

Wir haben in einer LogUtils-Klasse tatsächlich etwas Ähnliches. Ja, es ist irgendwie icky, aber die Vorteile sind es für mich wert. Wir wollten sicherstellen, dass wir durch das wiederholte Aufrufen keinen Overhead haben, daher stellt unser (etwas hässliches) sicher, dass es NUR aus einem statischen Initialisierungskontext aufgerufen werden kann.

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

Es schlägt fehl, wenn es von einer normalen Methode oder von einem Instanzeninitialisierer aufgerufen wird (d. H., Wenn die 'statische' oben deaktiviert wurde), um das Risiko eines Performance-Overhead zu reduzieren. Die Methode ist:

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

Wer fragt, welchen Vorteil das hat, ist vorbei 

= Logger.getLogger(MyClass.class);

hat sich wahrscheinlich noch nie mit jemandem befassen müssen, der diese Zeile von einem anderen Ort kopiert und einfügt und den Klassennamen nicht ändert, sodass Sie sich mit einer Klasse befassen müssen, die ihr gesamtes Zeug an einen anderen Logger sendet.

17
Cowan

Angenommen, Sie behalten statische Refs für die Logger bei. Hier ist ein eigenständiger statischer Singleton:

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

Die Nutzung ist schön und sauber:

Logger logger = LoggerUtils.getLogger();
8
EGB

Für jede Klasse, mit der Sie dies verwenden, müssen Sie den Logger trotzdem nachschlagen. In diesen Klassen können Sie auch einen statischen Logger verwenden.

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

Dann verweisen Sie nur auf diesen Logger, wenn Sie Ihre Protokollnachrichten erstellen müssen. Ihre Methode macht dasselbe wie der statische Log4J Logger. Warum also das Rad neu erfinden?

4
18Rabbit

Dann ist das Beste eine Mischung aus zwei. 

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

Und dann in jeder Klasse:

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

in Code :

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

Sie erhalten einen statischen Logger, den Sie einfach in jede Klasse kopieren und einfügen können, ohne dass ein Aufwand entsteht.

Alaa

3
Alaa Murad

Nach dem Durchlesen aller anderen Rückmeldungen auf dieser Website habe ich Folgendes für die Verwendung mit Log4j erstellt:

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

}

Jetzt brauchen Sie in Ihrem Code nichts weiter

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

oder

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

Wenn Sie mehr Log4j-Methoden benötigen, delegieren Sie sie einfach von der oben aufgeführten Logger-Klasse.

3
Ed Sarrazin

Ich bevorzuge die Erstellung eines (statischen) Loggers für jede Klasse (mit ihrem expliziten Klassennamen). Ich benutze den Logger so, wie er ist.

2
Philip Helger

Sie können Log4J natürlich auch mit dem entsprechenden Musterlayout verwenden:

Für den Klassennamen "org.Apache.xyz.SomeClass" gibt das Muster% C {1} beispielsweise "SomeClass" aus. 

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

2
Ian

Sie müssen kein neues Throwable-Objekt erstellen. Sie können einfach anrufen Thread.currentThread().getStackTrace()[1]

2
ykaganovich

Einfache und triviale ALTE SCHULE:

Erstellen Sie einfach Ihre eigene Klasse und übergeben Sie den Klassennamen, den Methodennamen und den Kommentar (wenn die Klasse/Methode geändert wurde, werden sie automatisch umgestaltet Umschalt + F6).

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

und einfach überall in der App verwenden (kein Kontext erforderlich, keine Initialisierung, keine zusätzlichen Bibliotheken und kein Nachschlagen) - kann für jede Programmiersprache verwendet werden!

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

dies wird in Ihrer Konsole gedruckt:

MY_TAG-Klasse: MainActivity meth: onCreate comm: Hello world

0
Choletski

Dieser Mechanismus verursacht zur Laufzeit viel zusätzlichen Aufwand.

Wenn Sie Eclipse als IDE verwenden, sollten Sie Log4e verwenden. Dieses praktische Plugin generiert Logger-Deklarationen für Sie mit Ihrem bevorzugten Protokollierungs-Framework. Ein Bruchteil mehr Aufwand bei der Codierung, aber viel weniger Arbeit zur Laufzeit.

0
Bill Michell

Eine gute Alternative ist die Verwendung einer der Log-Annotationen von Lombok: https://projectlombok.org/features/Log.html

Es generiert die entsprechende Protokollanweisung mit der aktuellen Klasse.

0
user2189998

Eine schöne Möglichkeit, dies ab Java 7 zu tun:

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

Der Logger kann static sein und das ist in Ordnung ..__

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

Sie kann jedoch grundsätzlich mit jedem Protokollierungsframework verwendet werden. Wenn der Logger ein String-Argument benötigt, fügen Sie toString() hinzu.

0
James Mudd

Ich habe nur die folgende Zeile zu Beginn der meisten Kurse.

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

ja, schon beim ersten Erstellen eines Objekts dieser Klasse gibt es einen gewissen Aufwand, aber ich arbeite hauptsächlich in Webapps. Das Hinzufügen von Mikrosekunden zu einem Start von 20 Sekunden ist also kein Problem.

0
muttonUp

Wenn Sie wirklich Ihren Logger nicht statisch benötigen, können Sie dies verwenden

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

Die Google Flogger-Protokollierungs-API unterstützt dies, z.

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

Weitere Informationen finden Sie unter https://github.com/google/flogger .

0
James Mudd

Werfen Sie einen Blick auf Logger class aus jcabi-log . Es erfüllt genau das, wonach Sie suchen, und bietet eine Sammlung statischer Methoden. Sie müssen keine Logger mehr in Klassen einbetten:

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

Logger sendet alle Protokolle an SLF4J, die Sie zur Laufzeit an eine andere Protokollierungsfunktion weiterleiten können.

0
yegor256

Warum nicht?

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

Und wenn Sie einen Logger für eine Klasse brauchen:

getLogger(this).debug("Some log message")
0
Mario Ortegón

Siehe meine statische getLogger () -Implementierung (verwende die gleiche "Sun. *" - Magie auf JDK 7 als Standard-Java-Logger-Doit).

  • beachten Sie statische Protokollierungsmethoden (mit statischem Import) ohne hässliche Log-Eigenschaft ...

    importieren Sie static my.pakg.Logger. *;

Und ihre Geschwindigkeit entspricht der Implementierung von nativem Java (geprüft mit 1 Million Protokollspuren)

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