minato128 blog

minato128の公開メモ帳です。

ASP.NET Core で Serilog を使ってログ出力する

Serilog とは?

構造化ログ出力ライブラリです。

C# 側でこう書くと、

_logger.LogWarning("送信できないドメインです {@Params}", new { model.Id, model.Category });

こんな感じで出力できます。

{
  "@t": "2017-08-24T02:24:16.0165287Z",
  "@mt": "送信できないドメインです {@Params}",
  "@l": "Warning",
  "Params": {
    "Id": "1234abc",
    "Category": "Test"
  },
  "SourceContext": "MailWorker.Controllers.MainController",
  "ActionId": "b9510bc5-ac72-4651-b3ba-1116371b019a",
  "ActionName": "MailWorker.Controllers.MainController.Post (MailWorker)",
  "RequestId": "0HL7AISC1171N:00000004",
  "RequestPath": "/",
  "MachineName": "TEST-002"
}

構造化された状態で持っておくことでログを効率的に処理する(集計やアラート設定など)ことができます。

Install / Setup (ASP.NET Core / .NET Core 2.0)

Install-Package Serilog
Install-Package Serilog.Extensions.Logging
Install-Package Serilog.Enrichers.Environment
Install-Package Serilog.Formatting.Compact
Install-Package Serilog.Sinks.Logentries
public class Startup
{
    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
            .AddEnvironmentVariables();

        Configuration = builder.Build();

        // Serilog settings
        var token = Configuration.GetValue<string>("Logentries:Token");
        if (!string.IsNullOrWhiteSpace(token))
        {
            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Information() // デフォルトログレベル
                .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) // SourceContext毎のレベルの上書き
                .MinimumLevel.Override("System", LogEventLevel.Warning)
                .Enrich.FromLogContext() // Context (RequestIdやRequestPathなど)を付加する
                .Enrich.WithMachineName() // マシン名を付加する
                .WriteTo.Logentries(token, new CompactJsonFormatter()) // Logentries に CompactJson で書き出す
                .CreateLogger();
            Log.Information("Startup End"); // アプリの起動ログが書ける
        }
    }

    public IConfigurationRoot Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddLogging(loggingBuilder => loggingBuilder.AddSerilog(dispose: true));
    }
}

通常は標準のLoggerをDIで使いますが、Startupでは使えないのでSerilogのAPIで起動ログを書いています。

用語

https://github.com/serilog/serilog/wiki/Writing-Log-Events https://github.com/serilog/serilog/wiki/Configuration-Basics

  • Source Contexts
    • NLog や Log4net でいうところの logger name
  • Enrichers
    • ログに特定の情報を固定付与する
      • NLogでもできないことはないが、こちらの方が拡張しやすい
    • Serilog.Enrichers.[付与情報] という命名規則でライブラリが作成されている
  • Sinks

最初に知っておいたほうがよさそうなライブラリ

  • Serilog.Extensions.Logging
    • 標準の Microsoft.Extensions.Logging に紐付ける
    • ログ出力が標準実装になっていれば後からSerilogを追加したとても全く既存コードに影響はない
  • Serilog.Formatting.Compact
    • デフォルトのJsonFormatterでもJSONにはできるがこれを使うとよりコンパクトにできる
  • Serilog.Enrichers.Environment
    • MachineNameを付与できる
      • ライブラリ側でWindowsとその他のOSの差異を吸収している
  • Serilog.Sinks.Async
    • 非同期化
  • Serilog.Settings.Configuration
    • 標準っぽく appsettings.json に設定を書ける
    • タイプセーフに設定を書けたほうがいいので個人的にはこれは使わなくていいと思う
      • NLog で設定ファイル間違えてサイレントにログが出なくなってハマった思い出
      • CI/CDをちゃんとやっているとデプロイのコストは変わらない

リファレンス