it-swarm-eu.dev

Logger Java che determina automaticamente il nome della classe del chiamante

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

Questo metodo restituirebbe un logger che conosce la classe per cui sta registrando . Qualche idea contro di essa?

Molti anni dopo: https://github.com/yanchenko/droidparts/blob/master/droidparts/src/org/droidparts/util/L.Java

33
yanchenko

Immagino che aggiunga un sacco di spese generali per ogni classe. Ogni classe deve essere "alzata". Crei nuovi oggetti Throwable per farlo ... Questi articoli non sono gratuiti.

15
Daan

La creazione di una traccia di stack è un'operazione relativamente lenta. Il chiamante sa già in che classe e metodo si trova, quindi lo sforzo è sprecato. Questo aspetto della tua soluzione è inefficiente.

Anche se si utilizzano informazioni di classe statiche, non è necessario recuperare di nuovo il Logger per ciascun messaggio. Dall'autore di Log4j, Ceki Gülcü: 

L'errore più comune nelle classi wrapper è l'invocazione del metodo Logger.getLogger su ogni richiesta di log. Questo è garantito per devastare le prestazioni della tua applicazione. Veramente!!! 

Questo è l'idioma convenzionale ed efficiente per ottenere un Logger durante l'inizializzazione della classe:

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

Nota che questo ti dà un Logger separato per ogni tipo in una gerarchia. Se trovi un metodo che richiama getClass() su un'istanza, vedrai i messaggi registrati da un tipo di base visualizzato sotto il logger del sottotipo. Forse questo è auspicabile in alcuni casi, ma trovo che sia fonte di confusione (e tendo a favorire comunque la composizione sull'eredità). 

Ovviamente, l'utilizzo del tipo dinamico tramite getClass() richiederà di ottenere il logger almeno una volta per istanza, piuttosto che una volta per classe come l'idioma consigliato che utilizza le informazioni di tipo statico.

22
erickson

La classe MethodHandles (a ​​partire da Java 7) include una classe Lookup che, da un contesto statico, può trovare e restituire il nome della classe corrente. Considera il seguente esempio:

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

Quando si esegue questo produce:

Main

Per un logger, puoi usare:

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

In realtà abbiamo qualcosa di abbastanza simile in una classe LogUtils. Sì, è un po 'icky, ma i vantaggi valgono la pena per quanto mi riguarda. Volevamo essere sicuri che non ci fosse un sovraccarico perché veniva chiamato ripetutamente, quindi il nostro (un po 'incerto) assicura che possa essere chiamato SOLO da un contesto di inizializzazione statico, a la:

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

Fallirà se viene invocato da un metodo normale o da un inizializzatore di istanza (ad esempio, se "statico" è stato disattivato in precedenza) per ridurre il rischio di overhead delle prestazioni. Il metodo è:

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

Chiunque chieda che vantaggio ha questo 

= Logger.getLogger(MyClass.class);

probabilmente non ha mai avuto a che fare con qualcuno che copia e incolla quella linea da qualche altra parte e dimentica di cambiare il nome della classe, lasciandoti alle spalle una classe che invia tutte le sue cose a un altro logger.

17
Cowan

Supponendo che tu stia mantenendo i riferimenti statici ai logger, ecco un singleton statico standalone:

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'utilizzo è piacevole e pulito:

Logger logger = LoggerUtils.getLogger();
8
EGB

Per ogni classe con cui lo utilizzi, dovrai comunque cercare il Logger, quindi potresti anche usare un Logger statico in quelle classi.

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

Quindi fai riferimento a quel registratore quando hai bisogno di fare i tuoi messaggi di log. Il tuo metodo fa la stessa cosa che fa il Log LogJog statico, quindi perché reinventare la ruota?

4
18Rabbit

Quindi la cosa migliore è un mix di due. 

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

E poi in ogni classe:

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

nel codice:

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

Ottieni un logger statico che puoi copiare e incollare in ogni classe e senza sovraccarico ...

Alaa

3
Alaa Murad

Dalla lettura di tutti gli altri feedback su questo sito, ho creato quanto segue per l'uso con 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);
}

}

Ora nel tuo codice tutto ciò di cui hai bisogno è

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

o

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

Se hai bisogno di più esposizione ai metodi log4j, delegali semplicemente dalla classe Logger sopra elencata.

3
Ed Sarrazin

Preferisco creare un Logger (statico) per ogni classe (con il suo nome di classe esplicito). Io di usare il logger così com'è.

2
Philip Helger

Naturalmente, puoi semplicemente usare Log4J con il layout di pattern appropriato:

Ad esempio, per il nome della classe "org.Apache.xyz.SomeClass", il pattern% C {1} mostrerà "SomeClass". 

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

2
Ian

Non è necessario creare un nuovo oggetto Throwable. Puoi semplicemente chiamare Thread.currentThread().getStackTrace()[1]

2
ykaganovich

Questo meccanismo richiede molto sforzo in fase di esecuzione.

Se si utilizza Eclipse come IDE, considerare l'utilizzo di Log4e . Questo pratico plug-in genera dichiarazioni del registratore per l'utilizzo del framework di registrazione preferito. Una frazione di sforzo in più al momento della codifica, ma molto meno lavoro in fase di esecuzione.

0
Bill Michell

Una buona alternativa è usare (una delle) annotazioni dei log di lombok: https://projectlombok.org/features/Log.html

Genera l'istruzione di registro corrispondente con la classe corrente.

0
user2189998

Un bel modo per farlo da Java 7 in poi:

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

Il logger può essere static e quel fine . Ecco il suo utilizzo dell'API SLF4J

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

Ma in principio può essere utilizzato con qualsiasi framework di registrazione. Se il logger ha bisogno di un argomento stringa add toString()

0
James Mudd

Ho solo la seguente riga all'inizio della maggior parte delle mie lezioni.

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

sì, c'è un sovraccarico la prima volta che viene creato un oggetto di quella classe, ma io lavoro principalmente nelle webapp, quindi aggiungere microsecondi ad un avvio di 20 secondi non è davvero un problema.

0
muttonUp

A meno che tu davvero abbia bisogno che il tuo Logger sia statico, potresti usarlo

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

L'API di registrazione di Google Flogger supporta questo ad es.

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

Vedi https://github.com/google/flogger per ulteriori dettagli.

0
James Mudd

Dai un'occhiata a Logger class da jcabi-log . Fa esattamente quello che stai cercando, fornendo una raccolta di metodi statici. Non è più necessario incorporare i logger nelle classi:

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

Logger invia tutti i log a SLF4J, che è possibile reindirizzare a qualsiasi altra struttura di registrazione, in runtime.

0
yegor256

Perchè no?

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

E poi quando hai bisogno di un logger per una classe:

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

Per favore vedi la mia implementazione statica di getLogger () (usa la stessa magia di "Sun. *" su JDK 7 come default di Java Logger)

  • nota i metodi di registrazione statica (con importazione statica) senza brutta proprietà di registro ...

    importa static my.pakg.Logger. *;

E la loro velocità è equivalente all'implementazione nativa di Java (verificata con 1 milione di tracce di log)

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