本ページはプロモーションが含まれています

Java入門

logback.xmlの書き方|ログレベル設定とサンプルコードで解説

CATEGORY
REV.
DRAWN BY
TOM
SHEETS
8枚
logback.xml(ログレベル設定の書き方)

トム

SESで約8年、環境を変えられないもどかしさの中にいました。32歳で自社開発企業に転職し、今はClaude Codeと自動化で、仕事も時間も組み直している途中です。
・Javaバックエンドエンジニア(Java歴12年以上)
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎
詳細なプロフィール

𝕏

logback.xmlは、Javaのログ出力ライブラリ「Logback」の設定ファイルです。src/main/resourcesに置き、<appender>(出力先)・<logger>(パッケージごとのレベル)・<root>(全体のレベル)の3要素を書けば動きます。

Spring Bootでは、同じ書き方のままファイル名をlogback-spring.xmlにすると、<springProfile>で開発・本番のログレベルを切り替えられます。

この記事の設定例は、Logback 1.6.3・SLF4J 2.0.18・Java 21で動かして確認しています(2026年9月)。

「とりあえずSystem.out.printlnでデバッグしているけど、これで本当にいいのかな…?」と感じている人も多いはずです。

私も新人時代は、ログの重要性を理解せず、print文をコードに大量に埋め込んでデバッグしていました。

その結果、本番環境で原因不明のエラーが発生した際、情報が何もなくて原因の特定に何日もかかった苦い経験があります。

その失敗から一念発起し、ログについて徹底的に学びました。今では、適切なログ設計のおかげで、障害発生時の原因調査を以前よりずっと短い時間で終えられるようになりました。

結論から言うと、Javaで実務レベルのログを出したいなら「SLF4J + Logback」の組み合わせが第一候補です。

この記事を読めば、(1) print文よりログ出力ライブラリを使うべき理由、(2) logback.xmlのコピペで使える具体的な書き方とSpring Bootのlogback-spring.xmlとの違い、(3) TRACE〜ERRORのログレベルの正しい使い分け、(4) 個人情報漏洩などプロが避けている落とし穴、の4点がわかります。

今日から実務でそのまま使える形でまとめました。

この記事でわかること

  • print文よりログ出力ライブラリを使うべき理由
  • logback.xmlのコピペで使える具体的な書き方
  • Spring Bootで使うlogback-spring.xmlとの違い
  • TRACE〜ERRORのログレベルの正しい使い分け
  • MDC・AsyncAppenderを使った実務レベルの応用機能
  • 個人情報漏洩などプロが避けている落とし穴

PARTS LIST /目次

Javaでログ出力はなぜ重要なのか

ログ出力は、障害調査の迅速化・システム監視・セキュリティ監査という3つの役割を同時に担う、開発に必要な仕組みです。

システム開発において、ログ出力は地味ながらも重要な役割を担っています。まずは、なぜログ出力が必要なのか、その理由から見ていきましょう。

print文との違いとは?

開発中に変数の値を確認したいとき、手軽なSystem.out.println()を使いがちです。しかし、System.out.println()は本格的な開発には向いていません。ログ出力ライブラリと比較すると、主に以下の違いがあります。

機能System.out.println()ログ出力ライブラリ
出力のON/OFFコード修正が必要設定ファイルで変更可能
出力レベルなし5段階以上で制御可能
出力先コンソールのみファイル、DB、ネットワークなど自由
フォーマット毎回指定が必要統一した形式で出力できる
パフォーマンス低速になる場合がある高速化の工夫がされている

print文は、あくまで一時的なデバッグ用です。本番運用まで見据えたシステムでは、ログ出力ライブラリの利用が必須です。

ログを残すことで得られる3つのメリット

