it-swarm-eu.dev

Java logger, který automaticky určuje jméno volajícího třídy

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

Tato metoda by vrátila logger, který zná třídu, do které se zapisuje. Jakékoliv nápady proti němu?

O mnoho let později: https://github.com/yanchenko/droidparts/blob/master/droidparts/src/org/droidparts/util/L.Java

33
yanchenko

Myslím, že to přidává spoustu režie pro každou třídu. Každá třída musí být „vzhlédla“. Vytvoříte pro to nové házitelné předměty ... Tyto házení nepřicházejí zdarma.

15
Daan

Vytvoření trasování zásobníku je relativně pomalá operace. Váš volající už ví, jakou třídu a metodu má, takže úsilí je zbytečné. Tento aspekt vašeho řešení je neefektivní.

I když použijete informace o statické třídě, neměli byste pro každou zprávu znovu načítat Logger. Od autora z Log4j, Ceki Gülcü: 

Nejběžnější chyba ve třídách wrapperu je vyvolání metody Logger.getLogger na každém požadavku protokolu. To je zaručeno, že způsobí spoušť na výkonu vaší aplikace. Opravdu!!! 

Toto je konvenční, efektivní idiom pro získání Loggeru je během inicializace třídy:

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

Všimněte si, že to poskytuje samostatný Logger pro každý typ v hierarchii. Pokud při instanci přijdete s metodou, která vyvolá getClass(), zobrazí se zprávy zaznamenané základním typem zobrazujícím se pod loggerem podtypu. Možná je to v některých případech žádoucí, ale považuji to za matoucí (a stejně mám sklon upřednostňovat složení před dědictvím). 

Použití dynamického typu přes getClass() bude vyžadovat, abyste logger získali alespoň jednou za instanci, spíše než jednou za třídu, jako je doporučený styl používající informace o statickém typu.

22
erickson

Třída MethodHandles class (jako Java 7) obsahuje třídu Lookup , která ze statického kontextu může najít a vrátit název aktuální třídy. Zvažte následující příklad:

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

Při spuštění se vytvoří:

Main

Pro záznamník můžete použít:

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

Ve třídě LogUtils vlastně máme něco podobného. Ano, je to druh icky, ale výhody jsou za to, co se mě týká. Chtěli jsme se ujistit, že jsme neměli žádnou režii z toho, že je opakovaně volán, takže naše (poněkud hacknutě) zajišťuje, že může být POUZE volána ze statického inicializačního kontextu, a la:

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

To se nezdaří, pokud je vyvoláno z normální metody nebo z inicializátoru instance (tj. Pokud bylo „statické“ vynecháno výše), aby se snížilo riziko režie. Metoda je:

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

Každý, kdo se ptá, jakou výhodu to má za sebou 

= Logger.getLogger(MyClass.class);

pravděpodobně nikdy nemusel jednat s někým, kdo kopíruje a vkládá řádek z jiného místa a zapomíná změnit název třídy a nechává vás zabývat se třídou, která posílá všechny své věci jinému loggeru.

17
Cowan

Za předpokladu, že si ponecháváte statické odrazy pro loggery, zde je samostatný statický 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();
    }
}

Použití je pěkné a čisté:

Logger logger = LoggerUtils.getLogger();
8
EGB

Pro každou třídu, kterou používáte, budete muset v každém případě vyhledat Logger, takže můžete v těchto třídách také použít statický Logger.

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

Pak stačí odkazovat na tento záznamník, když potřebujete dělat zprávy protokolu. Vaše metoda dělá to samé, že statický Log4J Logger již dělá, proč znovu objevovat kolo?

4
18Rabbit

Pak je nejlepší mix dvou. 

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

A pak v každé třídě:

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

v kódu:

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

Získáte statický záznamník, který můžete kopírovat a vkládat do každé třídy a bez režijních nákladů ...

Alaa

3
Alaa Murad

Po přečtení všech ostatních zpětných vazeb na těchto stránkách jsem vytvořil:

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

}

Nyní ve vašem kódu, co potřebujete, je

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

nebo

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

Pokud potřebujete více metod log4j, stačí je delegovat z výše uvedené třídy Logger.

3
Ed Sarrazin

Dávám přednost vytvoření (statického) loggeru pro každou třídu (s jeho explicitním názvem třídy). Já než používám záznamník tak, jak je.

2
Philip Helger

Samozřejmě můžete použít Log4J s příslušným rozvržením vzoru:

Například pro název třídy "org.Apache.xyz.SomeClass" bude na výstupu "SomeClass" vystupovat vzor% C {1}. 

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

2
Ian

Nemusíte vytvářet nový objekt Throwable. Stačí zavolat Thread.currentThread().getStackTrace()[1]

2
ykaganovich

Tento mechanismus má za běhu spoustu dalšího úsilí.

Pokud používáte Eclipse jako IDE, zvažte použití Log4e . Tento praktický plugin vygeneruje pro vás logovací deklaraci pomocí vašeho oblíbeného logovacího rámce. A zlomek větší úsilí při kódování času, ale mnohem méně práce za běhu.

0
Bill Michell

Dobrou alternativou je použití anotací lombok (jeden z): https://projectlombok.org/features/Log.html

Generuje odpovídající výpis protokolu s aktuální třídou.

0
user2189998

Pěkný způsob, jak to udělat z Java 7 dále:

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

Záznamník může být static a to v pořádku. Zde jeho použití SLF4J API

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

Ale v principu lze použít s jakýmkoliv logovacím rámcem. Pokud logger potřebuje argument řetězec add toString()

0
James Mudd

Mám na začátku většiny mých tříd následující řádek.

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

ano tam je nějaká režie, když je poprvé vytvořen objekt této třídy, ale pracuji většinou ve webapps, takže přidání mikrosekund do 20 sekundového spuštění není ve skutečnosti problém.

0
muttonUp

Pokud jste opravdu potřebujete, aby váš Logger byl statický, můžete použít

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

Rozhraní API protokolu Flogger Google toto podporuje, např.

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

Další informace naleznete na adrese https://github.com/google/flogger .

0
James Mudd

Podívejte se na Logger class z jcabi-log . Dělá přesně to, co hledáte, a poskytuje kolekci statických metod. Už nemusíte vkládat loggery do tříd:

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

Logger odešle všechny protokoly do SLF4J, které můžete přesměrovat do libovolného jiného logovacího zařízení v runtime.

0
yegor256

Proč ne?

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

A pak, když potřebujete logger pro třídu:

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

Prosím, podívejte se na mou statickou implementaci getLogger () (použijte stejné kouzlo "Sun. *" na JDK 7 jako výchozí Java Logger doit)

  • poznámka: statické metody protokolování (statický import) bez ošklivé vlastnosti protokolu ...

    import static my.pakg.Logger. *;

A jejich rychlost odpovídá nativní implementaci jazyka Java (zkontrolováno s 1 milionem stop protokolu)

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