C# Tips | ログ・例外・診断:デバッグ出力

C# C#
スポンサーリンク

はじめに:デバッグ出力は「本番にはいらないけど、開発中はめちゃくちゃ役に立つメモ」

コードを書いているとき、「ここまで処理が来ているか確認したい」「この変数の中身を一瞬だけ見たい」という場面が必ず出てきます。
ブレークポイントを張ってウォッチしてもいいのですが、毎回それをやるのは面倒ですし、バッチやサーバーサイドでは VS をつながないことも多いですよね。

そこで使えるのが デバッグ出力(Debug / Trace 出力) です。
「画面には出さないけれど、開発者向けのメッセージを“出力ウィンドウ”やログに流す」ための仕組みです。

C# では主に System.Diagnostics.DebugSystem.Diagnostics.Trace を使います。

ここでは、初心者向けに

Debug と Trace の違い
Debug.WriteLine / Trace.WriteLine の基本
リスナー(Listeners)を使った出力先の切り替え
小さな「デバッグ出力ユーティリティ」の形

を、例題付きでかみ砕いて説明します。


Debug と Trace の違いをざっくり理解する

Debug は「デバッグビルド専用」、Trace は「リリースでも使える」

まず一番大事な違いから押さえます。

Debug クラスのメソッド(Debug.WriteLine など)は、DEBUG シンボルが定義されているときだけ有効になります。
通常、Visual Studio の「デバッグビルド」では DEBUG が定義され、「リリースビルド」では定義されません。
そのため、Debug.WriteLine はリリースビルドではコンパイル時に消えてしまいます。

一方、Trace クラスのメソッド(Trace.WriteLine など)は、TRACE シンボルが定義されているときに有効になります。
多くのプロジェクトでは、デバッグビルド・リリースビルドの両方で TRACE が定義されていることが多く、「本番環境でも有効にできる“軽めのログ”」として使えます。

ここでの重要ポイントは、次の一言です。

「開発中だけ見たいものは Debug、本番でも残しておきたい“軽いトレース”は Trace」

この感覚を持っておくと、どちらを使うか迷いにくくなります。


Debug.WriteLine / Trace.WriteLine の基本

とりあえず「出力ウィンドウに文字を出す」ところから

一番シンプルな使い方は、WriteLine に文字列を渡すだけです。

using System.Diagnostics;

public void DoSomething()
{
    Debug.WriteLine("DoSomething に入りました。");
    // 何か処理
    Debug.WriteLine("DoSomething を抜けます。");
}
C#

デバッグ実行中に Visual Studio の「出力」ウィンドウ(表示 → 出力)を開いておくと、
このメッセージが流れていくのが見えます。

Trace.WriteLine も同じように使えます。

Trace.WriteLine("外部 API 呼び出し開始");
C#

違いは先ほどの通り、「Debug はデバッグビルド専用」「Trace は TRACE シンボルがある限り有効」という点です。

ここでの重要ポイントは、「Debug / Trace は“例外を投げないログ”」だということです。
Console.WriteLine のように見えますが、出力先が「デバッグ出力」であること、ビルド構成によってはコンパイル時に消えることが大きな違いです。


リスナー(TraceListener)で出力先を変える

「出力ウィンドウ」だけでなく「ファイル」「コンソール」「イベントログ」にも流せる

DebugTrace の出力は、内部的には「リスナー(TraceListener)」という仕組みに流れています。

何も設定しない場合は、DefaultTraceListener が使われ、Visual Studio の出力ウィンドウなどに表示されます。
しかし、リスナーを追加すれば、出力先を自由に増やせます。

例えば、「デバッグ出力をコンソールにも出したい」場合はこう書きます。

using System.Diagnostics;

public static class DebugOutputConfig
{
    public static void Configure()
    {
        Trace.Listeners.Clear();

        Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));
        Trace.AutoFlush = true;
    }
}
C#

アプリ起動時に DebugOutputConfig.Configure(); を一度呼んでおくと、
Debug.WriteLineTrace.WriteLine の出力がコンソールにも流れるようになります。

同じように、TextWriterTraceListener にファイルストリームを渡せば、デバッグ出力をファイルに書き出すこともできます。

ここでの重要ポイントは、「Debug と Trace は同じ Listeners コレクションを共有している」という点です。
どちらにリスナーを追加しても、両方の出力がそのリスナーに流れます。


条件付きで出力する:カテゴリやスイッチを使う

「うるさすぎるデバッグ出力」をオンオフできるようにする

開発が進むと、デバッグ出力の量が増えすぎて「ノイズだらけ」になりがちです。
そこで使えるのが、カテゴリやスイッチ(TraceSwitchBooleanSwitch)です。

まずはカテゴリ付きの出力。