適切にログを残すことで、私たちは大きな恩恵を受けられます。代表的なメリットは次の3つです。

  1. 問題発生時の迅速な原因特定。これが最大のメリットです。エラーが発生した箇所や、その直前のシステムの動きがログに記録されていれば、原因を素早く特定し修正にとりかかれます。情報がなければ手探りで調査することになり、解決までに多大な時間を要します。
  2. システムの動作状況の監視。システムの正常な動作を記録することで、「いつ、どの処理が実行されたか」を把握できます。パフォーマンスのボトルネックを発見したり、ユーザーの利用状況を分析したりするなど、システムの改善に役立つ貴重なデータとなります。
  3. セキュリティ監査の証跡。「誰が、いつ、何をしたか」を記録する監査ログは、不正アクセスや情報漏洩が発生した際の追跡に欠かせません。セキュリティを担保する上で、ログは重要な証拠(エビデンス)の役割を果たします。

システム開発でログが果たす役割

ログは、開発のフェーズから運用・保守のフェーズまで、長期にわたってシステムを支える土台です。

開発中はデバッグ情報として、リリース後はシステムの健康状態を監視するカルテとして機能します。

しっかりとしたログ出力の仕組みは、いわばシステムの「ドライブレコーダー」のようなものです。

万が一の事故が起きたときに、何が起こったのかを正確に記録してくれている、頼れる存在なのです。

Javaで使われる代表的なログ出力方法

結論として、これから使うなら「SLF4J(窓口)+ Logback(実装)」の組み合わせが最有力です。

Javaには、ログ出力のためのライブラリがいくつか存在します。ここでは、代表的なものをピックアップし、それぞれの特徴を解説します。

System.out.println() の基本と問題点

前述のとおり、System.out.println()はJavaで最も手軽な標準出力の方法です。

public class PrintExample {
    public static void main(String[] args) {
        String name = "Taro";
        int age = 30;
        System.out.println("名前: " + name + ", 年齢: " + age);
    }
}

この方法は簡単に使えますが、本番コードに残してしまうと、大量の不要なログが出力され続けてパフォーマンスが低下したり、サーバーのディスク容量を圧迫したりする原因になります。

java.util.logging(JUL)の特徴

java.util.loggingは、Javaの標準ライブラリに含まれているログ機能で、通称JUL(Java Util Logging)と呼ばれます。

標準機能なので、追加でライブラリを導入する必要がないのが最大のメリットです。

しかし設定が少し分かりにくく、他の高機能なライブラリと比較するとパフォーマンス面で見劣りします。

そのため、現在では積極的に採用されることは少なくなっています。

Log4j と SLF4J の違い

Javaのログ出力ライブラリを語る上で欠かせないのがLog4jとSLF4Jです。この2つの違いを理解することが、モダンなログ実装への第一歩です。

違い

  • Log4j: ログを実際に出力する「実装」ライブラリです。多機能で人気がありましたが、2021年にLog4j 2系で深刻な脆弱性「Log4Shell」(CVE-2021-44228)が発見され、世界中のシステムで緊急対応が必要になった経緯があります(2.17.1以降で修正済み)。
  • SLF4J (Simple Logging Facade for Java): ログ出力の「インターフェース(窓口)」を提供するライブラリです。「Facade」は「建物の正面」という意味で、ログライブラリの窓口として振る舞います。

プログラムコードではSLF4Jの作法に沿ってログ出力を記述します。

そして、実際にログを動かすライブラリとしてLog4jや後述するLogbackなどを裏側で組み合わせるのです。

この仕組みには大きなメリットがあります。

将来的に「Logbackから別のライブラリに乗り換えたい」となった場合でも、プログラムコードを修正することなく、設定の変更だけで対応できるのです。

Logbackとは?人気が高い理由

Logbackとは、Javaプログラムの実行状況をコンソールやファイルに記録するためのログ出力ライブラリである。

2026年9月現在も、Javaのログ出力ライブラリとして広く使われているのがLogbackです(本記事は1.6系で確認)。

