Logger

ビルドレベルでのミュート、ローテーション式ファイルバックエンド、差し替え可能な出力を備えた、カテゴリ化されフィルタリング可能なロギング。

ゲームコードとフレームワークのためのカテゴリ化ロギング · 最初に起動するサービス · no-op 差し替え: NullLogger

概要

Logger は UnityEngine.Debug.Log を、単一のフィルタリング可能な診断システムにラップします。ビルドレベルでのミュート(Release ビルドは Debug レベルのメッセージを自動的に破棄)、カテゴリごとのオン/オフスイッチ、そして差し替え可能な出力バックエンド — デフォルトは Unity Console、ほかにローテーション式のログファイルや自作のクラッシュレポートシンク — を提供します。散らばった Debug.Log 呼び出しの代わりに、ゲームコードとすべてのフレームワークサービスの診断は Logger.Info(category, message) を通り、一貫したフォーマットと、すべてを制御できる 1 つの中心的な場所が手に入ります。

簡単な使用例

セーブのデバッグ中だけ、それ以外の Console ノイズを抑える例です:

using CommonGameSystem.Core;
using UnityEngine;
using Logger = CommonGameSystem.Core.Logger;  // Unity has its own Logger type

[DefaultExecutionOrder(100)]  // Run after the framework has started
public class DebugConfig : MonoBehaviour
{
    private void Awake()
    {
        // Silence everything except the Save category
        Logger.MinimumLevel = LogLevel.Off;
        Logger.SetCategoryFilter(Logger.Categories.Save, LogLevel.Debug);
    }
}
// Elsewhere — only Save messages appear in the Console
Logger.Info(Logger.Categories.Net, "heartbeat ok");         // Silent
Logger.Info(Logger.Categories.Save, "slot 0 corrupted");    // Shown

static な Logger ヘルパーは、何も解決せずにどこでも使えます — 起動時にロギングバックエンドをキャッシュしてくれます。明示的な依存関係を好む場合(たとえばテストでフェイクを注入するため)は、Service Locator から ILogger を解決して Awake でキャッシュしてください。

API の全体像

出力メソッド(すべて static、スレッドセーフ)

  • Logger.Debug(string category, string message) — Debug レベル。Release ビルドでは呼び出し箇所全体がコンパイラによって除去されます
  • Logger.Debug(string message)Categories.Default での Debug
  • Logger.Info(string category, string message) — Info レベル
  • Logger.Info(string message)Categories.Default での Info
  • Logger.Warning(string category, string message) — Warning レベル
  • Logger.Warning(string message)Categories.Default での Warning
  • Logger.Error(string category, string message) — Error レベル
  • Logger.Error(string message)Categories.Default での Error
  • Logger.Critical(string category, string message) — Critical レベル
  • Logger.Critical(string message)Categories.Default での Critical
  • Logger.Exception(string category, Exception ex, string message = null) — 例外をスタックトレースと任意のコンテキストメッセージ付きで記録します。ex が null なら ArgumentNullException をスローします

どの出力メソッドでも、category が null の場合は Categories.Default にフォールバックします。

フィルター制御(メインスレッド専用)

  • Logger.MinimumLevel { get; set; } — グローバルなレベルの下限。デフォルトはエディタと Development ビルドで Debug、Release ビルドで WarningLogLevel.Off に設定するとすべてをミュートします — static ヘルパーも含めて即座に効きます。
  • Logger.SetCategoryFilter(string category, LogLevel level) — 1 つのカテゴリの下限を上書きします。category が null なら ArgumentNullException をスローします。
  • Logger.ClearCategoryFilter(string category) — カテゴリの上書きを削除し、グローバルな下限に戻します。category が null なら ArgumentNullException をスローします。

LogLevel の値

