¿Haciendo simples modificaciones de rendimiento a un tarro ya compilado?
-
05-07-2019 - |
Pregunta
Como muchos usuarios de log4j, a menudo tenemos un registro de nivel de depuración que es costoso evaluar. Así que guardamos esos casos con código como:
if( _logger.isDebugEnabled )
_logger.debug("Interesting, my foojes are goofed up: " + getFullDetails())
Sin embargo, eso es más feo que una simple llamada _logger.debug, y algunas veces el programador no se da cuenta de que la evaluación podría ser costosa.
Parece que debería ser bastante simple escribir un programa que tome un archivo compilado y proteja todas las llamadas _logger.debug con el cheque isDebugEnabled. Es probable que estemos dispuestos a aceptar que la sobrecarga adicional de la comprobación es DebugEnabled en todos los casos.
¿Alguien ha intentado este enfoque o ha realizado un procesamiento posterior similar de un frasco?
Solución
¿Ha visto AspectJ ? Esto admite aspectos que utilizan el tejido de código de bytes y puede interceptar en un archivo .jar previamente compilado .
Otros consejos
En lugar de buscar modificar el archivo, buscaría una solución usando Instrumentación Bytecode . El problema será identificar las partes del código que desea ajustar dentro de un .isDebugEnabled ()
: tendrá que identificar los objetos que solo se usan para las invocaciones de log4j.
Creo que una buena solución sería que el código sería eficiente como es .
Considera que log4j está en desuso. Su propio 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 uno ...
Creo que, en este nuevo servicio de registro, puede enviar parámetros de Objeto al registro, y que el nivel se evalúa antes de convertir los Objetos (a Cadena o de otra manera). La idea es utilizar una cadena de formato y parámetros.
Nuestro código no usa slf4j, pero tenemos métodos de utilidad que hacen exactamente eso. Se codifica aproximadamente de la siguiente manera (de la 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 usar el método
toString
. Esto es apropiado si su registro es 'técnico' , y se usará también al depurar. Si su registro es más funcional (no está dirigido al desarrollador), sugiero que defina una interfaz (es funcional en este caso, por lo que es útil proporcionar un significado) :
public interface Detailable { // the name could also suggest logging? String getFullDetails(); }
Implemente esa interfaz en cualquier objeto que deba pasarse como objeto de registro, con un cálculo complejo para construir el registro.