1.6系は実行にJava 11以上が必要です。旧1.5系(最終版1.5.38)は公式に「レガシー」扱いとなっており、公式は後継で置き換えがきく1.6系への移行を案内しています。1.6系は1.5系の置き換えとしてそのまま使えますが、Janinoを使ったJava式による条件分岐(<if condition="...">)のサポートは削除されているため、該当する設定がある場合だけ書き換えが必要です(出典: Logback公式ニュース)。

Logbackは、Log4jの開発者Ceki Gülcü氏が、Log4j 1.xの後継を目指して設計を刷新した別プロジェクトとして開発したライブラリで、主に以下の理由から高い支持を得ています。

理由

  • 高速なパフォーマンス: Log4j 1.xの経験をもとに内部が書き直されており、公式サイトでは一部の処理経路で約10倍高速になったと説明されています。
  • SLF4Jとの親和性: SLF4Jと同じ開発者によって作られたため、ネイティブでSLF4Jを実装しており、スムーズに連携できます。
  • 設定の柔軟性: 設定ファイル(logback.xml)を修正した際に、<configuration scan="true">と指定しておけば、アプリケーションを再起動しなくても自動で変更を検知して反映してくれる機能があります。
  • 豊富な機能: ログの圧縮や、条件によるフィルタリングなど、Log4jの優れた機能を引き継ぎつつ、さらに多くの便利な機能を提供しています。

これからJavaでログ出力を始めるなら、「SLF4J + Logback」の組み合わせが最もおすすめの選択肢です。

Logbackの基本設定|logback.xmlの置き場所と構造

Logbackの設定はlogback.xmlという1ファイルで完結します。

logback.xmlの書き方|コンソール出力の基本設定

まずは、コンソールにログを出力する最も基本的な設定例です。

このファイルをクラスパスの通った場所(例: src/main/resources)に配置します。

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>

</configuration>

この設定では、「全てのLogger」(root)に対して、「debugレベル以上」のログを、「STDOUT」という名前で定義した設定(コンソール出力)で出力するように指定しています。

コードでlogger.info()を呼んでから実際に出力されるまでの流れを図にすると、次のようになります。

logger.info() を呼んでから出力されるまでの流れ
1
CODE / アプリのコードlogger.info(...) を呼ぶ
2
LOGGERLogger が受け取るクラス・パッケージ単位
<logger> / <root> の level設定レベル以上か?
設定レベル未満出力しないここで捨てられる
設定レベル以上Appender へ渡す下の 3〜5 へ進む
3
<appender> / どこへ出すかAppenderSTDOUT / FILE
4
<encoder> / どんな形式で出すかEncoder + Layoutpattern で1行に整形
5
OUTPUTコンソール / ログファイルへ出力

<logger>/<root>で「どのレベルから出すか」を決め、<appender>で「どこへ出すか」を、その中の<encoder>(<pattern>)で「どんな形式で出すか」を決める、という役割分担です。

実務でありがちなのが、logback.xmlをsrc/main/resourcesではなくsrc/main/java側に置いてしまい、設定が反映されないまま気づかずデフォルト設定で動いてしまうケースです。

設定を変えたのに反映されないときは、ビルド後のtarget/classes(Mavenの場合)直下にlogback.xmlがコピーされているかを確認すると、原因の切り分けが早くなります。

設定ファイルが読まれていないことに気づきにくいのは、既定の出力が本記事の基本設定とよく似ているためです。同じプログラムを、logback.xmlなしと本記事の基本設定ありで動かすと次のようになりました(Logback 1.6.3・SLF4J 2.0.18・Java 21で確認)。

# logback.xml が読まれていない(既定の設定)
13:53:59.294 [main] DEBUG com.example.Main -- DEBUGのログ
13:53:59.295 [main] INFO com.example.Main -- INFOのログ

# 本記事の基本設定(logback.xml)が読まれている
13:53:59.218 [main] DEBUG com.example.Main - DEBUGのログ
13:53:59.218 [main] INFO  com.example.Main - INFOのログ

