web-dev-qa-db-fra.com

Logger Java qui détermine automatiquement le nom de classe de l'appelant

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

Cette méthode renverrait un enregistreur qui connaît la classe pour laquelle il se connecte . Des idées contre elle?

De nombreuses années plus tard: https://github.com/yanchenko/droidparts/blob/master/droidparts/src/org/droidparts/util/L.Java

33
yanchenko

Je suppose que cela ajoute beaucoup de frais généraux pour chaque classe. Chaque classe doit être "levée". Vous créez de nouveaux objets Throwable pour le faire ... Ces objets à lancer ne sont pas gratuits.

15
Daan

La création d'une trace de pile est une opération relativement lente. Votre interlocuteur sait déjà dans quelle classe et quelle méthode il est, donc l'effort est gaspillé. Cet aspect de votre solution est inefficace.

Même si vous utilisez des informations de classe statiques, vous ne devez plus extraire le consignateur pour chaque message. De l'auteur de Log4j, Ceki Gülcü: 

L'erreur la plus courante dans les classes wrapper est l'appel de la méthode Logger.getLogger à chaque demande de journal. Ceci est garanti pour faire des ravages sur les performances de votre application. Vraiment!!! 

C'est l'idiome conventionnel et efficace pour obtenir un enregistreur lors de l'initialisation de la classe:

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

Notez que cela vous donne un enregistreur distinct pour chaque type dans une hiérarchie. Si vous proposez une méthode qui appelle getClass() sur une instance, vous verrez les messages consignés par un type de base apparaissant sous le consignateur du sous-type. C'est peut-être souhaitable dans certains cas, mais je trouve cela déroutant (et j'ai tendance à privilégier la composition plutôt que l'héritage). 

De toute évidence, si vous utilisez le type dynamique via getClass(), vous devrez obtenir le consignateur au moins une fois par instance, plutôt que par classe, comme l'idiome recommandé utilisant des informations de type statique.

22
erickson

La classe MethodHandles class (à partir de Java 7) inclut une classe Lookup qui, à partir d'un contexte statique, peut rechercher et renvoyer le nom de la classe actuelle. Prenons l'exemple suivant:

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

Lorsqu'il est exécuté, cela produit:

Main

Pour un enregistreur, vous pouvez utiliser:

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

Nous avons en fait quelque chose d'assez similaire dans une classe LogUtils. Oui, c'est un peu dégueulasse, mais les avantages en valent la peine en ce qui me concerne. Nous voulions nous assurer que nous n’avions pas à payer de frais généraux en raison de son appel répété, de sorte que le nôtre (quelque peu haussement) s’assure qu’il ne peut être appelé UNIQUEMENT qu’à partir d’un contexte d’initialiseur statique:

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

Il échouera s'il est appelé à partir d'une méthode normale ou d'un initialiseur d'instance (c'est-à-dire si le paramètre 'static' a été laissé ci-dessus) pour réduire le risque de perte de performances. La méthode est la suivante:

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

Quiconque demande quel avantage cela a-t-il sur 

= Logger.getLogger(MyClass.class);

n’a probablement jamais eu à traiter avec une personne qui copie et colle cette ligne ailleurs et qui oublie de changer le nom de la classe, ce qui vous laisse face à une classe qui envoie tout son contenu à un autre enregistreur.

17
Cowan

En supposant que vous conserviez des références statiques aux enregistreurs, voici un singleton statique autonome:

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

L'utilisation est agréable et propre:

Logger logger = LoggerUtils.getLogger();
8
EGB

Pour chaque classe avec laquelle vous l'utilisez, vous devrez quand même rechercher Logger. Vous pouvez également utiliser un Logger statique dans ces classes.

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

Ensuite, il vous suffit de référencer cet enregistreur lorsque vous devez créer vos messages de journal. Votre méthode fait la même chose que le Log4J Logger statique, alors pourquoi réinventer la roue?

4
18Rabbit

Alors la meilleure chose est un mélange de deux. 

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

Et puis dans chaque classe:

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

dans du code :

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

Vous obtenez un enregistreur statique que vous pouvez simplement copier et coller dans toutes les classes sans aucun frais supplémentaire ...

Alaa

3
Alaa Murad

En lisant tous les autres commentaires sur ce site, j'ai créé ce qui suit pour une utilisation avec 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);
}

}

Maintenant, dans votre code, tout ce dont vous avez besoin est

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

ou

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

Si vous avez besoin d'une plus grande exposition aux méthodes log4j, il vous suffit de les déléguer à partir de la classe de journalisation indiquée ci-dessus.

3
Ed Sarrazin

Je préfère créer un enregistreur (statique) pour chaque classe (avec son nom de classe explicite). J'utilise l'enregistreur tel quel.

2
Philip Helger

Vous pouvez bien sûr simplement utiliser Log4J avec la disposition de motif appropriée:

Par exemple, pour le nom de classe "org.Apache.xyz.SomeClass", le modèle% C {1} générera "SomeClass". 

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

2
Ian

Vous n'avez pas besoin de créer un nouvel objet Throwable. Vous pouvez simplement appeler Thread.currentThread().getStackTrace()[1]

2
ykaganovich

Ce mécanisme demande beaucoup d’efforts supplémentaires à l’exécution.

Si vous utilisez Eclipse comme votre IDE, envisagez d'utiliser Log4e . Ce plugin pratique générera des déclarations de consignateurs à l’aide de votre infrastructure de journalisation préférée. Une fraction d'effort supplémentaire au moment du codage, mais beaucoup moins de travail au moment de l'exécution.

0
Bill Michell

Une bonne alternative consiste à utiliser (une des) les annotations des journaux lombok: https://projectlombok.org/features/Log.html

Il génère l'instruction de journal correspondante avec la classe actuelle.

0
user2189998

Une bonne façon de faire cela à partir de Java 7:

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

L’enregistreur peut être static et ça va très bien .

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

Mais en principe, peut être utilisé avec n’importe quel cadre de journalisation. Si le consignateur a besoin d'un argument de chaîne, ajoutez toString().

0
James Mudd

J'ai juste la ligne suivante au début de la plupart de mes cours.

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

oui, il y a des frais généraux la toute première fois qu'un objet de cette classe est créé, mais je travaille principalement dans des applications Web. L'ajout de microsecondes à un démarrage de 20 secondes ne pose donc pas vraiment de problème.

0
muttonUp

L’API de journalisation Google Flogger prend en charge cet exemple, par exemple.

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

Voir https://github.com/google/flogger pour plus de détails.

0
James Mudd

Jetez un coup d'œil à Logger class à partir de jcabi-log . Il fait exactement ce que vous recherchez en fournissant une collection de méthodes statiques. Vous n'avez plus besoin d'intégrer les enregistreurs dans les classes:

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

Logger envoie tous les journaux à SLF4J, que vous pouvez rediriger vers toute autre fonction de journalisation, au moment de l'exécution.

0
yegor256

Pourquoi pas?

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

Et puis, quand vous avez besoin d’un enregistreur pour un cours:

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

Veuillez voir mon implémentation statique de getLogger () (utilisez la même magie "Sun. *" sur JDK 7 que Java Logger doit par défaut)

  • notez les méthodes de journalisation statique (avec import statique) sans propriété de journal déplaisante ...

    importer statique my.pakg.Logger. *;

Et leur vitesse est équivalente à la mise en oeuvre Java native (vérifiée avec 1 million de traces de journal)

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