47

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

log.info("hey");

 [INFO] [Foo:413] hey

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

log_info("hey");

[INFO] [LoggingSupport:123] hey

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

これは実装固有のものだと思います。私が必要としているのは Commons Logging 経由の Log4J ですが、他のオプションについても知りたいと思っています。

4

8 に答える 8

36

別の答え。

メソッドを使用して、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 は通常どおり動作し、スタック トレースが必要ない場合は計算を回避します。

于 2009-09-28T11:16:39.503 に答える
5

行番号を指定することは、Log4j から自然に得られるもの、または次のいずれかの場合、非常にコストがかかることに注意してください。そのコストを受け入れなければならない...

次の API を使用できます。

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

更新しました:

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

  • (ログ形式で) 出力しないように log4j に依頼し、
  • メッセージの先頭に行番号の明示を挿入します(log4jに送信する文字列)。

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

  • 適切な場合は、明示的なロガー(私が推測するパラメーターとして渡される)を使用します(特定のコンテキストに対して特定のロガーを定義することがあります。たとえば、どのクラスが行うかに関係なく、データベース要求を送信するためのロガーがあります。これにより、構成ファイルに加えられた変更を 1 か所に、それらを (非) アクティブにしたい場合 ...)
  • 呼び出し元のクラスに Logger を使用します。この場合、パラメーターを渡す代わりに、同様に呼び出し元のクラス名を推測できます...
于 2009-09-28T09:59:19.910 に答える
4

ロガー ヘルパーに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");
}

}
于 2013-10-07T21:49:45.173 に答える
2

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>

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

于 2009-09-28T10:40:33.413 に答える
1

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

于 2009-09-28T11:32:54.547 に答える
1

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

于 2009-09-28T10:00:18.083 に答える
0

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

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 注釈を付けるだけで、本当に必要な正しいソース行番号を取得できます。

于 2009-12-08T05:35:54.890 に答える