違いは区切りが「-」か「--」かと、INFOの後ろの空白だけです。見た目で判断せず、起動時に-Dlogback.debug=trueを付けて確かめるのが確実です。設定ファイルが見つかれば「Found resource [logback.xml]」と表示され、見つからないときは何も表示されません。

ログレベル(TRACE / DEBUG / INFO / WARN / ERROR)はどう使い分ける?

Logbackでは、ログの重要度に応じてレベルを使い分けることが重要です。レベルが低い順に並べると以下のようになります。

レベル内容用途の例
TRACE最も詳細な情報メソッドの入り口と出口など、処理の流れを追跡したいとき
DEBUG開発時のデバッグ情報変数の値やSQLの実行クエリなど、開発中にのみ見たい情報
INFOシステムの正常な動作処理の開始・終了、重要なイベントの発生など、通常運用で確認したい情報
WARN警告(すぐに問題にはならない)非推奨のAPIが使われた、設定ファイルが見つからないがデフォルト値で続行するなど
ERROR予期せぬエラー例外が発生した、処理が異常終了したなど、対応が必要な問題

設定ファイルで指定したレベル(例: level="info")以上のログのみが出力されます。

つまり、本番環境ではINFOに設定しておき、開発中だけDEBUGに切り替える、といった運用が可能です。

ログレベル設定で出力が変わる(info と debug)
レベル(低→高)
level="info"
本番の例
level="debug"
開発中の例
TRACElogger.trace(...)
出力しない
出力しない
DEBUGlogger.debug(...)
出力しない
出力する
INFOlogger.info(...)
出力する
出力する
WARNlogger.warn(...)
出力する
出力する
ERRORlogger.error(...)
出力する
出力する
NOTE上から下へレベルが高くなります。指定したレベル以上だけが出力され、debug に切り替えると DEBUG も出力されます(TRACE はどちらでも出力されません)。

コンソール出力とファイル出力の切り替え方

ログをファイルに保存するには、FileAppenderを使います。コンソール出力に加えて、ファイルにも出力する設定例は以下のようになります。

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <file>my-app.log</file>
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="info">
    <appender-ref ref="STDOUT" />
    <appender-ref ref="FILE" />
  </root>

</configuration>

<root>タグの中に<appender-ref ref="FILE" />を追加するだけで、コンソールとファイルの両方に出力できるようになります。

ログファイルを自動で分割するローテーション設定

先ほどのFileAppenderのままだと、my-app.logというファイルが際限なく肥大化し続け、いずれディスク容量を圧迫します。

本番運用では、日付やサイズを基準にログファイルを自動で分割するRollingFileAppenderを使うのが定石です。

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  <file>logs/my-app.log</file>
  <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    <fileNamePattern>logs/my-app.%d{yyyy-MM-dd}.log</fileNamePattern>
    <maxHistory>30</maxHistory>
  </rollingPolicy>
  <encoder>
    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
  </encoder>
</appender>

TimeBasedRollingPolicyで日付ごとにファイルを切り替え、maxHistoryで保持する世代数(この例では30日分)を指定しています。

古いログは自動で削除されるため、手動でのファイル整理は不要になります。

Spring Bootではlogback-spring.xmlを使う

Spring Bootではspring-boot-starterにLogbackが最初から含まれているため、依存関係の追加は不要です。設定ファイルはlogback.xmlではなくlogback-spring.xmlという名前でsrc/main/resourcesに置くのが推奨されています。

理由は、logback.xmlはSpring Bootの初期化より前に読み込まれてしまい、<springProfile>(プロファイルごとの切り替え)や<springProperty>(application.propertiesの値の参照)といったSpring Boot独自の拡張タグが使えないためです(出典: Spring Boot公式リファレンス「Logging」)。

書き方自体はlogback.xmlと同じで、次のように環境ごとのログレベルを1ファイルで切り替えられます。