意味
Debugフレーム単位のトレースや冗長な詳細。Release ビルドからは完全に除去されます。
Info状態遷移やライフサイクルイベント。常にコンパイルされます。
Warning回復済みの障害、疑わしい入力、非推奨のパス。常にコンパイルされます。
Error操作は失敗したがゲームは続行。常にコンパイルされます。
Criticalプロセスの整合性が損なわれている — 次の失敗は致命的になる可能性が高い。常にコンパイルされます。
Offセンチネル — 下限として設定すると、すべてのレベルをフィルタで除外します。

標準カテゴリ(文字列定数)

Logger.Categories.Default.Bootstrap.ServiceLocator.Save.Audio.UI.Input.AI.Net.Perf(パフォーマンス: プール、時間、タイマー)、.Configuration.Scene.Localization.Achievements

カテゴリキーは大文字と小文字を区別します — 常に定数を使い、文字列リテラルは決して使わないでください。

ILogger インターフェース(カスタムバックエンドはこれを実装)

  • void Log(LogLevel level, string category, string message) — メッセージを出力
  • void LogException(string category, Exception ex, string message) — 例外をスタックトレース付きで出力
  • LogLevel MinimumLevel { get; set; } — インスタンスごとのレベル下限
  • LogLevel GetEffectiveLevel(string category) — カテゴリの実効的な下限を照会。category が null なら ArgumentNullException をスロー

組み込みバックエンド

  • UnityConsoleLogger — デフォルト。Bootstrap が登録し、フォーマット済みの行を Unity Console に書き出します。

  • FileLogger — リリース後のサポートに向けた、オプトインのローテーション式ファイルロガー。Unity 自身の Player.log は区別のない単一ストリームで、Windows では次回起動時に上書きされます — プレイヤーが送ってくれる頃には、証拠はしばしば消えています。FileLogger は各行にカテゴリとレベルを残し、ファイルをローテーションするので履歴が生き残ります:

    using CommonGameSystem.Core;
    using UnityEngine;
    
    [DefaultExecutionOrder(100)]
    public class FileLoggerInstaller : MonoBehaviour
    {
        private void Awake()
        {
            var fileLog = new FileLogger();       // {persistentDataPath}/logs/cgs.log
            fileLog.CaptureUnityLogStream(true);  // also record uncaught exceptions
            ServiceLocator.Replace<ILogger>(fileLog);
        }
    }
    
    • FileLogger(long maxBytes = 4 MB, int maxFiles = 3){persistentDataPath}/logs/cgs.log に記録し、ファイルが maxBytes を超えるとローテーションして、書き込み中のものを含め maxFiles 個のファイルを保持します。メインスレッドで構築してください(Application.persistentDataPath を一度読み取ります)。
    • FileLogger(string path, long maxBytes, int maxFiles) — 明示的なパスに記録します。ディレクトリがなければ作成されます。パスが null または空白なら ArgumentException をスローします。
    • FilePath — 現在書き込み中のファイルの絶対パス。
    • CaptureUnityLogStream(bool enabled) — Unity 自身のログストリームをファイルにミラーリングします: スタックトレース付きの未捕捉例外に加えて、ILogger を経由しなかったあらゆる Debug.Log も記録します。UnityConsoleLogger も同時にアクティブな間は、これを有効化しないでください — あちらのロガーは Debug.Log 経由で書き込むため、フレームワークのすべての行が二重に記録されてしまいます。
    • スレッドセーフ(すべての書き込みはロックで直列化)で、決してスローしません: I/O の失敗は握りつぶされ、書き込みが 8 回連続で失敗すると、失敗が確実な書き込みを毎回リトライする代わりにインスタンス自身が無効化されます。LogException 経由で記録された例外は MinimumLevel をバイパスします — クラッシュを説明する唯一のレコードをフィルタで消すことは、この設定の意図では決してないからです。
    • IDisposable を実装しています — シャットダウン時に破棄して、フラッシュと購読解除を行ってください。
  • NullLogger — すべてのロギング呼び出しを静かな no-op にします:

    // Mute everything at runtime — takes effect immediately, static helpers included:
    Logger.MinimumLevel = LogLevel.Off;
    
    // Or replace the backend with a silent no-op (picked up by new resolves):
    ServiceLocator.Replace<ILogger>(new NullLogger());
    

    ヘッドレスや自動化ビルドからロギングのオーバーヘッドを取り除く、Console スパムなしでゲームコードをテストする、ロギングノイズなしでベンチマークする、といった用途に使います。ロギングを消音しても、他のすべてのサービスは通常どおり動作し続けます — フレームワークのいかなる部分もログ出力には依存していません。

