13

LOG.debug("Exported {}.", product)slf4jのようなことをすると、最終的に引数に対して toString() が呼び出されproductます。

特定の理由により、引数として使用したいすべてのクラスで toString() をオーバーライドすることはできません。一部のクラスはサードパーティの jar から取得されますが、他のコンテキストでは toString() が呼び出されるため、ログ ステートメントに出力したい情報が利用できません。

DebugFormatter.format(Object)ただし、オブジェクトに関する有用なデバッグ情報を見つけるためにルーチンを選択する instanceofs の長いカスケードを持つメソッドを持つデバッグ目的のクラスがあります。

私の質問は: toString() の代わりにそのような静的メソッドを呼び出すように slf4j を構成することは可能ですか?

もちろん、オブジェクトをパラメーターとして渡す前に、オブジェクトで format メソッドを呼び出すこともできますLogger.debug()が、それぞれのロガーが有効になっていない場合でも実行されます。だから私はそれを囲む必要がありましif (LOG.isDebugEnabled())た。これは、debug() に引数を持つという全体のポイントが失われたことを意味します。

4

4 に答える 4

11

DebugFormatter.format()オブジェクトを取得してそのtoString()関数から呼び出して、shim を作成できます。このようなもの:

class DebugFormatObject {
  private final Object o;

  public static DebugFormatObject forDebug(Object o) {
    return new DebugFormatObject(o);
  }

  private DebugFormatObject(Object o) {
    this.o = o;
  }

  @Override
  public String toString() {
    return DebugFormatter.format(o);
  }
}

適切な静的インポートを使用すると、ログ ステートメントは次のようになります。

LOG.debug("Exported {}.", forDebug(product));

これには、オブジェクトを直接渡すよりもオーバーヘッドがわずかに大きくなりますが、これは小さく一定のオーバーヘッドであり、作成されたオブジェクトの寿命は非常に短くなります。

于 2012-07-02T14:54:37.100 に答える
6

基礎となるロギング フレームワークがなどのネイティブ実装である場合、引数に対する toString() 呼び出しは、 SLF4J ではなくロギング フレームワークによって実行されます。カスタムコンバーターを作成することにより、ログメッセージが実際に出力/印刷されるときに DebugFormatter.format(o) を呼び出すことができます。具体的には、%msg/%message を置き換えるコンバーターを作成します。

非ネイティブ実装の場合、toString() 呼び出しは SLF4J によって行われます。したがって、Andrew と Sean によって提供された回答が適用されます。

于 2012-07-03T09:55:55.903 に答える
4

カスタムLogbackConvereterを作成したい場合は、レガシーオブジェクトの完全なダンプを回避するために作成したコードを次に示します。

...
import ch.qos.logback.classic.pattern.MessageConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;

public class PrettyMessageConverter extends MessageConverter {

    private static final String EMPTY = "";
    private static final String PLACEHOLDER = "{}";

    @Override
    public String convert(ILoggingEvent event) {
        StringBuilder message = new StringBuilder(event.getMessage());

        int argumentIndex = 0;
        int placeholderIndex = -1;
        while ( (placeholderIndex=message.indexOf(PLACEHOLDER, placeholderIndex))!=-1 && message.charAt(placeholderIndex-1)!='\\' ) {
            String stringValue = valueOf(getArgument(event.getArgumentArray(), argumentIndex++));
            message.replace(placeholderIndex, placeholderIndex+PLACEHOLDER.length(), stringValue);
        }
        return message.toString();
    }

    /**
     * Return a {@link String} representation of the given object. It use convert 
     * some complex objects as a single line string and use {@link String#valueOf(Object))} 
     * for other objects.
     */
    private String valueOf(final Object object) {
        if ( object instanceof AuthenticatedUser )
            return valueOf((AuthenticatedUser) object);

        return String.valueOf(object);
    }

    private String valueOf(AuthenticatedUser user) {
        return user.getUsername();
    }

    /** 
     * Retrieve an argument at a given position but avoid {@link ArrayIndexOutOfBoundsException}
     * by returning {@link PrettyMessageConverter#EMPTY} string when the index 
     * is out of bounds. 
     */
    private Object getArgument(Object[] arguments, int index) {
        return (index<arguments.length)?arguments[index]:EMPTY;
    }

}

logback.xmlからのこの行で:

 <conversionRule conversionWord="msg" converterClass="PrettyMessageConverter" />
于 2012-08-29T09:45:27.630 に答える
2

残念ながら、slf4j はそのような方法で拡張できるように構築されていません。できることは、既存の実装の周りのデコレーターとして機能する独自の slf4j 実装を実装し、いくつかの既知の型に特別な処理を提供することです。しかし、多くの車輪を再発明する必要があります。if(underLyingLogger.isXyzEnabled())例:デコレーターが基礎となるメソッドを呼び出した後、基礎となるロガーはまったく同じチェックを再度実行しますが、ロガー実装メソッドはそれぞれから開始する必要があります。

一方、私が過去に使用した回避策は ToString-Delegate です。最も簡単な形式は、無名のオブジェクトです。

final SomeObject myObj = ...;
LOG.debug("foo {}", new Object(){
     public String toString(){
         return myObj.someMethod();
     }
});

ここにも短命のラッパー オブジェクトがありますが、実際には必要になるまで文字列をレンダリングしません。

上記の構文は非常に見苦しいため、これらの ToString オブジェクトを作成する静的ヘルパー メソッドを Factory クラスに提供することをお勧めします。私の場合、ToStringWrapper という抽象クラスがあり、グアバ ジョイナーなどでイテラブルを結合するためのファクトリ メソッドがあります。

于 2012-07-02T15:09:10.407 に答える