web-dev-qa-db-ja.com

Java Logging:ロギングヘルパーメソッドではなく、呼び出し元のソース行番号を表示します)

Java=)の多数の(ため息...)ロギングフレームワークはすべて、ログメッセージを作成したメソッドのソースファイル名の行番号を表示するという素晴らしい仕事をします。

log.info("hey");

 [INFO] [Foo:413] hey

ただし、間にヘルパーメソッドがある場合、実際の呼び出し元はヘルパーメソッドになります。これはあまり有益ではありません。

log_info("hey");

[INFO] [LoggingSupport:123] hey

印刷するソースの場所を特定するときに、ロギングシステムにコールスタックから1フレームを削除するように指示する方法はありますか?

これは実装固有のものだと思います。 Commons Loggingを介したLog4Jが必要ですが、他のオプションについて聞きたいです。

44
Thilo

別の答え。

メソッドを使用して、ヘルパークラスを除外するようにlog4jに要求することができます。

Category.log(String callerFQCN、Priority level、Object message、Throwable t)

ヘルパークラスを「callerFQCN」として指定します。

たとえば、次はヘルパーを使用するクラスです。

public class TheClass {
    public static void main(String...strings) {
        LoggingHelper.log("Message using full log method in logging helper.");
        LoggingHelper.logNotWorking("Message using class info method");
}}

ヘルパーのコード:

public class LoggingHelper {
private static Logger LOG = Logger.getLogger(LoggingHelper.class);

public static void log(String message) {
    LOG.log(LoggingHelper.class.getCanonicalName(), Level.INFO, message, null);
}

public static void logNotWorking(String message) {
    LOG.info(message);
} }

最初の方法は期待した結果を出力します。

 Line(TheClass.main(TheClass.Java:4))ロギングヘルパーでフルログメソッドを使用したメッセージ。
 Line(LoggingHelper.logNotWorking(LoggingHelper.Java:12))クラス情報メソッドを使用したメッセージ

この方法を使用すると、Log4jは通常どおり動作し、スタックトレースが不要な場合は計算されません。

34
vdr

行番号を指定することは、Log4jから自然に取得するものか、以下のいずれか非常にコストがかかるであることに注意してください。あなたはその費用を受け入れなければなりません...

次のAPIを使用できます。

    StackTraceElement[] stackTraces = Thread.currentThread().getStackTrace();
    StackTraceElement stackTraceElement = ...;
    stackTraceElement.getLineNumber();

更新しました:

自分で計算する必要があります。そう:

  • (ログ形式で)出力しないようにlog4jに要求します。
  • そして、メッセージの最初に自分自身の行番号の明示を挿入します(log4jに送信する文字列)。

ロガーの好みに応じて、ヘルパーメソッドは次のようになります。

  • 必要に応じて、(おそらくパラメーターとして渡される)明示的なロガーを使用します(特定のコンテキストに対して特定のロガーを定義する場合があります。たとえば、データベースリクエストを送信するためのロガーがあり、どのクラスがそれを実行するかに関係なく、これにより、構成ファイルに加えた変更を1か所に配置し、それらを(非)活動化したいときに...)
  • 呼び出し元のクラスにロガーを使用します。この場合、パラメーターを渡す代わりに呼び出し元のクラス名を推測できます同様に...
5
KLE

