Skip to content

MS_NLog

nishi_74322014 edited this page Sep 1, 2026 · 1 revision

NLog

概要

log4net と I/F レベルで仕様の互換性は高い。

補足(NLog の現況): log4net と比べた
現在の立ち位置を先に示しておく。

log4net NLog Serilog
開発 Apache。一時休止 → 再開 継続的に活発 活発
設計思想 log4j 由来(テキスト ログ) log4j 由来+独自拡張 構造化ログが前提
設定 XML XML / JSON / コード 主にコード
.NET 対応 .NET 8 等に対応 対応が速い 対応が速い
ILogger 統合 あり ありNLog.Extensions.Logging ありSerilog.AspNetCore
新規採用 少ない あり 最も多い
【現在の指針】([.NETのログ](MS_DotNetLogging) と同じ)
   ・アプリのコードは【ILogger<T> に書く】★
   ・NLog / Serilog は【その裏側のプロバイダー】として使う
   → 出力先ライブラリは後から差し替えられる状態にしておく

詳細

log4net との違い

レイアウト(Layout)

log4net と同様に、

  • アペンダが出力するログのフォーマット(ヘッダとボティ)を定義する。
  • 豊富なレイアウト・レンダラーが準備されている。

ターゲット(Target)

log4net の Appender と同様

  • ラッパー・ターゲットというバッファリング・レイヤを挿入できるらしい。

ロガー(Logger)

こちらも、log4net と同様

ログ ヘッダ

log4net と同様にレイアウトで定義。

ログ レベル

log4net と比べると、Trace が増設されている。

補足(ラッパー ターゲットが NLog の特徴): 原文が「らしい」と
書いているラッパー ターゲットは、
NLog を選ぶ実質的な理由の 1 つなので具体化しておく。

【発想】
   Target(出力先)を【別の Target で包む】ことができる
     → デコレーター パターン
     → [エンコーディング](MS_Encoding) の Stream の重ね方と同じ発想 ★
ラッパー 効果
AsyncWrapper 非同期化。呼び出し側を待たせない ★
BufferingWrapper バッファリングしてまとめて書く
RetryingWrapper 失敗したら再試行する
FallbackGroup 失敗したら別の出力先へ(DB → ファイル 等)
AutoFlushWrapper 特定条件(Error 以上)で強制フラッシュ
FilteringWrapper 条件に合うものだけ通す
LimitingWrapper 単位時間あたりの件数を制限(ログの洪水対策)
<!-- 例:非同期+失敗時のフォールバック -->
<target name="db" xsi:type="FallbackGroup">
  <target xsi:type="AsyncWrapper">
    <target xsi:type="Database" connectionString="..." />
  </target>
  <target xsi:type="File" fileName="logs/fallback.log" />
</target>
【これが解決すること】
   [log4net](MS_ApacheLog4net) の AdoNetAppender の問題
     「DB が落ちるとアプリまで落ちる/遅くなる」
       → AsyncWrapper で【業務処理を待たせない】
       → FallbackGroup で【DB が駄目ならファイルへ】★

   ただし、非同期化には代償がある
       → プロセスが強制終了すると【バッファ内のログが失われる】
       → 終了時に LogManager.Shutdown() を呼ぶ
       → 重要なログには AutoFlushWrapper を併用する

**LimitingWrapper(件数制限)**も実務で効く。

【ログの洪水】
   ループの中で例外が出続ける
     → 1 秒間に数万行
     → ディスクが埋まる、他のログが埋もれる、CPU を食う ★
   → LimitingWrapper で上限を設ける

参考

移行メモ(「log4net の開発休止宣言」について): 参考リンクは
2020 年時点の状況を反映している。
Apache log4net に記した通り、
その後 log4net はメンテナンスが再開された

【したがって】
   「log4net は開発が止まったから移行が必要」という
   当時の前提は【現在は当てはまらない】★

【現在、移行を検討する理由があるとすれば】
   ・構造化ログにしたい(Serilog / NLog の JSON 出力)
   ・ILogger 経由に統一したい
   ・ラッパー ターゲットのような機能が欲しい
   ・.NET の新しい版への追随速度

【移行しない判断も妥当】
   ・動いているものを触るリスク
   ・ログの形式が変わると、既存の解析基盤・運用手順に影響する
   → ILogger 経由に寄せておけば、必要になった時点で差し替えられる

ログの設定

ローリング

補足(log4net との設計の違い): 原文が指摘する
**「ターゲットが別にあるのではなく、属性として指定する」**という
違いは重要である。

【log4net】
   FileAppender と RollingFileAppender が【別のクラス】
     → ローリングするかどうかで型を選ぶ

