MCP ロギング 実装ガイド
このドキュメントは、MCPサービス実装時のロギング方針を提供します。 開発者がサービスクラスにログを追加する際の具体的な指針を示します。
1. 基本原則
1.1 ログレベルの使い分け
| レベル | 用途 | 例 |
|---|---|---|
| Trace | LLM動作の詳細トレース | プロンプト、レスポンス、推論過程 |
| Debug | 開発・デバッグ情報 | 内部状態、変数値、フロー確認 |
| Info | 通常の実行フロー | 処理開始、完了、主要な状態変化 |
| Warn | 警告(継続可能) | 設定不足、フォールバック実行、非推奨機能使用 |
| Error | エラー(継続 可能) | 操作失敗だが結果オブジェクトで通知可能 |
| Critical | 致命的エラー(継続不可) | 例外をthrowする直前 |
2. 例外処理とロギングのパターン
2.1 基本パターン:例外を先に作成してからログ
// ? 悪い例
if (options == null)
{
McpLogger?.Critical($"{LogPrefix} 初期化失敗");
throw new ArgumentNullException(nameof(options));
}
// ? 良い例
if (options == null)
{
var ex = new ArgumentNullException(nameof(options));
McpLogger?.Critical($"{LogPrefix} 初期化失敗", ex);
throw ex;
}
理由:
- 例外オブジェクトをログに含めることで、スタックトレースなどの詳細情報が記録される
- ログと例外の情報が一致する
2.2 Critical vs Error の使い分け
Critical: 継続不可能で throw する場合
if (!File.Exists(fullPath))
{
var ex = new FileNotFoundException($"File not found: {filePath}");
McpLogger?.Critical($"{LogPrefix} ファイルが見つかりません: fullPath={fullPath}", ex);
throw ex;
}
Error: 継続可能な場合(結果オブジェクトで通知)
if (!Repository.IsValid(repoPath))
{
var ex = new InvalidOperationException($"Not a valid git repository: {repoPath}");
McpLogger?.Error($"{LogPrefix} リポジトリが無効です: {repoPath}", ex);
return new GitPullResult
{
Success = false,
Message = ex.Message
};
}
使い分けの基準:
- 例外を throw する → Critical
- 結果オブジェクトを返す → Error
- 処理を続行できる → Warn
3. サービスクラスでのロギング実装パターン
3.1 LogPrefix の定義
各サービスクラスの先頭で定義します。
public class GitHubService : McpServiceBase, IGitHubService
{
private const string LogPrefix = $"{nameof(GitHubService)}:";
// ...
}
3.2 コンストラクタ
public GitHubService(IMcpLogger mcpLogger, IGitHubSettings gitHubSettings, ...)
: base(mcpLogger)
{
McpLogger?.Info($"{LogPrefix} 初期化処理開始");
if (gitHubSettings == null)
{
var ex = new ArgumentNullException(nameof(gitHubSettings));
McpLogger?.Critical($"{LogPrefix} 初期化失敗", ex);
throw ex;
}
_gitHubSettings = gitHubSettings;
McpLogger?.Info($"{LogPrefix} 初期化完了");
}
3.3 公開メソッド
簡単な取得メソッド
public IEnumerable<string> GetRepositoryKeys()
{
McpLogger?.Debug($"{LogPrefix} GetRepositoryKeys 開始");
var keys = _gitHubSettings.GitHubRepositories.Keys;
McpLogger?.Debug($"{LogPrefix} GetRepositoryKeys 完了: {keys.Count()}件");
return keys;
}
複雑な処理メソッド
public async Task<string> GetFileContentAsync(string repositoryKey, string filePath)
{
McpLogger?.Info($"{LogPrefix} GetFileContentAsync 開始: repositoryKey={repositoryKey}, filePath={filePath}");
if (!_gitHubSettings.GitHubRepositories.TryGetValue(repositoryKey, out var repoSettings))
{
var ex = new ArgumentException($"Repository '{repositoryKey}' not found in configuration.");
McpLogger?.Critical($"{LogPrefix} リポジトリが設定に見つかりません: repositoryKey={repositoryKey}", ex);
throw ex;
}
McpLogger?.Debug($"{LogPrefix} ローカル優先モード: localPath={repoSettings.LocalPath}");
// ... 処理 ...
McpLogger?.Info($"{LogPrefix} GetFileContentAsync 完了: サイズ={content.Length}文字");
return content;
}
3.4 プライベートメソッド
重要な処理のみログを追加します。
private async Task<string> GetGitHubFileAsync(string owner, string repo, string path, string branch)
{
var cacheKey = $"github:{owner}/{repo}:{branch}:{path}";
McpLogger?.Debug($"{LogPrefix} GetGitHubFileAsync 開始: owner={owner}, repo={repo}, path={path}");
if (_cache.TryGetValue(cacheKey, out string? cachedContent))
{
McpLogger?.Debug($"{LogPrefix} キャッシュヒット: cacheKey={cacheKey}");
return cachedContent;
}
McpLogger?.Debug($"{LogPrefix} キャッシュミス、GitHubから取得");
// ... API呼び出し ...
McpLogger?.Debug($"{LogPrefix} GetGitHubFileAsync 完了: サイズ={content.Length}文字");
return content;
}
4. ログに含めるべき情報
4.1 必須情報
- メソッド名: どの処理か特定できるように
- 主要パラメータ: 処理対象の識別情報
- 処理結果: 件数、サイズ、成功/失敗