<configuration>
  <!-- Spring Boot標準のコンソール・ファイル出力設定を読み込む -->
  <include resource="org/springframework/boot/logging/logback/base.xml" />

  <!-- 開発環境(devプロファイル)では自作パッケージをDEBUGまで出す -->
  <springProfile name="dev">
    <logger name="com.example" level="DEBUG" />
  </springProfile>

  <!-- 本番環境(prodプロファイル)ではWARN以上に絞る -->
  <springProfile name="prod">
    <logger name="com.example" level="WARN" />
  </springProfile>
</configuration>

起動時に--spring.profiles.active=devのようにプロファイルを指定すると、該当する<springProfile>の中身だけが有効になります。

なお、ログレベルを変えるだけならapplication.propertiesにlogging.level.com.example=DEBUGと書く方法でも足ります。出力先やフォーマットまで細かく制御したいときにlogback-spring.xmlを用意する、と使い分けるとよいでしょう。

実践!ログ出力のサンプルコード

設定ファイルの用意ができたら、あとはコードからloggerを呼び出すだけです。

本記事のサンプルコードはLogback 1.6.3(依存するSLF4Jは2.0.18)で確認しています。新しい版を使う場合は、Maven Centralで版を確かめて、下の<version>を置き換えてください。

pom.xmlには以下の依存関係を追加してください。

<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.6.3</version>
</dependency>

簡単なログ出力プログラム例

SLF4JとLogbackを使った基本的なプログラムです。

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

public class SimpleLogExample {

    // Loggerインスタンスを取得
    private static final Logger logger = LoggerFactory.getLogger(SimpleLogExample.class);

    public static void main(String[] args) {
        logger.info("プログラムを開始します。");
        logger.debug("これはデバッグメッセージです。 name={}", "Taro");

        try {
            int result = 10 / 0;
        } catch (Exception e) {
            logger.error("計算中にエラーが発生しました。", e);
        }

        logger.info("プログラムを終了します。");
    }
}

このコードでは、LoggerFactory.getLogger()でLoggerインスタンスを取得しています。

メッセージ内に {} を使うと、第2引数以降の値で置換してくれるため、文字列連結よりも効率的です。

パッケージ構成に合わせたLogger設定方法

getLogger()の引数にクラスを渡す(SimpleLogExample.class)のが一般的です。

これにより、logback.xmlでパッケージごとにログレベルを細かく制御できます。

例えば、「com.example.serviceパッケージ配下だけ、もっと詳細なdebugレベルのログを見たい」という場合は、以下のように設定を追加します。

  <logger name="com.example.service" level="debug" />

  <root level="info">
    <appender-ref ref="STDOUT" />
  </root>

このように設定すると、他のパッケージはinfoレベルのまま、com.example.serviceパッケージ内のクラスから出力されるログだけがdebugレベルまで出力されるようになります。

例外発生時のログの書き方

例外が発生した際は、必ずスタックトレースも一緒に出力するように心がけてください。

スタックトレースは、エラーが発生するまでのメソッド呼び出し履歴であり、原因究明のための最も重要な手がかりです。

悪い例:
// メッセージしか出力されず、どこでエラーが起きたか分からない
logger.error("エラーが発生しました: " + e.getMessage());
良い例:
// 第2引数にExceptionオブジェクトを渡すことで、スタックトレースも出力される
logger.error("エラーが発生しました。", e);

この小さな違いが、障害対応のスピードを大きく左右します。

あわせて読む
あわせて読む

実務で差がつくLogbackの応用機能

フィルタリング・MDC・非同期化の3つを使いこなせば、Logbackを本番運用レベルまで引き上げられます。

フィルター機能で出力するログを絞り込む

ログレベルをroot単位で設定するだけでは、「このパッケージだけ詳しく見たいのに、他のログに埋もれて探せない」という状況になりがちです。

ThresholdFilterを使うと、アペンダーごとに出力するログの下限レベルをさらに絞り込めます。

