2009-10-22 10 views
5

Como muchos usuarios de log4j, a menudo tenemos un registro de nivel de depuración que es costoso de evaluar. Así protegemos esos casos con un código como:¿Haciendo modificaciones de rendimiento simples a un contenedor ya compilado?

if(_logger.isDebugEnabled) 
    _logger.debug("Interesting, my foojes are goofed up: " + getFullDetails()) 

Sin embargo, es más feo que una llamada _logger.debug normal, ya veces el programador no se da cuenta de la evaluación podría ser costoso.

Parece que debería ser bastante simple escribir un programa que tome un jar compilado y guarde todas las llamadas a _logger.debug con la comprobación isDebugEnabled. Es probable que estemos dispuestos a aceptar la sobrecarga adicional de comprobar que se haya habilitado DebugEnabled en todos los casos.

¿Alguien ha probado este enfoque o hecho un post-procesamiento similar de un jarro?

+0

No estoy seguro de que log4j no lo haga ya (comprobación interna que esDebugEnabled = true antes de imprimir una gran cantidad y tiempo para evaluar cosas) ... ¿lo has comprobado? –

+1

log4j no comprueba el nivel de registro en sí (por supuesto). La razón por la cual las personas están usando el indicador 'isDebugEnabled()' es que quieren evitar la sobrecarga causada por la construcción de cadenas ('" Interesante, mis foojes están equivocados: "+ getFullDetails()' en el ejemplo) y similares . – sfussenegger

+1

log4j hace check isDebugEnabled primero. Estoy tratando de evitar la evaluación getFullDetails si Debug no está habilitado. –

Respuesta

2

¿Has visto AspectJ? Esto admite aspectos mediante el uso de bytecode weaving, y puede intercepciones into a previously compiled .jar file.

+0

Muy útil, esto es lo más cercano a lo que estaba buscando. –

+0

No veo cómo esto ayuda con el ejemplo de prueba. Incluso si establece un corte de punto en 'Logger.debug (String)' con el consejo 'around' para evitar que se invoque,' getFullDetails() 'seguirá siendo llamado. – McDowell

4

En lugar de buscar modificar el jar, buscaría una solución usando Bytecode Instrumentation. El problema será identificar las partes del código que desea envolver dentro de un .isDebugEnabled(); deberá identificar los objetos que solo se utilizan para las invocaciones de log4j.

+0

Supongo que podría retroceder desde el 'ivokevirtual' en' debug' para encontrar la instrucción 'getfield' que obtuvo el registrador. Sin embargo, puede haber casos donde es ambiguo donde comienza y termina la llamada de registro. Vale la pena investigar – McDowell

1

Creo que una buena solución sería que el código sería eficiente como.

Tenga en cuenta que log4j está en desuso. Su autor lo dejó como está, para evitar romper la compatibilidad, pero creó uno nuevo, SLF4J (http://www.slf4j.org/). Proporciona tanto una fachada como una implementación, de acuerdo con la distinción commons-logging/log4j, pero sin los defectos de cada ...

Creo que, en esta nueva instalación de registro, puede enviar parámetros de Objeto al registro , y que el nivel se evalúa antes de convertir los Objetos (a Cadena u otros). La idea es usar una cadena de formato y parámetros.


Nuestro código no utiliza slf4j, pero tenemos métodos de utilidad que hacen exactamente eso. Se codifica más o menos de la siguiente manera (de memoria):

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

Se utiliza como:

 public void myMethod(Object param) { 
     LogLevel.WARNING.log(LOGGER, "What is my message ....", "myMethod", param); 
    } 

ACTUALIZACIÓN: Si necesita llamar a un método en el registro .. .

  • Una posibilidad es utilizar toString método. Esto es apropiado si su registro es 'técnico', y se utilizará también cuando se depure.

  • Si su registro es más funcional (no dirigida a la developper), sugiero para definir una interfaz (es funcionalmente sonido en ese caso, lo que es útil para proporcionar significado):

    public interface Detailable { // the name could also suggest logging? 
        String getFullDetails(); 
    } 
    

    Implemente esa interfaz en cualquier objeto que necesite pasar como objeto de registro, con un cálculo complejo para compilar el registro.

+0

Pasar objetos a la llamada de registro no ayuda. El costo está en evaluar getFullDetails. –

+1

@Ted, si cambia getFullDetails para proporcionar un objeto que haga la evaluación en su método toString que demoraría efectivamente el trabajo. Eso ayudaría a todos los casos, excepto cuando "el programador no se da cuenta de que la evaluación podría ser costosa". – Yishai

+0

Podría hacer algo como esto: 'log.debug (" Interesante, mis foojes son tontos: {} ", nuevo Object() {String toString() {return getFullDetails());' - pero es más lindo que el ¿original? :) – sfussenegger

Cuestiones relacionadas