非常に単純な解決策があることがわかります。ロガーヘルパーに [〜#〜] fqcn [〜#〜] (ラッパークラスの完全修飾クラス名)を追加するだけです。

public class MyLogger extends Logger {

private static final String FQCN = MyLogger.class.getName() + ".";

protected MyLogger(String name) {
    super(name);
}

public void info(final Object msg) {
    super.log(FQCN, Level.INFO, msg, null);
}

//etc...

あなたの労働者階級であなたはただします:

public class MyClass {

private static final Logger LOG = MyLogger.getLogger();   

private void test()
{
    LOG.info("test");
}

}
4
savemaxim

KLE回答に詳細を追加します。 (申し訳ありませんが、noobユーザーは、別の回答を作成するよりも良い方法を知りません)

メッセージに行番号を付ける代わりに、MDCコンテキストに入れることができます。 org.Apache.log4j.MDCを参照してください

例えば:

StackTraceElement[] stackTraces = Thread.currentThread().getStackTrace();
StackTraceElement stackTraceElement = ...;
int l = stackTraceElement.getLineNumber();

MDC.put("myLineNumber", l);

これにより、ユーザーはlog4j構成ファイルでmylineNumberを使用できます

<layout class="org.Apache.log4j.PatternLayout">
    <param name="ConversionPattern" 
           value="Line(%X{myLineNumber})- %m%n"/>
</layout>

注:これにより、ユーザーはメッセージ内の行番号がどこにどのように表示されるかを制御できます。ただし、スタックトレースの取得は非常にコストがかかるため、機能をオフにする方法を見つける必要があります。

2
vdr

Log4j2の場合、Log4j2マニュアルの 生成されたロガーラッパーの使用例 で説明されているように、ロガーラッパーを使用することで完全に答えが得られます。単一のSTUBレベルのロガーラッパーを(そこに示されているorg.Apache.logging.log4j.core.tools.Generate $ ExtendedLoggerツールを使用して)生成し、それを適応させて、logIfEnabledの使用を模倣したカスタムロギングメソッドを作成できます。 (FQCN、LEVEL、マーカー、メッセージ、スロー可能)-STUBレベルを無視して通常のレベルを使用する-必要に応じて、STUBレベルとそのメソッドを削除またはコメント化する)。このためには、FormattedMessageが役立ちます。

ソース行は高価ですが、構成で指定された PatternLayout の%l位置変換パターン要素を使用するか、より具体的には%Lを使用することで、完全な位置情報の一部として簡単に表示できます。行番号や%Mメソッド変換。

次に、完全な例を示します。 Java Logging:Log4j Version2.x:エンドクライアントの呼び出し元のメソッドを表示します(中間のロギングヘルパーメソッドではありません)

これはそのままでは不可能です。この場合にできる最善の方法は、呼び出し元でロガーを作成し、それをutilメソッドに渡すことです。このようにして、少なくとも呼び出しがどこから来たかを知ることができます。

1
Aaron Digulla

独自のロギングユーティリティメソッドがある場合は、ロギング引数リストに行番号とファイル名を追加して、cppルートを取ることができます。つまり、_ [〜#〜] line [〜#〜] _および_ [〜#〜] file [〜#〜] _のようなタグを置き換えるためにソースを前処理しますコンパイルを行います。追加のボーナスとして、これは実行時に把握するほど多くのリソースを必要としません。

1
Some one

おそらく、スタックトレース要素を使用してログヘルパー関数を実装し、行番号を取得して、次のような特定の注釈を付けたメソッドでフレームをバイパスできます。

public @interface SkipFrame {}

// helper function
@SkipFrame // not necessary on the concrete log function
void log(String... message) {
    // getStackTrace()...
    int callerDepth = 2;  // a constant number depends on implementation
    StackTraceElement callerElement = null; 
    for (StackTraceElement e: stackTrace) {
         String className, methodName = e.getClassName, getMethodName()...
         Class callClass = Class.forName(className);
         // since there maybe several methods with the same name
         // here skip those overloaded methods
         Method callMethod = guessWhichMethodWithoutSignature(callClass, methodName);
         SkipFrame skipFrame = callMethod.getAnnotation(SkipFrame.class); 
         if (skipFrame != null)
             continue; // skip this stack trace element
         if (callerDepth-- == 0) {
             callerElement = e; 
             break;
         }
     }
     assert callerDepth == 0; 
     assert callerElement != null;
     Log4j.info(callerElement.getLineNumber()... + "message... "); 
}

@SkipFrame
void logSendMail(Mail mailObject) {
    log("Send mail " + mailObject.getSubject()); 
}

したがって、ヘルパー関数がネストされている場合、またはより多くのヘルパー関数が使用されている場合は、それらすべてにSkipFrameアノテーションをマークするだけで、必要な正しいソース行番号を取得できます。

0
Xiè Jìléi