0Pricing
Java Academy · レッスン

パラメータ化ロギング

効率的にメッセージを整形します。

「パラメータ化ロギング」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。

文字列連結の問題

ログメッセージを連結すると、レベルが無効であっても、呼び出しの前に完全な文字列が構築されます。

log.debug("id=" + id + " name=" + name)では、DEBUGが無効でも文字列が構築されるため、CPUを無駄に消費します。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        String name = "Ada"; int id = 7;
        // Bad: string is built even if DEBUG is disabled
        log.debug("id=" + id + " name=" + name);
    }
}

{}によるプレースホルダー

SLF4Jでは、{}をプレースホルダーとして使用します。値は追加の引数として渡します。

メッセージはレベルが有効な場合にのみ組み立てられるため、無効なログによるコストはほとんどありません。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        String name = "Ada"; int id = 7;
        // Good: no string built unless DEBUG is on
        log.debug("id={} name={}", id, name);
    }
}

複数のプレースホルダー

プレースホルダーには順番に値が入ります。最初の{}には最初の引数が、2番目には2番目の引数が入り、その後も同様です。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        log.info("order {} for user {} totaling {} cents", "A-1", 42, 1599);
    }
}

効率的な理由

フレームワークはまずレベルを確認します。無効な場合は、どの引数に対してもtoString()を呼び出さず、すぐに戻ります。

コストの高いフォーマット処理は、メッセージが実際に出力される場合にのみ遅延して実行されます。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    static String describe(Object o) { System.out.println("toString called"); return o.toString(); }
    public static void main(String[] args) {
        // Note: passing describe(...) still evaluates eagerly; pass the raw object instead
        log.trace("value={}", new int[]{1, 2, 3});
    }
}

フォーマット済み文字列ではなくそのままのオブジェクトを渡す

遅延処理の利点を得るには、フォーマットするメソッド呼び出しではなく、そのままのオブジェクトを渡します。

log.debug("{}", buildReport())と記述すると、buildReport()はすぐに実行されてしまいます。ロガー自身がフォーマットできる引数を渡してください。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        Object user = new Object();
        // Logger calls user.toString() only if DEBUG is enabled
        log.debug("current user: {}", user);
    }
}

負荷の高い処理に対する遅延サプライヤー

フォーマット処理自体に大きなコストがかかる場合は、Supplierを使うSLF4J 2.xのfluent APIを使用して、処理を遅延させます。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    static String heavyReport() { return "...expensive..."; }
    public static void main(String[] args) {
        log.atDebug()
           .setMessage("report: {}")
           .addArgument(() -> heavyReport())
           .log();
    }
}

リテラルの波括弧をエスケープする

メッセージにリテラルの{}を含める必要がある場合は、バックスラッシュでエスケープします:\\{}。

このような場面はまれですが、JSON形式に似たテキストをログに記録するときに役立ちます。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        // Logs a literal {} then the value
        log.info("empty object is \\{} and id is {}", 7);
    }
}

例外とプレースホルダーの組み合わせ

プレースホルダーと末尾の例外を組み合わせることもできます。SLF4Jは最後の引数がThrowableであることを検出し、そのスタックトレースを出力します。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        int id = 99;
        try {
            throw new RuntimeException("db down");
        } catch (Exception e) {
            log.error("failed processing record {}", id, e);
        }
    }
}

引数を増やしすぎない

プレースホルダーの数と引数の数が一致しない場合、SLF4Jは可能な限り処理しますが、出力が不自然になることがあります。

数を一致させてください。値が2、3個を超える場合は、代わりに構造化コンテキスト(MDC)の利用を検討してください。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        // Two placeholders, two arguments: correct
        log.info("user {} did {}", "ada", "login");
    }
}

MDCでコンテキストを追加する

リクエストIDのように複数の行で共有する値は、各メッセージに含めるのではなくMDCに格納します。

そうすると、パターンレイアウトによってMDCの値をすべての行に自動的に出力できます。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        MDC.put("requestId", "req-1234");
        try {
            log.info("handling request");
            log.info("request done");
        } finally {
            MDC.clear();
        }
    }
}

ベストプラクティスのまとめ

効率的なロギングのルールは次のとおりです。

  • 常に{}プレースホルダーを使用し、+による連結は決して使用しません。
  • あらかじめ構築した文字列ではなく、そのままのオブジェクトを渡します。
  • 本当に負荷の高い処理には、サプライヤーまたはisXEnabled()を使用します。
  • 横断的なコンテキストにはMDCを使用します。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        log.info("processed {} items in {} ms", 1000, 42);
    }
}

クイックチェック

パラメーター化ロギングについての理解度を確認します。

振り返り

パラメーター化ロギングについて学びました。

  • 追加の引数とともに{}プレースホルダーを使用します。
  • メッセージは遅延して、レベルが有効な場合にのみ構築されます。
  • そのままのオブジェクトを渡し、負荷の高い処理にはサプライヤーを使用します。
  • 共有するコンテキスト値にはMDCを使用します。

次は、Logbackバックエンドの設定です。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);
    public static void main(String[] args) {
        log.info("Parameterized logging recap: {}", "done");
    }
}

よくある質問

「パラメータ化ロギング」レッスンは無料ですか?

はい。「パラメータ化ロギング」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。

「パラメータ化ロギング」で何を学びますか?

効率的にメッセージを整形します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Java Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「パラメータ化ロギング」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このJava Academyレッスンでコードを書いて実行できますか?

はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 構造化ログを使う理由
  2. SLF4J ファサードとロガー
  3. パラメータ化ロギング
  4. Logback の設定
← Java Academyに戻る