独自バックエンドの作成

ILogger を実装するクラスなら何でもバックエンドになれます。たとえば、エラーをクラッシュレポート SDK に転送する場合:

using System;
using CommonGameSystem.Core;

public class CrashReportLogger : ILogger
{
    public LogLevel MinimumLevel { get; set; } = LogLevel.Warning;

    public void Log(LogLevel level, string category, string message)
    {
        if (level < MinimumLevel) return;
        // Forward to your crash-reporting or analytics SDK here.
        Console.WriteLine($"[{level}] [{category}] {message}");
    }

    public void LogException(string category, Exception ex, string message)
    {
        Console.WriteLine($"[Exception] [{category}] {message}\n{ex}");
    }

    public LogLevel GetEffectiveLevel(string category) => MinimumLevel;
}

ServiceLocator.Replace<ILogger>(new CrashReportLogger()) で登録します。この呼び出しの後に Service Locator から ILogger を解決するコードは、あなたのバックエンドを使います。注意点が 1 つ: static な Logger ヘルパーは初回実行時にバックエンドをキャッシュし、それはフレームワーク起動中に起こります — Play セッションの後半で行った差し替えは、static ヘルパーをリターゲットしません。キャッシュがリセットされる次の Play 開始時に、あなたのバックエンドを拾います。

挙動とエッジケース

  • Release ビルドは Debug 呼び出しを完全に除去します。 Logger.Debug($"msg = {Expensive()}") は、Release ビルドでは呼び出し箇所全体がコンパイラによって除去されるため、Expensive() は決して実行されません。タダで手に入る最適化です。

  • Unity の型との名前衝突。 ファイルに using CommonGameSystem.Core;using UnityEngine; の両方があると、LoggerILoggerUnityEngine.Logger および UnityEngine.ILogger と衝突します。ファイルの先頭にエイリアスを追加してください:

    using Logger = CommonGameSystem.Core.Logger;
    using ILogger = CommonGameSystem.Core.ILogger;
    
  • ワーカースレッドには起動時のウォームアップが必要です。 Bootstrap は起動中にメインスレッドで Logger を一度呼び出し、それによって以降のワーカースレッドからの呼び出し(セーブ I/O、ネットワーク I/O)が安全になります。ウォームアップ前にワーカースレッドがログを出すと、メッセージは素の Debug.Log にフォールバックします。

  • カテゴリキーは大文字と小文字を区別します。 "Net""net" は別のフィルターです。常に Logger.Categories.* 定数を使い、文字列リテラルは使わないでください。

  • フィルター API はメインスレッド専用です。 SetCategoryFilter / ClearCategoryFilter はメインスレッドから呼び出してください。出力メソッド(InfoWarningError など)はどこからでもスレッドセーフです。

  • GetEffectiveLevel は非 null のカテゴリを要求します。 出力パス(nullCategories.Default として扱う)とは異なり、照会 API は null カテゴリに対して ArgumentNullException をスローします。これにより設定バグが早期に表面化します。

  • セッション途中のバックエンド差し替えは static ヘルパーをリターゲットしません。 上の「独自バックエンドの作成」の注意点を参照してください。実行時に即座にミュートするには Logger.MinimumLevel = LogLevel.Off を使ってください。

関連ページ