2012-11-02 9 views
8

Hier sind zwei Aussagen, die allgemein akzeptiert zu sein scheinen, aber das kann ich wirklich nicht verwinden:Parametrisierte Protokollierung in slf4j - wie wird es mit den Parametern von scala verglichen?

1) Scala by-Name params anmutig ersetzt das immer so ärgerlich log4j Nutzungsmuster:

if (l.isDebugEnabled()) { 
     logger.debug("expensive string representation, eg: godObject.toString()") 
    } 

, da der By-Name-Parameter (eine Scala-spezifische Sprachfunktion) nicht vor dem Methodenaufruf ausgewertet wird.

logger.debug("expensive string representation, eg {}:", godObject[.toString()]); 

Also, wie funktioniert das:

2) Jedoch wird dieses Problem durch parametrisierte Protokollierung in slf4f gelöst? Gibt es in der Bibliothek slf4j eine Low-Level-Magie, die die Auswertung des Parameters vor der Ausführung der "Debug" -Methode verhindert? (Ist das überhaupt möglich? Kann eine Bibliothek solch einen fundamentalen Aspekt der Sprache beeinflussen?)

Oder ist es einfach die einfache Tatsache, dass ein Objekt an die Methode übergeben wird - und nicht ein String? (und möglicherweise wird toString() dieses Objekts in der debug() -Methode selbst aufgerufen, falls zutreffend).

Aber stimmt das nicht auch für log4j? (Es hat Methoden mit Objektparametern). Und würde das nicht bedeuten, dass, wenn Sie eine Zeichenfolge übergeben - wie im obigen Code - würde es sich identisch mit log4j verhalten?

Ich würde wirklich gerne etwas Licht in diese Angelegenheit werfen.

Danke!

Antwort

24

Es gibt keine Magie in slf4j. Das Problem mit der Protokollierung verwendet sein, dass, wenn man wollte die

logger.debug("expensive string representation: " + godObject) 

dann unabhängig davon, ob die Debug-Level sagen protokollieren lassen wurden im Logger aktiviert oder nicht, können Sie immer godObject.toString() ausgewertet, die eine teuere Operation sein können, und dann auch String-Verkettung. Dies ergibt sich einfach aus der Tatsache, dass in Java (und den meisten Sprachen) Argumente ausgewertet werden, bevor sie an eine Funktion übergeben werden.

Deshalb eingeführt slf4j (und andere Varianten für weitere Argumente). Die ganze Idee ist, dass Sie billige Argumente an die debug-Funktion übergeben und sie toString auf ihnen aufruft und sie nur dann in eine Nachricht zusammenfasst, wenn der Debug-Level aktiviert ist.

Beachten Sie, dass

logger.debug("expensive string representation, eg: {}", godObject.toString()); 

Sie drastisch diesen Vorteil reduzieren, da diese Art und Weise Sie godObject die ganze Zeit zu konvertieren, indem Aufruf, bevor Sie es an debug passieren, egal, welche Debug-Ebene eingeschaltet ist. Sie sollten nur

logger.debug("expensive string representation, eg: {}", godObject); 

jedoch verwenden, dies immer noch nicht ideal ist. Es erspart nur den Aufruf toString und String-Verkettung. Wenn Ihre Protokollierungsnachricht jedoch eine andere kostspielige Verarbeitung erfordert, um die Nachricht zu erstellen, wird es nicht helfen.Wie, wenn Sie brauchen, um einige expensiveMethod rufen Sie die Nachricht zu erstellen:

logger.debug("expensive method, eg: {}", 
    godObject.expensiveMethod()); 

dann expensiveMethod immer vor zu logger bestanden bewertet wird. Um diese Arbeit effizient mit slf4j zu machen, müssen Sie immer noch zurück greifen, um

if (logger.isDebugEnabled()) 
    logger.debug("expensive method, eg: {}", 
     godObject.expensiveMethod()); 

Scala Call-by-Namen viel in dieser Angelegenheit hilft, weil es Ihnen erlaubt, beliebiges Stück Code in eine Funktion Objekt zu wickeln und Bewerten Sie diesen Code nur bei Bedarf. Genau das brauchen wir. Schauen wir uns zum Beispiel slf4s an. Diese Bibliothek stellt Methoden wie

def debug(msg: => String) { ... } 

Warum wie keine Argumente in slf4j der Logger? Weil wir sie nicht mehr brauchen. Wir können nur

logger.debug("expensive representation, eg: " + 
    godObject.expensiveMethod()) 

Wir schreiben nicht passieren eine Nachricht und ihre Argumente geben wir direkt ein Stück Code, der auf die Nachricht ausgewertet wird. Aber nur wenn der Logger sich dazu entschließt. Wenn der Debug-Level nicht aktiviert ist, wird nichts, was in logger.debug(...) ist, jemals ausgewertet, das Ganze wird einfach übersprungen. Weder expensiveMethod wird aufgerufen noch toString Aufrufe oder String-Verkettung passieren. Dieser Ansatz ist also am allgemeinsten und am flexibelsten. Sie können jeden Ausdruck übergeben, der String zu debug auswertet, egal wie komplex es ist.

+1

Kristallklar, vielen Dank. Ich schätze, deine gründliche Erklärung gleicht die Magie aus, auf die ich gehofft hatte und die ich nicht bekommen habe. :) – teo