構造化ログを使う理由
System.out.println の先を学びます。
「構造化ログを使う理由」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。
println の問題点
System.out.printlnは簡単な実験には適していますが、実際のアプリケーションにおけるロギングツールとしては不十分です。
- タイムスタンプ、レベル、スレッド名がありません。
- 本番環境で無効にする方法がありません。
- stdout にしか書き込めず、出力先を振り分けられません。
public class Main {
public static void main(String[] args) {
// Crude: no level, no timestamp, always on
System.out.println("User logged in");
}
}ロギングフレームワークが提供するもの
ロギングフレームワークを使うと、次の機能を利用できます。
- ノイズを絞り込むためのレベル(TRACE、DEBUG、INFO、WARN、ERROR)。
- タイムスタンプ、スレッド名、ロガー名の自動付加。
- ファイル、コンソール、リモートシステムへの出力先の振り分け。
- 再コンパイルせずに実行時設定を変更する機能。
public class Main {
public static void main(String[] args) {
// A framework would render: time, level, logger, thread, message
String simulated = "2026-01-01 10:00:00 INFO c.e.App [main] - User logged in";
System.out.println(simulated);
}
}ログレベルの説明
レベルは、メッセージを重要度で分類します。
- TRACE:非常に細かな情報。
- DEBUG:開発時の診断情報。
- INFO:通常の進捗や節目。
- WARN:予期しないものの、復旧可能な状態。
- ERROR:対処が必要な障害。
public class Main {
enum Level { TRACE, DEBUG, INFO, WARN, ERROR }
public static void main(String[] args) {
Level threshold = Level.INFO;
for (Level l : Level.values()) {
boolean shown = l.ordinal() >= threshold.ordinal();
System.out.println(l + " visible? " + shown);
}
}
}しきい値によるフィルタリング
ロガーにはしきい値があります。しきい値より低いレベルのメッセージは、低コストで破棄されます。
本番環境では INFO に設定して DEBUG と TRACE を無効にすれば、コードを変更せずにノイズとコストを削減できます。
public class Main {
enum Level { TRACE, DEBUG, INFO, WARN, ERROR }
static void log(Level threshold, Level level, String msg) {
if (level.ordinal() >= threshold.ordinal()) {
System.out.println(level + " - " + msg);
}
}
public static void main(String[] args) {
Level t = Level.WARN;
log(t, Level.DEBUG, "hidden detail");
log(t, Level.ERROR, "shown error");
}
}構造化ログと非構造化ログ
非構造化ログは通常の文章です。構造化ログは、機械で解析できるキーと値のコンテキスト(ユーザー ID、リクエスト ID など)を付加します。
構造化ログは、Elasticsearch や Loki などのツールで検索や集計を行えます。
import java.util.Map;
public class Main {
public static void main(String[] args) {
// Structured context as key-value pairs
Map<String, Object> fields = Map.of("event", "login", "userId", 42, "ok", true);
System.out.println(fields);
}
}ファサードという考え方
コードを特定のロギングライブラリに依存させるべきではありません。SLF4J のようなファサードを使えば、1つの API に対してコードを書き、後からバックエンド(Logback、Log4j2)を交換できます。
これにより、アプリケーションをロギング実装の選択から切り離せます。
public class Main {
public static void main(String[] args) {
// Concept: your code calls a facade interface, not a concrete logger
System.out.println("App -> SLF4J facade -> Logback backend");
}
}ブール値のフラグだけではいけない理由
if (DEBUG) System.out.println(...)のようなコードを使うこともできます。しかし、これはコードベース全体に処理を散在させ、パッケージごとの制御もできません。
ロギングフレームワークなら設定を一元化できるため、あるパッケージでは DEBUG を有効にし、それ以外では ERROR に設定できます。
public class Main {
static final boolean DEBUG = false;
public static void main(String[] args) {
// Inflexible: one global switch, scattered checks
if (DEBUG) System.out.println("diagnostic");
System.out.println("A framework controls this per logger instead");
}
}パフォーマンスに関する注意点
ロギングは無効なときに低コストであるべきです。フレームワークはフィルタリングされたレベルの処理を省略し、パラメーター化されたメッセージによって、破棄する文字列の生成を避けます。
println は、出力を誰も読まない場合でも、引数を常に評価します。
public class Main {
static String expensive() {
System.out.println("(building expensive string)");
return "big report";
}
public static void main(String[] args) {
boolean debugEnabled = false;
// Only build the message if it will actually be logged
if (debugEnabled) System.out.println(expensive());
System.out.println("skipped expensive work");
}
}出力先の一元的な振り分け
フレームワークは、1つ以上のアペンダーにログを送ります。アペンダーには、コンソール、ローテーションするファイル、syslog、ネットワークコレクターなどがあります。
出力先は一度設定すればよく、アプリケーションコードはそのまま維持できます。
public class Main {
public static void main(String[] args) {
String[] appenders = {"CONSOLE", "ROLLING_FILE", "JSON_HTTP"};
for (String a : appenders) System.out.println("route logs to: " + a);
}
}相関とコンテキスト
実際のシステムでは、リクエスト内のすべてのログ行にリクエスト IDを追加します。これにより、スレッドやサービスをまたいで、1人のユーザーの処理の流れを追跡できます。
SLF4J では、MDC(Mapped Diagnostic Context)を通じてこの機能をサポートしています。
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map<String, String> mdc = new HashMap<>();
mdc.put("requestId", "req-1234");
System.out.println("All logs in this request tagged with " + mdc);
}
}計画
このコースでは、次のことを学びます。
- SLF4J ファサードを使ってロガーを取得します。
- 適切なレベルでログを記録します。
- 効率的なパラメーター化メッセージを使います。
- アペンダーとパターンを使ってLogbackを設定します。
public class Main {
public static void main(String[] args) {
System.out.println("Next: SLF4J facade and loggers");
}
}クイックチェック
構造化ロギングについての理解度を確認します。
振り返り
構造化ロギングが重要である理由を学びました。
- printlnにはレベル、メタデータ、ルーティングがありません。
- フレームワークによってレベル、フィルタリング、アペンダーが追加されます。
- ファサードによって、コードとバックエンドを分離できます。
- 構造化コンテキストとMDCによって、相関付けが可能になります。
次は、SLF4Jファサードとロガーの作成です。
public class Main {
public static void main(String[] args) {
System.out.println("Structured logging recap complete");
}
}よくある質問
「構造化ログを使う理由」レッスンは無料ですか?
はい。「構造化ログを使う理由」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。
「構造化ログを使う理由」で何を学びますか?
System.out.println の先を学びます。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Java Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「構造化ログを使う理由」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このJava Academyレッスンでコードを書いて実行できますか?
はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- 構造化ログを使う理由
- SLF4J ファサードとロガー
- パラメータ化ロギング
- Logback の設定