Domanda

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 contraria?

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

È stato utile?

Soluzione

Immagino che aggiunga molto sovraccarico per ogni lezione.Ogni classe deve essere "cercata".Crei nuovi oggetti Throwable per farlo...Questi oggetti da lancio non vengono forniti gratuitamente.

Altri suggerimenti

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

Anche se utilizzi informazioni di classe statiche, non dovresti recuperare nuovamente il Logger per ogni 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.Ciò sicuramente danneggerà le prestazioni della tua applicazione.Veramente!!!

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

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

Tieni presente che questo ti dà un Logger separato per ogni tipo in una gerarchia.Se trovi un metodo che invoca getClass() su un'istanza, vedrai i messaggi registrati da un tipo di base visualizzati nel logger del sottotipo.Forse questo è desiderabile in alcuni casi, ma lo trovo confuso (e tendo comunque a favorire la composizione rispetto all'ereditarietà).

Ovviamente, utilizzando il tipo dinamico via getClass() richiederà di ottenere il logger almeno una volta per istanza, anziché una volta per classe come l'idioma consigliato utilizzando informazioni di tipo statico.

IL MetodoHandles class (a partire da Java 7) include a Cercare classe 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 eseguito, questo produce:

Main

Per un logger, potresti usare:

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

In realtà abbiamo qualcosa di abbastanza simile in una classe LogUtils.Sì, è un po' scomodo, ma per quanto mi riguarda i vantaggi valgono la pena.Volevamo assicurarci di non avere alcun sovraccarico derivante dal fatto che veniva chiamato ripetutamente, quindi il nostro (in modo un po' complicato) assicura che possa essere chiamato SOLO da un contesto di inizializzatore 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 esempiose il 'statico' è stato tralasciato sopra) per ridurre il rischio di sovraccarico 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 si chieda quale vantaggio ha questo rispetto

= Logger.getLogger(MyClass.class);

probabilmente non ha mai avuto a che fare con qualcuno che copia e incolla quella riga da qualche altra parte e si dimentica di cambiare il nome della classe, lasciandoti a dover gestire una classe che invia tutto il suo materiale a un altro logger.

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

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

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

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

Quindi fai semplicemente riferimento a quel logger quando devi creare i tuoi messaggi di registro.Il tuo metodo fa già la stessa cosa che fa già il Logger statico Log4J, quindi perché reinventare la ruota?

Allora 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 semplicemente copiare e incollare in ogni classe e senza sovraccarico ...

Alaa

Dalla lettura di tutti gli altri feedback su questo sito, ho creato quanto segue da utilizzare 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 maggiore esposizione ai metodi log4j, delegali semplicemente dalla classe Logger elencata sopra.

Ovviamente potresti semplicemente usare Log4J con il layout del modello appropriato:

Ad esempio, per il nome della classe "org.apache.xyz.SomeClass", il modello %C{1} restituirà "SomeClass".

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

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

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

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 certo sovraccarico la prima volta che viene creato un oggetto di quella classe, ma lavoro principalmente nelle webapp, quindi aggiungere microsecondi a un avvio di 20 secondi non è davvero un problema.

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

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

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

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

Questo meccanismo comporta uno sforzo aggiuntivo in fase di esecuzione.

Se usi Eclipse come IDE, considera l'utilizzo Log4e.Questo pratico plugin genererà dichiarazioni di logger per te utilizzando il tuo framework di registrazione preferito.Uno sforzo in più per programmare, ma tanto meno lavoro in fase di esecuzione.

A meno che tu Veramente hai bisogno che il tuo Logger sia statico, potresti usare

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

Si prega di consultare la mia implementazione statica getLogger() (usa la stessa magia "sun.*" su JDK 7 come java Logger doit predefinito)

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

    importa statico my.pakg.Logger.*;

E la loro velocità è equivalente all'implementazione Java nativa (controllata 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);
}

}

Dare un'occhiata a Logger classe da jcabi-log.Fa esattamente quello che stai cercando, fornendo una raccolta di metodi statici.Non è più necessario incorporare 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 puoi reindirizzare a qualsiasi altra struttura di registrazione, in runtime.

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

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

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 va bene così.Qui utilizza l'API SLF4J

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

Ma in linea di principio può essere utilizzato con qualsiasi framework di registrazione.Se il logger necessita di un argomento stringa, aggiungi toString()

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top