1. はじめに
普通のテキストログがあなたの日記でメモが "今日ご飯を食べた" みたいなものだとすると、構造化ロギングは各エントリをフィールド付きのカードに変えます: {日付: ..., イベント: "食べた", カロリー: 500, 料理: "焼き肉"}。つまり、その日記を読むだけでなく、月ごとのカロリーのグラフを作ったり、料理でフィルタしたり、いつ遅く食べたかを調べられるってことです。
なぜプレーンテキストだけでは不十分なのか?
プレーンテキストだと楽なんだけど... ある時点から辛くなります。ログからエラーの統計を集めたり、EC サイトの売上を集計したり、あるユーザーの操作履歴を ID(例えば 42)で辿ったりするのは、全部がただの長いテキストだと大変です。構造化ロギングがあればログに分析やAIを繋げられる。異常検知したり、ダッシュボード作ったり、自動で問題に対処したりできます。
利点
- メッセージだけでなく、それに紐づくデータ(フィールド/プロパティ)もログできる。
- ログは自動的に解析できる:集計、フィルタ、レポート作成が容易。
- JSON のような標準フォーマットを使えるので、機械でのパースが簡単。
Serilog:何でこれを使うの?
Serilog(公式サイト: serilog.net, ドキュメント: github.com/serilog/serilog/wiki)は .NET 用の人気のある構造化ロギングライブラリです。Microsoft.Extensions.Logging とよく統合でき、ファイル、コンソール、Seq、ElasticSearch、Grafana、Azure など多数の出力先をサポートし、パフォーマンスへの影響は小さく設定もシンプルです。
Serilog は「普通のログ」と何が違うの?
- 構造: ログはフィールドを持つオブジェクトになり、ID 42 のユーザーのエラーだけを抽出する、みたいなことができる。
- フォーマット: テキストだけでなく JSON や XML を出力でき、後処理が楽。
- 柔軟性: たくさんの既製の sink パッケージがあって、どこにでも送れる。
Serilog のログエントリの構造
コードを書く前に、まず構造化ログの例を見てみましょう。
{
"Timestamp": "2024-06-22T10:23:45.123Z",
"Level": "Information",
"MessageTemplate": "ユーザー {UserId} がログインしました",
"Properties": {
"UserId": 42,
"IpAddress": "127.0.0.1"
}
}
これだけで解析ツールは「ユーザー #42 と IP アドレスが関係している」と分かります。
2. C# プロジェクトでの Serilog のインストールと基本設定
ステップ1. NuGet パッケージのインストール
Rider/Visual Studio の NuGet Package Manager で以下を入れてください:
- Serilog
- Serilog.Sinks.Console(コンソール出力)
- Serilog.Extensions.Logging(Microsoft.Extensions.Logging と統合するため)
コマンドラインからは:
dotnet add package Serilog
dotnet add package Serilog.Sinks.Console
ステップ2. 最低限の設定
Program.cs に設定を追加して最初のログを出してみましょう。
using System;
using Serilog;
namespace MySuperApp
{
class Program
{
static void Main(string[] args)
{
// 1. Serilog の基本設定: コンソール出力
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.CreateLogger();
// 2. 構造化ロギングの例
int userId = 42;
string ip = "127.0.0.1";
Log.Information("ユーザー {UserId} が IP {IpAddress} でログインしました", userId, ip);
Log.CloseAndFlush();
}
}
}
コンソールには大体こんな感じで出ます:
[10:30:16 INF] ユーザー 42 が IP 127.0.0.1 でログインしました
出力先を JSON ファイルや Seq、ほかのシステムに向けるのは簡単です。
3. ログのフォーマット: Message Template
Serilog では文字列連結の代わりにテンプレート構文を使います:
Log.Information("ファイルに対する操作 {Operation}: {FileName}", "削除", "test.txt");
これは単なる見た目の良さだけじゃなくて、ログに Operation や FileName といったフィールドが入るので、後でフィルタや集計に使えます。
string.Format と何が違うの?
string.Format("Операция {0} над файлом {1}", operation, fileName) はプレースホルダー {0}, {1} を置き換えるだけです。Serilog はフィールドを分離して扱うので、解析に向いています。
柔軟な設定: レベル、フィルター、複数の sink
Serilog は同時に複数の場所にログを書けます。
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.File("log.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
これでコンソールにもファイル(日別ローテーション)にもログが出ます。
4. 例
コンソールの「メモ帳」アプリを作るとします。ユーザーのエントリを作成できて、その操作を構造化ログとして残します。
using System;
using Serilog;
namespace NotesApp
{
class Program
{
static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.Console()
.WriteTo.File("notes-log.json", rollingInterval: RollingInterval.Day,
formatter: new Serilog.Formatting.Json.JsonFormatter())
.CreateLogger();
Console.WriteLine("ユーザー名を入力して:");
string userName = Console.ReadLine();
Log.Information("ユーザー {UserName} が NotesApp を起動しました", userName);
while (true)
{
Console.WriteLine("メモの内容を入力してください(終了 と入力すると終了):");
string note = Console.ReadLine();
if (note == "終了")
{
Log.Information("ユーザー {UserName} が終了しました", userName);
break;
}
Log.Information("ユーザー {UserName} がメモを作成しました: {NoteText}", userName, note);
}
Log.CloseAndFlush();
}
}
}
補足:
ログは誰がアプリを起動し、何を入力し、いつ終了したかを記録します。ファイル notes-log.json の各エントリは JSON オブジェクトで、解析が簡単です。
5. 便利なポイント
構造化ロギングのベストプラクティス
- 本番環境で Debug/Trace を乱用しない — Information や Warning を適切に使う。
- 文字列連結ではなく、テンプレートで名前付きパラメータを使う。
- 機密データ(パスワード、トークン、キー)は絶対にログに出さない。
- エラーだけでなく、重要なビジネスイベントもログする。
- ログのローテーションとクリーンアップを設定してディスク不足を防ぐ。
可視化と解析: Seq, Kibana, Application Insights
Serilog は多くの sink をサポートしていて、ログの送り先を柔軟に選べます。
| Sink | 概要 | どこで使われるか |
|---|---|---|
| Console | そのままコンソールに出力 | 開発、テスト |
| File | ローカルやネットワーク上のファイルに出力 | 小規模プロジェクト、dev |
| Seq | フィルタやダッシュボード付きの Web インターフェース | エンタープライズ、分析 |
| ElasticSearch | 強力な保存と解析のためのシステム | 大規模な組織 |
| Azure Application Insights | クラウド監視とテレメトリ | Azure を使ったサービス |
Seq (datalust.co/seq) は開発や社内利用で人気のあるソリューションです:フィールド検索が便利で、ダッシュボード作るのが速いです。
テーブル表示や可視化
以下は構造化ログでよく記録する項目の簡単な表です:
| ログするもの | Serilog での表現 | 値の例 |
|---|---|---|
| ユーザー ID | |
123 |
| アクション | |
"削除" |
| エラー | |
"登録モジュール" |
| 操作時間 | {Elapsed:0.000} 秒 | 1.234 |
| ファイル名 | |
"report.pdf" |
面白い機能と追加の可能性: Serilog
- Enrichers: 各ログにプロパティを追加できる(例えば .Enrich.WithMachineName())。
- 相関ログ: チェーンを辿れるように RequestId を入れる。
- appsettings.json での設定: 本番運用時に便利。
{
"Serilog": {
"MinimumLevel": "Debug",
"WriteTo": [
{ "Name": "Console" },
{ "Name": "File", "Args": { "path": "log.txt" } }
]
}
}
高度な sink:Slack、Telegram、メールにログを飛ばすことも可能ですが、エラーごとに大量にメールが飛ばないよう注意してください。
6. 実践: Microsoft.Extensions.Logging との統合
.NET では特定のライブラリに依存しないために標準のインターフェイス ILogger を使うことが多いです。Serilog はプロバイダとして接続できます。
ステップ1. パッケージのインストール
dotnet add package Serilog.Extensions.Logging
ステップ2. 設定
using Microsoft.Extensions.Logging;
using Serilog;
// ...
// 通常通り Serilog を設定:
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateLogger();
// で、Microsoft.Extensions.Logging を使う
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddSerilog();
});
ILogger<Program> logger = loggerFactory.CreateLogger<Program>();
logger.LogInformation("テストメッセージ: {TestValue}", 123);
// 忘れずにクローズ:
Log.CloseAndFlush();
補足:
これでコード中の ILogger<T> はプロバイダに依存しません — 必要なら NLog や Log4Net に切り替えられます。
7. Serilog を使うときのよくあるミス
ログの過剰出力: 何でもかんでも出すと重要な情報が埋もれます。
例外をテキストだけでログする: 例外オブジェクトを渡すオーバーロードを使いましょう — そうするとエラーの構造がログに残ります。
try
{
// some code
}
catch (Exception ex)
{
Log.Error(ex, "リクエストの実行中にエラーが発生しました");
}
設定の乱用: 設定を雑に増やしてしまうと管理が難しくなります — 必要な sink とレベルだけを追加しましょう。
GO TO FULL VERSION