Fazendo modificações de desempenho simples para um frasco já compilado?
-
05-07-2019 - |
Pergunta
Como muitos usuários log4j, muitas vezes temos registro de nível de depuração que é caro para avaliar. Então, nós proteger esses casos com um código como:
if( _logger.isDebugEnabled )
_logger.debug("Interesting, my foojes are goofed up: " + getFullDetails())
No entanto, isto é mais feio do que uma chamada _logger.debug simples, e às vezes o programador não percebe a avaliação poderia ser caro.
Parece que ele deve ser bastante simples para escrever um programa que leva um frasco compilado e guardas todas as chamadas _logger.debug com a verificação isDebugEnabled. Nós provavelmente seria dispostos a aceitar a sobrecarga extra de verificar isDebugEnabled em todos os casos.
Alguém já experimentou esta abordagem, ou pós-processamento semelhante feito de um frasco?
Solução
Você olhou para AspectJ ? Isto suporta aspectos utilizando bytecode tecelagem, e pode interceptações em um arquivo .jar compilada anteriormente .
Outras dicas
Ao invés de olhar para modificar o pote, eu procurar uma solução usando Bytecode Instrumentação . O problema será identificar as partes do código que você deseja quebrar dentro de um .isDebugEnabled()
-. Você terá que identificar objetos que são usados ??apenas para chamadas log4j
Eu acredito que uma boa solução seria a de que o código seria eficiente quanto é .
Considere que log4j é obsoleto. Seu autor em si deixou-o como é, para evitar quebrar a compatibilidade, mas ele criou um novo, SLF4J ( http: // www .slf4j.org / ). Ele fornece uma fachada e uma implementação, de acordo com a distinção commons-logging / log4j, mas sem as falhas de cada um ...
Eu acredito que, neste novo recurso de log, você pode enviar parâmetros de objeto para a extração de madeira, e que o nível é avaliado antes de converter os objetos (como String ou não). A idéia é usar uma seqüência de formato, e os parâmetros.
O nosso código não usa slf4j, mas temos métodos utilitários que fazem exatamente isso. Ele é codificado aproximadamente como segue (de memória):
public enum LogLevel {
FATAL, ERROR, WARNING, INFO, DEBUG;
public void log(Logger logger, String format, Object... parameters) {
if (isEnabled(logger)) {
logImpl(logger, String.format(format, parameters));
}
}
public boolean isEnabled(Logger logger) {
switch(this) {
case WARNING : return logger.isWarningEnabled();
case INFO : return logger.isInfoEnabled();
case DEBUG : return logger.isDebugEnabled();
default: return true;
}
}
private void logImpl(Logger logger, String message) {
switch(this) {
case WARNING : logger.warn(message);
// other cases
}
}
}
É usado como:
public void myMethod(Object param) {
LogLevel.WARNING.log(LOGGER, "What is my message ....", "myMethod", param);
}
Atualizar : Se você precisar chamar um método no log ...
-
Uma possibilidade é usar o método
toString
. Isto é apropriado se o seu registro for 'técnico' , e será usado também durante a depuração. -
Se o seu logging é mais funcional (não voltado para o developper), eu sugiro para definir uma interface (que é funcionalmente soar nesse caso, por isso é útil para fornecer significado) :
public interface Detailable { // the name could also suggest logging? String getFullDetails(); }
Implementar essa interface em qualquer objeto que precisam ser passados ??como objeto de registro, com um cálculo complexo para construir o log.