it-swarm-es.com

Java logger que determina automáticamente el nombre de la clase del llamante

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

Este método devolvería un registrador que conoce la clase para la que está registrando. ¿Alguna idea en contra?

Muchos años después: https://github.com/yanchenko/droidparts/blob/master/droidparts/src/org/droidparts/util/L.Java

35
yanchenko

Supongo que agrega muchos gastos generales para cada clase. Cada clase tiene que ser "buscada". Usted crea nuevos objetos Throwable para hacer eso ... Estos objetos arrojables no son gratuitos.

15
Daan

Crear un seguimiento de pila es una operación relativamente lenta. Su interlocutor ya sabe en qué clase y método se encuentra, por lo que se desperdicia el esfuerzo. Este aspecto de su solución es ineficiente.

Incluso si usa información de clase estática, no debe buscar el Logger nuevamente para cada mensaje. Del autor de Log4j, Ceki Gülcü:

El error más común en las clases de contenedor es la invocación del método Logger.getLogger en cada solicitud de registro. Esto garantiza causar estragos en el rendimiento de su aplicación. ¡¡¡De Verdad!!!

Este es el idioma convencional y eficiente para obtener un Logger durante la inicialización de la clase:

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

Tenga en cuenta que esto le proporciona un registrador separado para cada tipo en una jerarquía. Si encuentra un método que invoca getClass() en una instancia, verá mensajes registrados por un tipo base que se muestran debajo del registrador del subtipo. Tal vez esto sea deseable en algunos casos, pero lo encuentro confuso (y tiendo a favorecer la composición sobre la herencia de todos modos).

Obviamente, usar el tipo dinámico a través de getClass() requerirá que obtenga el registrador al menos una vez por instancia, en lugar de una vez por clase como el idioma recomendado usando información de tipo estático.

23
erickson

La clase MethodHandles (a partir de Java 7) incluye una clase Lookup que, desde un contexto estático, puede encontrar y devolver el nombre de la clase actual. Considere el siguiente ejemplo:

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

Cuando se ejecuta esto produce:

Main

Para un registrador, puede usar:

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

De hecho, tenemos algo bastante similar en una clase LogUtils. Sí, es un poco asqueroso, pero las ventajas lo valen en lo que a mí respecta. Sin embargo, queríamos asegurarnos de que no teníamos ningún gasto indirecto debido a que se llamaba repetidamente, por lo que el nuestro (algo hackeo) garantiza que SOLO se pueda llamar desde un contexto de inicializador estático, a la:

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

Fallará si se invoca desde un método normal o desde un inicializador de instancia (es decir, si el 'estático' se dejó arriba) para reducir el riesgo de sobrecarga del rendimiento. El metodo es:

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

Cualquiera que pregunte qué ventaja tiene esto sobre

= Logger.getLogger(MyClass.class);

probablemente nunca haya tenido que tratar con alguien que copia y pega esa línea desde otro lugar y se olvida de cambiar el nombre de la clase, dejándote lidiando con una clase que envía todas sus cosas a otro registrador.

18
Cowan

Suponiendo que mantiene referencias estáticas a los registradores, aquí hay un singleton estático independiente:

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

El uso es agradable y limpio:

Logger logger = LoggerUtils.getLogger();
8
EGB

Para cada clase con la que use esto, tendrá que buscar el registrador de todos modos, por lo que también podría usar un registrador estático en esas clases.

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

Luego solo hace referencia a ese registrador cuando necesita hacer sus mensajes de registro. Su método hace lo mismo que el Log4J Logger estático, ¿por qué reinventar la rueda?

4
18Rabbit

Entonces lo mejor es una mezcla de dos.

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

Y luego en cada clase:

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

en codigo :

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

Obtiene un registrador estático que puede copiar y pegar en cada clase y sin gastos generales ...

Alaa

3
Alaa Murad

Al leer todos los otros comentarios en este sitio, creé lo siguiente para usar 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);
}

}

Ahora en tu código todo lo que necesitas es

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

o

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

Si necesita más exposición a los métodos log4j, simplemente delegarlos de la clase Logger enumerada anteriormente.

3
Ed Sarrazin

Prefiero crear un registrador (estático) para cada clase (con su nombre de clase explícito). Yo que uso el registrador como es.

2
Philip Helger

Por supuesto, podría usar Log4J con el diseño de patrón apropiado:

Por ejemplo, para el nombre de clase "org.Apache.xyz.SomeClass", el patrón% C {1} generará "SomeClass".

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

2
Ian

No necesita crear un nuevo objeto Throwable. Simplemente puede llamar a Thread.currentThread().getStackTrace()[1]

2
ykaganovich

Solo tengo la siguiente línea al comienzo de la mayoría de mis clases.

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

sí, hay algunos gastos generales la primera vez que se crea un objeto de esa clase, pero trabajo principalmente en aplicaciones web, por lo que agregar microsegundos en un inicio de 20 segundos no es realmente un problema.

1
muttonUp

La API de registro de Google Flogger admite esto, p.

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

Ver https://github.com/google/flogger para más detalles.

1
James Mudd

ESCUELA VIEJA simple y trivial:

Simplemente cree su propia clase y pase el nombre de la clase, el nombre del método + comentario (si la clase/método cambió, se refactorizan automáticamente Mayús + F6)

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

y simplemente úselo en cualquier lugar de la aplicación (no se requiere contexto, no se inicializa, no hay libs adicionales ni consulta): ¡se puede usar para cualquier lenguaje de programación!

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

esto se imprimirá en tu consola:

MY_TAG clase: MainActivity meth: onCreate comm: Hola mundo

0
Choletski

Este mecanismo requiere mucho esfuerzo adicional en tiempo de ejecución.

Si usa Eclipse como su IDE, considere usar Log4e . Este práctico complemento generará declaraciones de registrador para usted utilizando su marco de registro favorito. Una fracción más de esfuerzo en el tiempo de codificación, pero mucho menos trabajo en tiempo de ejecución.

0
Bill Michell

Una buena alternativa es usar (una de) las anotaciones de registros de lombok: https://projectlombok.org/features/Log.html

Genera la instrucción de registro correspondiente con la clase actual.

0
user2189998

Una buena manera de hacer esto desde Java 7 en adelante:

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

El registrador puede ser static y eso está bien. Aquí está usando la API SLF4J

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

Pero en principio se puede usar con cualquier marco de registro. Si el registrador necesita un argumento de cadena, agregue toString()

0
James Mudd

A menos que usted realmente necesite que su Logger sea estático, puede usar

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

Eche un vistazo a Logger clase de jcabi-log . Hace exactamente lo que está buscando, proporcionando una colección de métodos estáticos. Ya no necesita incrustar registradores en las clases:

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

Logger envía todos los registros a SLF4J, que puede redirigir a cualquier otra instalación de registro, en tiempo de ejecución.

0
yegor256

Por qué no?

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

Y luego, cuando necesita un registrador para una clase:

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

Consulte mi implementación de getLogger () estático (use la misma magia "Sun. *" en JDK 7 como predeterminado Java Logger doit)

  • observe los métodos de registro estático (con importación estática) sin la propiedad de registro feo ...

    import static my.pakg.Logger. *;

Y su velocidad es equivalente a la implementación nativa Java (marcada con 1 millón de trazas de registro)

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