<appender name="FILE" class="ch.qos.logback.core.FileAppender">
  <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
    <level>WARN</level>
  </filter>
  <file>logs/warn-only.log</file>
  <encoder>
    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
  </encoder>
</appender>

この設定では、WARN以上のログだけを別ファイルwarn-only.logに出力します。

通常のログファイルとは別に、警告・エラーだけを集めたファイルを用意しておくと、障害時の一次確認がぐっと速くなります。

MDCでリクエスト単位のログを追跡する

1つのアプリケーションが複数のリクエストを同時に処理していると、ログファイルの中で異なるリクエストの出力が入り混じることがあります。

すると「このエラーがどのリクエストで起きたのか」が分からなくなってしまいます。

この問題を解決するのがMDC(Mapped Diagnostic Context)です。

MDCとは、スレッドローカルな連想配列(Map)にリクエストIDなどのコンテキスト情報を保存し、そのスレッドで出力される全てのログに自動で埋め込む仕組みです。

使い方はシンプルで、リクエスト処理の開始時にMDC.put()で値をセットし、logback.xmlの<pattern>に%X{キー名}を追加するだけです。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import java.util.UUID;

public class RequestHandler {

    private static final Logger logger = LoggerFactory.getLogger(RequestHandler.class);

    public void handle() {
        // リクエスト開始時にIDをセット
        MDC.put("requestId", UUID.randomUUID().toString());
        try {
            logger.info("リクエスト処理を開始します。");
            // ここで実際の処理を行う
        } finally {
            // 必ずクリアする(スレッドプール環境での値の残留を防ぐ)
            MDC.clear();
        }
    }
}

logback.xmlのパターン設定に%X{requestId}を追加すれば、以降のログすべてにリクエストIDが自動で付与されます。

<pattern>%d{HH:mm:ss.SSS} [%X{requestId}] [%thread] %-5level %logger{36} - %msg%n</pattern>

Webアプリケーションであれば、Servlet FilterやSpringのInterceptorでリクエストの開始・終了フックにこのputとclearを仕込んでおくのが定石です。

こうしておけば、障害調査の際に「このリクエストIDのログだけ」をgrepで抽出できます。

複数リクエストが同時に流れる本番環境でも、原因追跡が格段に楽になります。

MDC.clear()を忘れると、スレッドプールで使い回された別リクエストのログに前のリクエストIDが混入するため、必ずfinally句で呼び出してください。

注意点として、MDCはスレッドごとに独立した領域のため、ExecutorServiceやCompletableFutureで別スレッドに処理を委譲すると、そのスレッド内のログにはMDCの値が引き継がれません。

非同期処理をまたいでリクエストIDを追跡したい場合は、タスク投入時にMDC.getCopyOfContextMap()で現在のMDCをコピーし、別スレッド側でMDC.setContextMap()により復元する必要があります。

なお、MDCの値をそのままファイル名やパスの一部として使うSiftingAppender等の構成では、外部入力に由来する値をサニタイズせずにMDCへ格納すると意図しないパスにログファイルが作成されるリスクがある(CVE-2026-19880。1.6.3の修正は不十分で、近縁のCVE-2026-104721を含めてLogback 1.6.5で修正済み)。

1.6.5以降を利用し、MDCに格納する値は信頼できる形式に限定してください。

AsyncAppenderで非同期ロギングを行う

アクセス数が多いアプリケーションでは、ログの書き込み(ディスクI/O)自体がアプリケーションのレスポンスを遅らせる原因になることがあります。

この影響を軽減するのがAsyncAppenderです。ログイベントを一度内部キューに格納し、別のワーカースレッドが実際のファイル書き込みを担当します。

これにより、アプリケーション本体のスレッドがI/O待ちでブロックされるのを防げます。

設定は、既存のアペンダーをAsyncAppenderでラップするだけです。

<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
  <appender-ref ref="FILE" />
</appender>

<root level="info">
  <appender-ref ref="ASYNC" />
</root>

