Apportare semplici modifiche alle prestazioni a un vaso già compilato?
-
05-07-2019 - |
Domanda
Come molti utenti di log4j, spesso abbiamo una registrazione a livello di debug che è costosa da valutare. Quindi proteggiamo quei casi con un codice come:
if( _logger.isDebugEnabled )
_logger.debug("Interesting, my foojes are goofed up: " + getFullDetails())
Tuttavia, è più brutto di una semplice chiamata a _logger.debug e talvolta il programmatore non si rende conto che la valutazione potrebbe essere costosa.
Sembra che dovrebbe essere abbastanza semplice scrivere un programma che prende un jar compilato e protegge tutte le chiamate _logger.debug con il controllo isDebugEnabled. Saremmo probabilmente disposti ad accettare l'ulteriore sovraccarico di controllare isDebugEnabled in tutti i casi.
Qualcuno ha provato questo approccio o ha eseguito una post-elaborazione simile di un vaso?
Soluzione
Hai guardato AspectJ ? Ciò supporta aspetti che utilizzano la tessitura bytecode e può intercettare in un file .jar precedentemente compilato .
Altri suggerimenti
Invece di cercare di modificare il vaso, cerco una soluzione usando Strumentazione bytecode . Il problema sarà identificare quelle parti del codice che vuoi racchiudere in un .isDebugEnabled ()
- dovrai identificare gli oggetti che sono usati solo per le invocazioni log4j.
Credo che una buona soluzione sarebbe che il codice sarebbe efficiente come è .
Considera che log4j è obsoleto. L'autore stesso lo ha lasciato così com'è, per evitare di rompere la compatibilità, ma ne ha creato uno nuovo, SLF4J ( http: // www .slf4j.org / ). Fornisce sia una facciata che un'implementazione, secondo la distinzione commons-logging / log4j, ma senza i difetti di ogni ...
Credo che, in questa nuova funzione di registrazione, sia possibile inviare i parametri Object alla registrazione e che il livello venga valutato prima di convertire gli oggetti (in String o in altro modo). L'idea è di utilizzare una stringa di formato e parametri.
Il nostro codice non utilizza slf4j, ma abbiamo metodi di utilità che fanno esattamente questo. È codificato approssimativamente come segue (dalla 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
}
}
}
È usato come:
public void myMethod(Object param) {
LogLevel.WARNING.log(LOGGER, "What is my message ....", "myMethod", param);
}
AGGIORNAMENTO : se è necessario chiamare un metodo nel registro ...
-
Una possibilità è usare il metodo
toString
. Questo è appropriato se la tua registrazione è "tecnica" e verrà utilizzata anche durante il debug. Se la tua registrazione è più funzionale (non mirata allo sviluppatore), ti suggerisco di definire un'interfaccia (è funzionalmente sana in quel caso, quindi è utile fornire un significato) :
public interface Detailable { // the name could also suggest logging? String getFullDetails(); }
Implementa quell'interfaccia in qualsiasi oggetto che deve essere passato come oggetto di registrazione, con un calcolo complesso per costruire il registro.