it-swarm-pt.tech

Criador de log Java que determina automaticamente o nome da classe do chamador

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 retornaria um registrador que conhece a classe para a qual está registrando. Alguma idéia contra ele?

Muitos anos depois: https://github.com/yanchenko/droidparts/blob/master/droidparts/src/org/droidparts/util/L.Java

33
yanchenko

Eu acho que acrescenta muita sobrecarga para todas as classes. Cada turma tem que ser "olhado para cima". Você cria novos objetos Throwable para fazer isso ... Esses lançamentos não vêm de graça.

15
Daan

Criar um rastreamento de pilha é uma operação relativamente lenta. Seu interlocutor já sabe em que classe e método está, então o esforço é desperdiçado. Este aspecto da sua solução é ineficiente.

Mesmo se você usar informações de classe estática, você não deve buscar o Agente de Log novamente para cada mensagem. Do autor de Log4j, Ceki Gülcü: 

O erro mais comum nas classes de wrapper é a chamada do método Logger.getLogger em cada solicitação de log. Isso é garantido para causar estragos no desempenho do seu aplicativo. Mesmo!!! 

Este é o idioma convencional e eficiente para obter um Logger durante a inicialização da classe:

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

Observe que isso fornece um registrador separado para cada tipo em uma hierarquia. Se você inventar um método que chame getClass() em uma instância, você verá mensagens registradas por um tipo básico que aparece sob o registrador do subtipo. Talvez isso seja desejável em alguns casos, mas acho confuso (e eu tento favorecer a composição sobre a herança de qualquer maneira). 

Obviamente, usar o tipo dinâmico via getClass() exigirá que você obtenha o logger pelo menos uma vez por instância, em vez de uma vez por classe, como o idioma recomendado, usando informações de tipo estático.

22
erickson

A classe MethodHandles (a ​​partir do Java 7) inclui uma classe Lookup que, a partir de um contexto estático, pode encontrar e retornar o nome da classe atual. Considere o seguinte exemplo:

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 executado, isso produz:

Main

Para um logger, você poderia usar:

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

Na verdade, temos algo bastante semelhante em uma classe LogUtils. Sim, é meio nojento, mas as vantagens valem a pena no que me diz respeito. Nós queríamos ter certeza de que não tínhamos nenhuma sobrecarga de ser repetidamente chamada, então o nosso (de certa forma, de maneira muito rápida) garante que ele possa ser chamado APENAS de um contexto de inicializador estático, a la:

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

Ele falhará se for chamado a partir de um método normal ou de um inicializador de instância (ou seja, se a 'estática' foi deixada de fora) para reduzir o risco de sobrecarga de desempenho. O método é:

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

Alguém que pergunta que vantagem isso tem sobre 

= Logger.getLogger(MyClass.class);

provavelmente nunca teve que lidar com alguém que copia e cola essa linha de outro lugar e se esquece de mudar o nome da classe, deixando você lidando com uma classe que envia todas as suas coisas para outro logger.

17
Cowan

Supondo que você esteja mantendo referências estáticas aos registradores, aqui está um singleton estático autônomo:

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

O uso é agradável e limpo:

Logger logger = LoggerUtils.getLogger();
8
EGB

Para cada classe com a qual você usa isso, você terá que procurar o Logger de qualquer maneira, então é melhor usar um Logger estático nessas classes.

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

Então você apenas faz referência a esse logger quando precisar fazer suas mensagens de log. Seu método faz a mesma coisa que o Log4J Logger já faz, então por que reinventar a roda?

4
18Rabbit

Então a melhor coisa é mistura de dois. 

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 depois, em todas as aulas:

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

em código :

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

Você obtém logger estático que você pode simplesmente copiar e colar em todas as classes e sem sobrecarga ...

Alaa

3
Alaa Murad

Da leitura a todos os outros comentários neste site, criei o seguinte para uso com o 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);
}

}

Agora no seu código tudo que você precisa é

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

ou

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

Se você precisar de mais exposição aos métodos log4j, basta delegá-los da classe Logger listada acima.

3
Ed Sarrazin

Eu prefiro criar um registrador (estático) para cada classe (com seu nome de classe explícito). Eu uso o logger como está.

2
Philip Helger

Você poderia, é claro, apenas usar o Log4J com o layout padrão apropriado:

Por exemplo, para o nome de classe "org.Apache.xyz.SomeClass", o padrão% C {1} produzirá "SomeClass". 

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

2
Ian

Você não precisa criar um novo objeto Throwable. Você pode apenas ligar para Thread.currentThread().getStackTrace()[1]

2
ykaganovich

Esse mecanismo faz muito esforço extra no tempo de execução.

Se você usar o Eclipse como seu IDE, considere usar Log4e . Este prático plugin irá gerar declarações de logger para você usando sua estrutura de log de favoritos. Uma fração mais de esforço no tempo de codificação, mas muito menos trabalho em tempo de execução.

0
Bill Michell

Uma boa alternativa é usar (uma das) anotações de logs do lombok: https://projectlombok.org/features/Log.html

Gere a declaração de log correspondente com a classe atual.

0
user2189998

Uma boa maneira de fazer isso a partir do Java 7:

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

O registrador pode ser static e esta bem. Aqui está usando a API SLF4J

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

Mas no principal pode ser usado com qualquer estrutura de log. Se o logger precisar de um argumento de string, adicione toString()

0
James Mudd

Eu só tenho a seguinte linha no início da maioria das minhas aulas.

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

sim, há alguma sobrecarga na primeira vez que um objeto dessa classe é criado, mas eu trabalho principalmente em aplicativos da Web, portanto, adicionar microssegundos a uma inicialização de 20 segundos não é realmente um problema.

0
muttonUp

A menos que você realmente precise que seu registrador seja estático, você pode usar

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

A API de registro do Google Flogger oferece suporte a isso, por exemplo.

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

Veja https://github.com/google/flogger para mais detalhes.

0
James Mudd

Dê uma olhada em Logger class de jcabi-log . Ele faz exatamente o que você está procurando, fornecendo uma coleção de métodos estáticos. Você não precisa mais inserir os registradores nas classes:

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

Logger envia todos os logs para o SLF4J, que você pode redirecionar para qualquer outro recurso de registro, em tempo de execução.

0
yegor256

Por que não?

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

E quando você precisar de um registrador para uma classe:

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

Por favor, veja minha implementação estática de getLogger () (use a mesma magia "Sun. *" no JDK 7 como padrão Java Logger doit)

  • observe métodos de log estático (com importação estática) sem propriedade de log feia ...

    import static my.pakg.Logger. *;

E sua velocidade é equivalente à implementação Java nativa (verificada com 1 milhão de traços de 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