再デプロイなしで原因に迫る。IntelliJ IDEAのLogpointsを実務で使いこなす

Java / Spring

printlnとブレークポイントの間にある、実務的な選択肢

障害調査で最初に思い浮かぶ手段は、println によるログ出力か、デバッガーのブレークポイントでしょう。しかし、実行中の処理を止められない非同期処理や、タイムアウトが厳しい通信処理では、どちらも扱いにくい場面があります。

そこで有力な選択肢になるのが、IntelliJ IDEAの Logpoints です。Logpointsは、ブレークポイントのようにソース上へ設定しながら、プログラムを停止させずに式の評価結果をコンソールへ出力できます。さらに、アプリケーションを再ビルド・再デプロイせず、実行中のJavaプロセスへ診断情報を追加できる点が特徴です。

IntelliJ IDEA 2026.2では、実行可能な行の間にあるガターをクリックして、より短い手順でLogpointを設定できるようになりました。リモートデバッグやコンテナ上のJavaアプリケーションを調査する機会が多いエンジニアにとって、習得する価値の高い機能です。

事例:gRPCサーバーが割引額を誤って返す

ニュースで紹介されているサンプルでは、gRPCで接続するクライアントとサーバーを使い、特定のテナントに対して誤った割引額が返される問題を調査します。

期待される結果は20%の割引ですが、実際には割引が適用されず、価格がそのまま返されています。

tenant='JetBrains' region='EMEA' status=OK symbol=IDEA price=100.00 USD source=live detail=region=emea, discount_bps=0

本来は、次のような結果になる想定です。

tenant='JetBrains' region='EMEA' status=OK symbol=IDEA price=80.00 USD source=live detail=region=emea, discount_bps=2000

サーバーはDockerコンテナとして起動され、デバッグポートを公開しています。IntelliJ IDEAからアタッチすれば、ローカルプロセスだけでなく、別環境で動作するJavaプロセスも同じように調査できます。

重要なのは、デバッガーから見ると、プロセスがローカルにあるか、コンテナ内にあるか、リモートホスト上にあるかは本質的な違いではない点です。いずれもソケット経由でデバッガー接続を確立します。

Logpointsで処理の流れを止めずに観測する

まず、リクエスト処理メソッドの入り口にLogpointを設定し、テナント名やリージョンを出力します。

EMEA JetBrains

この状態でリクエストを発生させながら、呼び出し先へLogpointを追加していきます。既存のLogpointを変更したり、調査が終わった箇所を削除したりする操作も、コードを書き換えずに行えます。

最終的に割引額を計算する discountBpsFor() に到達すると、次の事実が分かります。

  • 入力されたテナント名は JetBrains
  • 処理が想定する値は jetbrains
  • 割引を返す条件分岐に入っていない

原因は、テナント名の正規化が不十分だったことです。例えば、比較時に大文字・小文字を区別しないよう修正すれば、期待する割引率を返せます。

private static int discountBpsFor(String tenant) {
    if ("jetbrains".equalsIgnoreCase(tenant)) {
        return 2_000;
    }
    return 0;
}

このようにLogpointsは、値を少しずつ観測しながら原因箇所を絞り込む用途に向いています。ログを大量に追加してから再デプロイするのではなく、必要な場所に必要な情報だけを段階的に追加できるためです。

通常のブレークポイントでは調査しにくい理由

今回の例で通常のブレークポイントを使うと、リクエスト処理が停止します。gRPCクライアント側には期限が設定されているため、デバッガーで停止している間に期限切れとなり、サーバー側の処理がキャンセルされる可能性があります。

その結果、調べたい正常な処理経路ではなく、キャンセル処理やタイムアウト処理へ進んでしまいます。デバッガーで値を確認するために停止したことが、問題の再現条件そのものを変えてしまうわけです。

一方、Logpointsはサーバーを停止させません。リクエストを通常どおり流しながら、ブレークポイントで確認するような値をコンソールで収集できます。

手段 プログラムの停止 再ビルド・再デプロイ 非同期・タイムアウト処理との相性
println しない 必要 状況による
通常のブレークポイント する 不要 条件によっては悪い
Logpoint しない 不要 比較的よい

ただし、Logpointsも無条件に無料ではありません。式は対象プロセス上で評価されるため、重い計算や大量の文字列生成をホットパスに設定すると、処理時間や負荷に影響します。IntelliJ IDEA 2026.2ではデバッガーによるオーバーヘッドを抑える仕組みが導入されていますが、ログに指定した式自体のコストには注意が必要です。

println よりLogpointsを選ぶ実務上の理由

println は手軽ですが、調査のたびにソースコードを変更し、ビルドして、対象環境へ反映しなければなりません。調査後にコードを戻し忘れたり、不要な出力を本番へ持ち込んだりするリスクもあります。

Logpointsには、次のような利点があります。

  • 調査用のコードをアプリケーション本体へ残さずに済む
  • 実行中のプロセスへ診断情報を追加できる
  • 出力する式や条件をその場で変更できる
  • 頻繁に発生するイベントをサンプリングする設定が可能
  • アプリケーションが直接呼び出す依存ライブラリ内部にも設定できる
  • コンテナや共有環境で、ログ追加のためだけに再デプロイせずに済む