注意
ログイベントはキューに溜まってから書き込まれるため、アプリケーションが異常終了(強制停止やクラッシュ)した場合、キューに残っていた未書き込みのログが失われる可能性があります。

障害調査で一番見たい直前のログが消えてしまっては本末転倒なので、採用する際は「パフォーマンスと引き換えにログ消失のリスクを受け入れる」というトレードオフを理解した上で使いましょう。

もう1つ見落としがちな挙動があります。

AsyncAppenderのキューサイズはデフォルトで256件、かつキューの使用率が80%を超えるとdiscardingThresholdの設定によりTRACE・DEBUG・INFOレベルのログが自動的に間引かれます(ERRORとWARNは間引かれません)。

「アクセスが集中した時間帯だけINFOログが一部欠けている」という現象に遭遇したら、まずこの仕様を疑ってください。

全件保持したい場合は<discardingThreshold>0</discardingThreshold>を明示的に指定します。

ログ出力でよくあるミスとベストプラクティス

ログレベルの誤用と個人情報の出力が、実務で最も起きやすい2大ミスです。

ログレベルをINFOで全部出すのはなぜダメ?

初心者にありがちなのが、とりあえず全てのログをINFOレベルで出力してしまうことです。これをやってしまうと、本番環境でシステムの動作ログが大量に出力され、本当に重要な警告(WARN)やエラー(ERROR)のログが埋もれてしまいます。

「これは正常動作の情報か?」「開発中にだけ見たい情報か?」を常に意識し、ログレベルを適切に使い分けることが、読みやすいログへの第一歩です。

特に実務で起きがちなのが、開発環境用の設定ファイルをそのまま本番環境にデプロイしてしまい、DEBUGレベルのログが延々と出力され続けてディスクを圧迫するケースです。

環境ごとにlogback-spring.xmlの<springProfile>タグでログレベルを切り替えるなど、設定ファイル自体を環境依存にしておく対策が有効です。

個人情報を出力してしまうリスク

ログには、ユーザーのパスワード、クレジットカード番号、マイナンバーといった機密情報や個人情報を絶対に出力してはいけません。

万が一ログファイルが流出した際に、重大な情報漏洩インシデントにつながります。

ユーザー情報を扱うオブジェクトをそのままログに出力するような実装は避け、必要な情報だけを抜き出すか、マスキング処理を施すなどの配慮が必要です。

マスキング処理の一例として、メールアドレスの一部を伏せ字にする実装を紹介します。

String email = "[email protected]";
String masked = email.replaceAll("(?<=.{3}).(?=[^@]*@)", "*");
logger.info("ユーザー登録: {}", masked); // tar********@example.com

個人情報を扱うログ出力は「マスキング関数を通してからでないとloggerに渡せない」というルールをコードレビューの観点に組み込んでおくと、うっかりミスを防げます。

運用で読みやすいログフォーマットの工夫

ログは、人間が読むだけでなく、ツールで集計・分析することも多いです。そのため、一貫性のあるフォーマットで出力することが重要になります。

logback.xmlの<pattern>を工夫して、最低でも以下の情報を含めることをお勧めします。

  • タイムスタンプ: %d{yyyy-MM-dd HH:mm:ss.SSS}
  • ログレベル: %-5level
  • スレッド名: [%thread]
  • Logger名(クラス名): %logger{36}
  • メッセージ: %msg%n

2026年現在では、ログをJSON形式で出力し、KibanaやSplunkといったツールで可視化・分析する手法も広く使われています。

よくある質問

Q1. logback.xmlはどこに置けばいいですか?

A1. クラスパスの通った場所、具体的にはsrc/main/resources直下に置きます。src/main/java側に置くと設定が反映されないため注意してください。

Q2. LogbackとLog4jの違いは何ですか?

A2. どちらもログを出力する「実装」ライブラリですが、LogbackはLog4jの開発者が設計を刷新した別プロジェクトで、SLF4Jとの親和性や設定の自動リロードなど機能面で優位です。2026年時点でもLogbackは広く使われている定番です。

