C# Tips | ログ・例外・診断:トレース出力

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

はじめに:トレース出力は「本番も視野に入れた“軽量ログ”」

「デバッグ出力」は開発中にとても便利ですが、多くはデバッグビルド専用で、本番では消えてしまいます。
一方、業務システムでは「本番環境でも、ある程度の流れを追える“軽いログ”」が欲しくなることが多いです。

そこで出てくるのが トレース出力(System.Diagnostics.Trace です。
トレース出力は、

開発中:出力ウィンドウやコンソールで処理の流れを追える
本番:設定次第でファイルやイベントログに書き出せる

という、「デバッグ寄りだけど、本番運用も視野に入れたログの仕組み」です。

ここでは、初心者向けに

Trace の基本
TraceListener による出力先の切り替え
レベル(情報・警告・エラー)を意識した書き方
小さなトレースユーティリティの形

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


Trace の基本:Debug との違いを押さえる

Debug は「開発専用」、Trace は「運用も視野」

System.Diagnostics には DebugTrace の 2 つのクラスがあります。

Debug
開発時(DEBUG シンボルが定義されているとき)だけ有効
リリースビルドではコンパイル時に消えることが多い

Trace
TRACE シンボルが定義されている限り有効
多くのプロジェクトでデバッグ・リリース両方に定義されている
本番でも有効にしておける

ざっくり言うと、

「開発中だけ見たいもの → Debug」
「本番でも残しておきたい“軽いログ” → Trace」

という使い分けになります。


Trace.WriteLine の基本的な使い方

まずは「処理の流れ」を文字で残してみる

一番シンプルな使い方は、Trace.WriteLine でメッセージを出すことです。

using System.Diagnostics;

public class UserService
{
    public void RegisterUser(string name)
    {
        Trace.WriteLine("ユーザー登録開始");

        // 何か処理
        Trace.WriteLine($"ユーザー登録処理中 Name={name}");

        // 登録完了
        Trace.WriteLine("ユーザー登録完了");
    }
}
C#

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

カテゴリ付きで出すこともできます。

Trace.WriteLine("ユーザー登録開始", "UserService");
Trace.WriteLine($"ユーザー登録処理中 Name={name}", "UserService");
Trace.WriteLine("ユーザー登録完了", "UserService");
C#

カテゴリを付けておくと、出力ウィンドウやログファイルで「どのモジュールのメッセージか」が分かりやすくなります。

ここでの重要ポイントは、「Trace は“処理の通過点”を軽く残すのに向いている」ということです。
例外ログほど重くはないけれど、「どこまで進んだか」を後から追えるようにするための線を引くイメージです。


TraceListener で出力先をコントロールする

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

Trace の強みは、「出力先を差し替えられる」ことです。
内部的には Trace.Listeners というコレクションに「リスナー(TraceListener)」がぶら下がっていて、
Trace.WriteLine した内容は、登録されているすべてのリスナーに流れます。

例えば、「コンソールにも出したい」場合はこうします。

using System.Diagnostics;

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

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

アプリ起動時に一度だけ呼びます。

TraceConfig.Configure();
C#

これで、Trace.WriteLine の内容がコンソールに表示されるようになります。

ファイルに書きたい場合は、ファイルストリームを渡します。

public static class TraceConfig
{
    public static void ConfigureFile(string filePath)
    {
        Trace.Listeners.Clear();

        var writer = new StreamWriter(filePath, append: true)
        {
            AutoFlush = true
        };

        Trace.Listeners.Add(new TextWriterTraceListener(writer));
    }
}
C#

ここでの重要ポイントは、「Trace の出力先はコードや設定で差し替えられる」ということです。
開発環境では出力ウィンドウ、本番ではファイルやイベントログ、といった切り替えがしやすい仕組みになっています。


レベルを意識したトレース:情報・警告・エラー

「全部同じ扱い」ではなく、重要度を分ける

素の Trace.WriteLine だけだと、すべてのメッセージが同じレベルで扱われます。
しかし、実務では「情報」「警告」「エラー」など、重要度を分けておくと後から分析しやすくなります。

Trace には、レベル付きのメソッドは標準では多くありませんが、
自分で簡単なラッパーを作ることで、レベルを表現できます。

public static class TraceLog
{
    public static void Info(string message, string? category = null)
    {
        Write("INFO", message, category);
    }

    public static void Warn(string message, string? category = null)
    {
        Write("WARN", message, category);
    }

    public static void Error(string message, string? category = null)
    {
        Write("ERROR", message, category);
    }

    private static void Write(string level, string message, string? category)
    {
        var line = $"[{DateTime.Now:O}] [{level}] {message}";

        if (category == null)
        {
            Trace.WriteLine(line);
        }
        else
        {
            Trace.WriteLine(line, category);
        }
    }
}
C#

使い方の例です。

TraceLog.Info("ユーザー登録開始", "UserService");
TraceLog.Warn("外部APIのレスポンスが遅い", "ExternalApi");
TraceLog.Error("ユーザー登録に失敗しました。", "UserService");
C#

ここでの重要ポイントは、「トレース出力にも“軽いレベル分け”を持たせると、後から検索・集計しやすくなる」ということです。
本格的なログフレームワークほどではなくても、最低限のレベルは付けておくと便利です。


トレース出力ユーティリティを作って「生 Trace 呼び出し」を減らす

ルールを一箇所に閉じ込める

アプリのあちこちで Trace.WriteLine を直書きしていると、

フォーマットがバラバラになる
出力先やルールを変えたいときに修正箇所が多くなる

といった問題が出てきます。

そこで、「トレース出力ユーティリティ」を一枚かませておくと、設計がきれいになります。

public static class AppTrace
{
    public static void ServiceStart(string serviceName)
    {
        TraceLog.Info($"サービス開始: {serviceName}", "Service");
    }

    public static void ServiceEnd(string serviceName)
    {
        TraceLog.Info($"サービス終了: {serviceName}", "Service");
    }

    public static void ExternalCall(string target, string detail)
    {
        TraceLog.Info($"外部呼び出し: {target} Detail={detail}", "External");
    }

    public static void ExternalWarning(string target, string detail)
    {
        TraceLog.Warn($"外部呼び出し警告: {target} Detail={detail}", "External");
    }

    public static void ExternalError(string target, string detail)
    {
        TraceLog.Error($"外部呼び出しエラー: {target} Detail={detail}", "External");
    }
}
C#

使う側はこうなります。

AppTrace.ServiceStart("UserRegister");

AppTrace.ExternalCall("UserApi", $"Id={id}");

// 何か処理…

AppTrace.ServiceEnd("UserRegister");
C#

ここでの重要ポイントは、「トレース出力の“文言・カテゴリ・レベル”をユーティリティに閉じ込める」ことです。
これにより、アプリ全体で一貫したトレースが取れるようになり、後から見返したときに理解しやすくなります。


トレース出力と本格ログ(ILogger)の関係

「トレース=軽量」「ILogger=本番の主役」と考える

最近の .NET では、Microsoft.Extensions.Logging.ILogger を使ったログが主流です。
では、Trace はもういらないのか?というと、そうでもありません。

考え方としては、

本番で必ず残したいログ(エラー、監査、業務イベント)
→ ILogger(Serilog、NLog など)で出す

開発・検証、本番の軽いトレース(処理の通過点、外部呼び出しの概要など)
→ Trace で出す(必要に応じてファイルやイベントログへ)

という役割分担がしっくりきます。

特に、古いコードベースや軽量なツールでは、
「大げさなログフレームワークは入れたくないけど、最低限のトレースは欲しい」という場面があり、
そこでは Trace がちょうどよい選択肢になります。

ここでの重要ポイントは、「Trace を“本番ログの代わり”にしない」ことです。
あくまで「軽量なトレース」として位置づけ、本当に重要なログは ILogger などでしっかり残す、という線引きを意識してください。


まとめ:トレース出力は“本番も見据えた、軽量な足跡”

トレース出力の本質を一言で言うと、

「処理の流れや外部呼び出しの足跡を、
開発中にも本番にも、軽いコストで残しておく仕組み」

です。

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

Trace は Debug より「運用寄り」で、TRACE シンボルがある限りリリースでも有効。
Trace.WriteLine で処理の通過点を簡単に記録できる。
TraceListener を使えば、出力先をコンソール・ファイル・イベントログなどに切り替えられる。
簡単なレベル(INFO/WARN/ERROR)を自前ラッパーで付けておくと、後から分析しやすい。
トレース出力ユーティリティを挟んで、「文言・カテゴリ・レベル」のルールを一箇所に閉じ込めると、コード全体が整う。

このあたりが腹落ちしていれば、「とりあえず Console.WriteLine」で済ませていた段階から一歩進んで、
“業務・実務でも通用するトレース出力”を設計できるようになります。

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