Debug.WriteLine("ユーザー情報取得開始", "UserService");
Debug.WriteLine("レスポンス: " + json, "UserService");
C#

出力ウィンドウでは、「カテゴリ: メッセージ」という形で表示されるので、
カテゴリでフィルタしたり、目視で追いやすくなります。

もう一歩進めて、TraceSwitch を使うと「どのレベルまで出すか」を切り替えられます。

static TraceSwitch ServiceSwitch = new TraceSwitch(
    "ServiceSwitch",
    "サービス層のトレース用スイッチ");

public void CallService()
{
    if (ServiceSwitch.TraceInfo)
    {
        Trace.WriteLine("サービス呼び出し開始", "Service");
    }

    // 処理…

    if (ServiceSwitch.TraceVerbose)
    {
        Trace.WriteLine("詳細なレスポンス内容...", "Service");
    }
}
C#

TraceSwitch のレベルは、.config ファイルなどから変更できるため、
「本番では Error だけ」「検証環境では Verbose まで」といった切り替えが可能です。

ここでの重要ポイントは、「デバッグ出力も“オンオフできる設計”にしておくと、後から運用しやすい」ということです。
最初から「全部出す or 何も出さない」ではなく、「必要なときにだけ詳しく出せる」ようにしておくと便利です。


小さな「デバッグ出力ユーティリティ」を作る

呼び出し側をシンプルにして、「やること」を一箇所に寄せる

毎回 Debug.WriteLine を直書きしていると、フォーマットがバラバラになったり、
あとから「やっぱり Trace に変えたい」「ファイルにも出したい」となったときに修正が大変です。

そこで、「デバッグ出力ユーティリティ」を一枚かませておくと、後からの変更が楽になります。

using System.Diagnostics;

public static class DebugLog
{
    public static void Info(string message, string? category = null)
    {
        if (category == null)
        {
            Debug.WriteLine(message);
        }
        else
        {
            Debug.WriteLine(message, category);
        }
    }

    public static void DumpObject(string title, object? obj)
    {
        Debug.WriteLine($"[{title}] {obj}");
    }

    public static void TraceInfo(string message, string? category = null)
    {
        if (category == null)
        {
            Trace.WriteLine(message);
        }
        else
        {
            Trace.WriteLine(message, category);
        }
    }
}
C#

使う側はこうなります。

DebugLog.Info("ユーザー登録開始", "User");
DebugLog.DumpObject("入力パラメータ", request);
DebugLog.TraceInfo("外部API呼び出し開始", "ExternalApi");
C#

ここでの重要ポイントは、「Debug / Trace の生呼び出しをアプリ全体にばらまかない」ことです。
ユーティリティを経由させておけば、「本番では Trace だけにする」「特定のカテゴリだけファイルに出す」といったポリシー変更を一箇所で行えます。


実務での使いどころと注意点

「本番ログ」と「デバッグ出力」をちゃんと分ける

業務システムでは、ILogger(Serilog、NLog、Microsoft.Extensions.Logging など)を使った本格的なログと、
Debug / Trace を使ったデバッグ出力をどう使い分けるかがポイントになります。

ざっくりした考え方としては、

本番で必ず残したいもの(監査ログ、エラー、業務イベント)
→ きちんとしたロガー(ILogger)で出す

開発・検証中にだけ見たい細かい情報(変数の中身、処理の通過確認など)
→ Debug / Trace 出力にする

という分け方が多いです。

ここでの重要ポイントは、「Debug / Trace に“本番で必須の情報”を乗せない」ことです。
デバッグ出力はあくまで「開発者のための補助線」であり、
本番運用の証跡や監査の代わりにはなりません。


まとめ:デバッグ出力は“開発者のためのメモ帳”、ユーティリティで賢く使う

デバッグ出力の本質を一言で言うと、

「ユーザーには見せないけれど、開発者がコードの流れや状態を追うためのメモを、
出力ウィンドウやファイルにさりげなく書き残す仕組み」

です。

押さえておきたいポイントを整理すると、次のようになります。

Debug はデバッグビルド専用、Trace は TRACE シンボルがある限り有効。
Debug.WriteLine / Trace.WriteLine で簡単にメッセージを出せる。
TraceListener を追加すれば、出力先をコンソールやファイル、イベントログなどに変えられる。
カテゴリやスイッチを使うと、「どのレベルまで出すか」「どの範囲だけ出すか」を切り替えられる。
小さなデバッグ出力ユーティリティを挟んでおくと、後からのポリシー変更や整理が楽になる。

このあたりが腹落ちしていれば、「とりあえず Console.WriteLine」で済ませていた段階から一歩進んで、
“業務・実務でも通用するデバッグ出力の書き方”に近づけます。

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