特に依存ライブラリ内部を調べられる点は、通信フレームワークやORM、メッセージング基盤の調査で有効です。アプリケーション側のコードだけを見ていても原因が分からない場合、ライブラリが受け取った値や内部で分岐した結果を直接観測できます。

コンソール上の出力をクリックして、対応するLogpointやコードへ移動できる点も便利です。IntelliJ IDEAのデバッガー上でプロセスを実行していれば、既存の println 出力についても関連箇所へ移動できる場合があります。

Logpointsで実行時の挙動を一時的に変える

Logpointsは本来、値を記録するための機能です。しかし、評価式に副作用を持つ処理を指定すれば、実行中の値を一時的に変更することもできます。

gRPCの例では、リクエストに含まれるタイムアウト値を読み取るライブラリ内部の処理にLogpointを設定し、デバッグ対象のリクエストだけ期限を延長します。これにより、ブレークポイントで処理を停止しても、タイムアウトによってキャンセルされる可能性を下げられます。

例えば、デバッグ用ヘッダーを持つリクエストだけを対象にする処理は、次のような形になります。

Metadata.Key<String> DEBUG_HEADER =
    Metadata.Key.of("Debug", Metadata.ASCII_STRING_MARSHALLER);
String debugHeader = headers.get(DEBUG_HEADER);
if ("Debug".equals(debugHeader)) {
    timeoutNanos = java.util.concurrent.TimeUnit.MINUTES.toNanos(5L);
    return "Timeout reset";
}

この手法は、共有ステージング環境のように、すべてのリクエストへ影響を与えたくない場合に有効です。デバッグ用の条件を設けることで、通常のトラフィックと調査対象を分離できます。

ただし、実行中の状態を書き換える操作は、通常のログ出力よりも慎重に扱う必要があります。

  • 対象リクエストを限定する
  • 変更内容と適用時間を記録する
  • 調査終了後にLogpointを確実に解除する
  • 本番環境では権限や監査のルールに従う
  • データ破壊やセキュリティ制御の回避につながる変更を行わない

Logpointsは強力な診断手段ですが、運用環境で使う場合は「一時的な観測」と「実行時変更」を明確に区別することが重要です。

AIエージェントとの組み合わせ

ライブラリ内部へLogpointを設定するには、対象メソッドの場所やデータの流れを理解する必要があります。gRPCの内部実装に詳しくない場合、そこが調査の壁になります。

紹介されている ij-debugger AI agent skillは、デバッガーを使った調査を支援します。エージェントが呼び出し経路を追跡し、タイムアウト値が読み取られる箇所を探し、対象リクエストだけに適用するLogpoint式を提案するという流れです。

AIエージェントを活用する場合も、提案された式をそのまま適用するのではなく、次の観点でレビューすると安全です。

  • どのクラスとメソッドに設定されるか
  • どのリクエストが条件に一致するか
  • 式の評価に副作用があるか
  • 高頻度処理で負荷が増えないか
  • 調査終了後に設定を解除できるか

AIはライブラリの探索や式の草案作成を高速化できますが、実行中のプロセスへ影響を与える最終判断はエンジニアが担うべきです。

実務での使い分け

Logpointsは、次のような状況で特に効果を発揮します。

  • リクエストを止めるとタイムアウトする
  • 非同期処理やイベント駆動処理の流れを追いたい
  • コンテナやリモートホスト上のJavaプロセスを調べたい
  • 再ビルドや再デプロイに時間がかかる
  • 依存ライブラリ内部の値を確認したい
  • 再現頻度が低く、条件付きで情報を採取したい

一方、複雑な状態を長時間確認したい場合や、処理を一時停止してステップ実行したい場合は、通常のブレークポイントが適しています。println は、恒久的に残すべきアプリケーションログや、デバッガーに接続できない環境での基本的な診断手段として役立ちます。

まとめ

Logpointsは、単に便利な println の代替ではありません。実行中の処理を止めず、コードを変更せず、必要な箇所だけを観測するためのデバッグ機能です。

gRPCのようにクライアントの期限がサーバー処理へ影響するシステムでは、停止を伴うブレークポイントが調査条件を壊すことがあります。そのような場合、Logpointsで処理を流しながら値を追跡する方法が有効です。

IntelliJ IDEA 2026.2では設定操作や実行時のオーバーヘッドにも改善が加えられています。まずはローカルのJavaアプリケーションで、次のような小さな調査から試すとよいでしょう。

  1. リクエスト入口で入力値を出力する
  2. 呼び出し先へLogpointを追加する
  3. 条件分岐の直前と直後を比較する
  4. 高コストな式や大量出力を避ける
  5. 調査が終わったら設定を解除する

ブレークポイントでは処理を止めるのが危険で、println では再デプロイが重い。その中間にある現実的な選択肢として、Logpointsをデバッグ手段の一つに加えてみてはいかがでしょうか。

出典: JetBrains Blog

タイトルとURLをコピーしました