【NLog】
   File ターゲットが 1 つあり、
   【archive* 属性を書けばローリングになる】★
     → 型を変えずに設定だけで切り替えられる
<!-- 日付でアーカイブ(現在の一般的な書き方) -->
<target name="file" xsi:type="File"
        fileName="${basedir}/logs/app.log"
        archiveFileName="${basedir}/logs/archives/app.{#}.log"
        archiveEvery="Day"
        archiveNumbering="Date"
        archiveDateFormat="yyyyMMdd"
        maxArchiveFiles="30"          <!-- ← 保持世代数 ★ -->
        encoding="utf-8"
        concurrentWrites="true"
        keepFileOpen="true"
        layout="${longdate}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}" />

maxArchiveFiles が log4net にない利点である。

【log4net の composite の問題】([Apache log4net](MS_ApacheLog4net) 参照)
   日付+サイズの併用では【古い日付が消えない】= ディスクを食う

【NLog では】
   maxArchiveFiles で【総世代数を制限できる】★
   maxArchiveDays で【保持日数を制限できる】
     → ディスク枯渇を設定だけで防げる

原文の「併用は不可能」について:

【当時の制約】
   fileName に ${shortdate} を入れて日付分割する方式と、
   archiveFileName によるアーカイブ方式は【併用できない】
     → 前者は「その日のファイルに書く」
     → 後者は「書き終えたら別名で退避する」
     → 役割が重複するため

【現在の推奨】
   ・【archive* 系を使う】(世代管理ができるため)★
   ・fileName に日付を埋める方式は、世代管理ができない

【なお、現在の NLog では】
   両者を組み合わせた場合の挙動が改善されているが、
   【意図が読みにくくなる】ため、どちらかに統一するのが無難

性能に関わる属性も押さえておく。

keepFileOpen="true"     … ファイルを開いたままにする【速い】★
concurrentWrites="true" … 複数プロセスからの書き込みを許す
                          ([Apache log4net](MS_ApacheLog4net) の MinimalLock 相当)

【両立させると遅くなる】
   → 複数プロセスから書く必要がないなら
     concurrentWrites="false" + keepFileOpen="true" が最速
   → プロセスごとにファイルを分ける方が根本的
       fileName="logs/app_${processid}.log"

詳細・例

ログ出力方式 (NLog) - Open 棟梁 Wiki(OTR_LoggingNLog.md

補足(ASP.NET Core / ILogger との統合): 原文は
.NET Framework 期の記述だが、現在の使い方を補っておく。

// Program.cs(ASP.NET Core)
using NLog.Web;

var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
builder.Host.UseNLog();          // ← NLog を ILogger の裏側にする ★
// アプリのコードは ILogger<T> のまま(NLog に依存しない)
public class OrderService(ILogger<OrderService> logger)
{
    public void Place(Order o)
        => logger.LogInformation("Order {OrderId} placed by {UserId}", o.Id, o.UserId);
}
<!-- 構造化(JSON)で出す -->
<target name="json" xsi:type="File" fileName="logs/app.json">
  <layout xsi:type="JsonLayout" includeEventProperties="true">
    <attribute name="time"    layout="${date:format=o}" />
    <attribute name="level"   layout="${level}" />
    <attribute name="logger"  layout="${logger}" />
    <attribute name="message" layout="${message}" />
    <attribute name="traceId" layout="${activity:property=TraceId}" />
    <attribute name="exception" layout="${exception:format=tostring}" />
  </layout>
</target>
【JsonLayout の価値】★
   includeEventProperties="true" にすると、
   ILogger のテンプレート引数({OrderId} 等)が
   【個別のフィールドとして出力される】
     → 収集基盤(Elasticsearch / Log Analytics)で
       OrderId による検索・集計ができる
     → [.NETのログ](MS_DotNetLogging) の「構造化ログ」がここで効く
【NLog 使用時の注意】
 ① 【終了時に必ず Shutdown する】★
      LogManager.Shutdown();
      → AsyncWrapper 使用時、バッファ内のログが失われるのを防ぐ
 ② 【起動直後のログ】
      ホストの構築前に例外が出るとログが残らない
      → try/catch で囲み、NLog を先に初期化しておく定石がある
 ③ 【設定の自動リロード】
      <nlog autoReload="true"> で実行中の変更を反映できる
      → log4net の Watch=true 相当

参考

Microsoft Learn


Tags: 移行, プログラミング, .NET開発

NetDevInfraWiki

マイクロソフト系技術情報 Wiki
Open 棟梁 Wiki

(未着手)

開発基盤部会 Wiki

移行管理: DONETODO

Clone this wiki locally