Q3. ログレベルはどう使い分ければいいですか?

A3. 開発中の詳細確認にはDEBUG、通常運用の記録にはINFO、対応不要な警告にはWARN、対応が必要なエラーにはERRORを使います。本番環境はINFO以上、開発中はDEBUG以上に設定するのが基本です。

Q4. SLF4JとLogbackは何が違いますか?

A4. SLF4Jはログ出力の「窓口(インターフェース)」、Logbackはその窓口を実際に動かす「実装」です。コードはSLF4Jの作法で書き、裏側の実装をLogbackに差し替える構成が一般的です。

Q5. ログファイルが肥大化するのを防ぐにはどうすればいいですか?

A5. FileAppenderではなくRollingFileAppenderとTimeBasedRollingPolicyを使い、日付単位でファイルを分割してmaxHistoryで保持期間を指定します。古いログは自動削除されます。

Q6. ログに個人情報を出力してしまった場合はどうすればいいですか?

A6. 該当のログファイルを速やかに削除・非公開化し、影響範囲を確認したうえで再発防止のためにマスキング処理を実装します。事前の対策として、ログ出力前にマスキング関数を通すルールをコードレビューに組み込むことが有効です。

まとめ:ログ出力を制する者が開発を制す

Javaのログ出力は「SLF4J + Logback」が第一候補であり、ログレベルの使い分けとMDC・AsyncAppenderの活用が実務レベルの分かれ目です。

ログ設計を最初に考える重要性

良いシステム開発は、良いログ設計から始まります。

後から付け足すのではなく、開発の初期段階で「どのような情報を」「どのレベルで」「どこに出力するか」を決めておくことが重要です。

そうすることで、開発効率とシステムの保守性は格段に向上します。

「とりあえず動けばいい」から一歩進んで、「運用しやすいコード」を書く意識を持つことが、エンジニアとしての成長につながります。

おすすめのライブラリと運用Tips

余談ですが、私がSES時代に経験した障害対応の多くは、AIどころかgrepとエディタだけでログを目で追う作業でした。

当時は「半日かかる調査」が普通でしたが、今はログさえきちんと設計されていれば、原因の当たりを付ける作業をAIに手伝わせることもできます。

裏を返せば、ログの質が低いと、AIを使っても「何もヒントが得られない」まま調査が長引くということです。

ログ設計への投資は、人力調査の時代よりもむしろ今の方がリターンが大きいと私は考えています。

結論として、これからJavaでログを扱うなら「SLF4J + Logback」の組み合わせを選んでおけば間違いありません。

そして、ただログを出すだけでなく、

  • 定期的にログの量や内容を見直す
  • ERRORレベルのログが出力されたら通知する仕組みを作る

といった運用面の工夫を取り入れることで、より堅牢なシステムを構築できます。

(本記事は2026年9月時点の情報をもとに、2026年9月25日に内容を見直しています。)

REVISIONS / 改訂履歴
REV.DATE内容
62026-10-15図解を描き直し(図面のデザインにそろえた)
52026-10-14雰囲気だけの挿絵を外した
42026-10-04記載内容の事実確認と修正
32026-08-262026年8月時点の情報で全体を見直し(古い記述を7件更新)
… ほか1回の改訂
12025-10-07初版
NEXT READ本棚から1本

AIで開発を組み直すなら、まずこの5冊から

入門・実践・設計まで、Claude Codeの本をレベル別に5冊選んで比べました。

→ おすすめ5冊を見る

  • この記事を書いた人
  • 最新記事

トム

SESで約8年、環境を変えられないもどかしさの中にいました。32歳で自社開発企業に転職し、今はClaude Codeと自動化で、仕事も時間も組み直している途中です。
・Javaバックエンドエンジニア(Java歴12年以上)
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎
詳細なプロフィール

𝕏

Java入門