より良いエンジニアを目指して

1日1つ。良くなる!上手くなる!

ログライブラリ「Serilog」〜未だにLog4netしか知らない自分へ

システムを開発する上で共通する課題の割にあまり盛り上がりを見せないのが「ログ」だと思います。

障害調査や利用状況をチェックする上に欠かせない基盤です。

職場であるツールを開発することになって、下記の記事を見ながら、ログどうするっかねーと眺めていたら、「Serilog」というのが登場したのでこちらを使ってみることにしました。

www.buildinsider.net

記事ではETWを推していますが、今回は本格的なシステムではなく、ツール程度なので、Serilogを選んだ次第です。

serilog.net

使って見た感想としては、今回のツールのログとしてのチョイスではlog4netよりベターだったなということ。

著名なライブラリだけあって安心感がある反面、その細かい設定にあります。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <log4net>
    <appender name="DebugLogDailyAppender" type="log4net.Appender.RollingFileAppender">
      <!-- ログ・ファイル名の先頭部分 -->
      <param name="File" value=".\log\Trace_" />
      <!-- ファイル名の日付部分 -->
      <param name="DatePattern" value='yyyyMMdd".xml"' />
      <!-- 日付ごとにファイルを作成することを指定 -->
      <param name="RollingStyle" value="date" />
      <!-- ログ・ファイル名が固定ではないので“false”を指定 -->
      <param name="StaticLogFileName" value="false" />
      <!-- 追加書き込み -->
      <param name="AppendToFile" value="true" />
      
      <!-- 全てのログ・レベルとする -->
      <filter type="log4net.Filter.LevelRangeFilter">
        <param name="LevelMax" value="FATAL" />
        <param name="LevelMin" value="DEBUG" />
      </filter>
      
      <!-- rollingStyle がSizeまたはCompositeの時の最大ファイルサイズ -->
      <param name="MaximumFileSize" value="10MB" />
      <!-- ファイルを切替えるマックス数。ここでは3世代まで管理する -->
      <param name="MaxSizeRollBackups" value="3" />
      
      <!-- ログの出力書式 YALVで解析できるフォーマットを指定 -->
      <layout type="log4net.Layout.XmlLayoutSchemaLog4j">
        <locationInfo value="true"/>
      </layout>
    </appender>
    <root>
      <!-- デバッグ以上のログを出力したい場合 -->
      <level value="Debug" />
      <!-- どのログ出力先を使用するか -->
      <appender-ref ref="DebugLogDailyAppender" />
    </root>
  </log4net>
</configuration>

このようなlog4net向けのxmlを用意するのですが、この設定を準備したり、設定ファイルを読み込む設定を行わなければならなかったり、やや面倒な印象でした。

設定がうまく行かず、ログが出力されなかったり、思うように行かず設定に苦戦するということもありました。

なのでツールが一通り完成してから、ログ機構を決めるようなことをしていたのでした。

Serilogは構造化データを出力するログ機構であるところが魅力なのですが、プログラムで設定出来るところが最初に良いなと感じました。

        public App()
        {
            var log = new LoggerConfiguration().WriteTo.File(@"..\log\log-.txt",
                rollingInterval: RollingInterval.Day,
                rollOnFileSizeLimit: true).CreateLogger();
            log.Information("アプリケーションが起動されました。");
        }

Serilogの構造化データを試してみる

せっかくなのでSerilogらしいところを試すことにします。

stackify.com

普通の人が読めるような形での出力も良いですが、Serilog.Formatting.CompactもNuGetから追加して、CompactJsonFormatterを指定した上でLogのインスタンスを生成します。

            var log = new LoggerConfiguration()
                .WriteTo.Console()
                .WriteTo.File(new CompactJsonFormatter(), @"..\log\log-.txt",
                    rollingInterval: RollingInterval.Day,
                    rollOnFileSizeLimit: true).CreateLogger();
            string message = "message";
            var ex = new Exception();
            log.Information("アプリケーションが起動されました。");
            log.Warning("ワーニング {message}", message);
            log.Error("エラー {ex}", ex);

コンソールでは下記のように出力されます。

[17:27:15 INF] アプリケーションが起動されました。 [17:27:15 WRN] ワーニング message [17:27:15 ERR] エラー System.Exception: 種類 'System.Exception' の例外がスローされました。

ログファイルには以下のようにJson形式となります。

f:id:rimever:20190309173532p:plain

{"@t":"2019-03-09T08:27:15.5060434Z","@mt":"アプリケーションが起動されました。"} {"@t":"2019-03-09T08:27:15.6480452Z","@mt":"ワーニング {message}","@l":"Warning","message":"message"} {"@t":"2019-03-09T08:27:15.8900459Z","@mt":"エラー {ex}","@l":"Error","ex":"System.Exception: 種類 'System.Exception' の例外がスローされました。"}

なんだかElasticSearchに入れられそうな形式。

と思ったら、ElasticSearchに入れるためのライブラリもありますね。

NuGet Gallery | Serilog.Sinks.ElasticSearch 7.1.0