Skip to content

MS_ApacheLog4net

nishi_74322014 edited this page Sep 1, 2026 · 1 revision

Apache log4net

概要

Apache log4net について。

補足(現況:メンテナンス モードから復帰した): 本ページを読む前に、
log4net の現在の立ち位置を押さえておく。

【経緯】
   2004頃   Java の log4j から移植。.NET のログの事実上の標準に
   2010年代 更新が滞る(2.0.8 が長く続いた)
   2020     【開発休止が示唆され、NLog 等への移行が進む】★
             ([NLog](MS_NLog) の参考リンクがその時期のもの)
   2021     Log4Shell(log4j 2 の重大な脆弱性)で注目が集まる
             ※ log4net は【影響を受けなかった】(別実装のため)
   2022~   【メンテナンスが再開】。2.0.15 以降が継続的に更新
   現在     3.x 系がリリースされ、.NET 8 等に対応

とはいえ、新規採用の第一候補ではない

【現在の選択】
   新規      → 【ILogger(Microsoft.Extensions.Logging)】★
                + Serilog / NLog をプロバイダーとして
   既存資産  → log4net のまま保守(無理に移行しない)
                → ILogger 経由に寄せると、後で差し替えられる

 → [.NETのログ](MS_DotNetLogging) 参照

本ページの価値は、概念(レイアウト/アペンダ/ロガー)の解説にある。
この 3 分割は NLog にも Serilog にも共通するため、
1 つ理解すれば他にも通じる。

詳細

コンポーネント

移行メモ(原文の綴り): 原文は「lon4net」となっている箇所が
複数あるが、正しくは log4net である(本ページでは修正した)。

レイアウト(Layout)

アペンダが出力するログのフォーマットを定義する。

アペンダ(Appender)

  • 具体的な出力処理を行う。
  • 出力先毎にアペンダの種類が存在する。
  • アペンダの種類毎に設定可能な項目が異なる。
  • 出力先、ローリング設定等を定義する。

ロガー(Logger)

  • 論理的なログファイル名
  • アペンダをロガーで束ねると複数の出力先へ出力できる。
  • ログレベル毎に Filter することができる。
  • ルートロガーとロガーがあり階層構造をとる。

補足(3 分割は現在も共通の設計である): この構造は
ログ ライブラリ全般に共通するため、対応表を示しておく。

概念 log4net NLog Serilog ILogger(標準)
何を書くか(書式) Layout Layout Formatter / Template (プロバイダー依存)
どこへ書くか(出力先) Appender Target Sink Provider
誰が書くか(分類・絞り込み) Logger Logger Logger カテゴリ(ILogger<T>
【階層構造の意味】★ これが最も実用的な機能

   ロガー名を【名前空間に合わせる】のが慣習
     MyApp
     MyApp.Data
     MyApp.Data.OrderRepository

   設定は【上位から継承される】
     <root level="INFO">                    ← 既定は INFO
     <logger name="MyApp.Data" level="DEBUG"> ← Data 配下だけ DEBUG

   → 【障害調査のとき、特定の層だけ詳細ログにできる】★
     本番でも全体を DEBUG にせずに済む
// ロガー名を型から取るのが定石(log4net)
private static readonly ILog Log =
    LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
// ILogger(現在)では、型パラメータがそのままカテゴリになる ★
public class OrderRepository(ILogger<OrderRepository> logger) { }
//   → カテゴリは "MyApp.Data.OrderRepository"
//   → appsettings.json の "Logging:LogLevel" で同じ階層制御ができる

アペンダ種類

アペンダには以下のような種類がある。

ファイル

  • .FileAppender
    テキストファイル
  • .RollingFileAppender
    ローリング・テキストファイル

コンソール

  • .ConsoleAppender
  • .ColoredConsoleAppender
  • .AnsiColorTerminalAppender

イベントログ

  • .EventLogAppender

DBMS

  • .AdoNetAppender
  • .AdoNetAppenderParameter

ネットワーク

  • .UdpAppender
  • .NetSendAppender
  • .TelnetAppender
  • .SmtpAppender
  • .SmtpPickupDirAppender
  • .RemotingAppender

TraceListener

  • .DebugAppender
    System.Diagnostics.Debug system
  • .TraceAppender
    System.Diagnostics.Trace system
  • .AspNetTraceAppender
    ASP.NET TraceContext

Syslog(LinuxおよびUNIX)

  • .LocalSyslogAppender
  • .RemoteSyslogAppender

その他

  • .TextWriterAppender
    .TextWriter クラス

  • .OutputDebugStringAppender
    OutputDebugString Win32API

  • .MemoryAppender

  • .AppenderCollection

  • .ForwardingAppender

  • .BufferingForwardingAppender

補足(アペンダの選び方と、避けるべきもの): 種類は多いが、
実務で使うのはごく一部である。

アペンダ 評価
RollingFileAppender 最も使われる。オンプレ/VM 環境の基本 ★
ConsoleAppender コンテナでは必須(後述)
EventLogAppender 少量の重要イベントのみ.NETのログ
AdoNetAppender 推奨しない(後述)
SmtpAppender 推奨しない(後述)
UdpAppender / RemoteSyslogAppender ログ集約基盤へ送る場合
RemotingAppender .NET Remoting は廃止済み。使わない
TelnetAppender / NetSendAppender 歴史的
【AdoNetAppender を推奨しない理由】★
   ・ログ出力のたびに【DB へ書き込む】
      → DB が遅い/落ちると、アプリまで遅くなる/落ちる
      → 「ログを出すために業務処理が止まる」本末転倒
   ・DB 障害の調査ログが DB に書けない(一番必要なときに使えない)
   ・トランザクションとの相互作用が読みにくい

  【代替】 ファイルに出し、収集基盤が取り込む
           ([ログ収集いろいろ](MS_LogCollection))

【SmtpAppender を推奨しない理由】
   ・障害時に【大量のメールが飛ぶ】(メールストーム)★
   ・SMTP が遅いとアプリが詰まる
   【代替】 監視基盤(アラート ルール)で通知する
            → ログ出力と通知は【責務を分ける】
【コンテナ環境での定石】★
   ・ファイルではなく【標準出力(Console)に出す】
      → コンテナのログ ドライバが回収する
      → Kubernetes / Docker の標準的な流儀
   ・ファイルに出すと、
      ・コンテナが消えるとログも消える
      ・ローリングの管理が二重になる
      ・ボリュームのマウントが必要になる

その他

ログ ヘッダ

定義ファイルでレイアウトを定義することにより、
アペンダ毎、ログ ヘッダを設定できる。

(例)

↓時間               ↓レベル ↓スレッドID ↓メッセージ
[2007/10/25 15:22:21,750], [DEBUG], [9], 任意のメッセージ

補足(ログに含めるべき項目): 例に挙がっている
時刻・レベル・スレッド ID・メッセージは最低限であり、
現在はさらに項目が要る

項目 理由
時刻 タイムゾーンを明示yyyy-MM-ddTHH:mm:ss.fffzzz)★
レベル 絞り込みの基本
ロガー名(カテゴリ) どの層のログか。原文の例には入っていない
スレッド ID 並行処理の追跡(ただし後述)
相関 ID / TraceId 1 リクエストを串刺しで追う
ユーザー ID / テナント ID 誰の操作か(個人情報に注意
マシン名 / インスタンス ID 複数台構成での特定
【スレッド ID の限界】★
   async/await では【await の前後でスレッドが変わる】
     → スレッド ID では 1 つの処理を追えない
     → [async/await](MS_AsyncAwait) 参照

   【現在の解】 相関 ID(TraceId)を使う
     ・ASP.NET Core は Activity.Current.Id を持っている
     ・W3C Trace Context(traceparent ヘッダ)で
       【サービスをまたいで引き継がれる】
<!-- log4net で相関 ID を出す(%property を使う) -->
<param name="ConversionPattern"
       value="%date{yyyy-MM-ddTHH:mm:ss.fffzzz} [%-5level] [%logger] [%property{TraceId}] %message%newline" />
// 値を積む(LogicalThreadContext は async でも引き継がれる)
log4net.LogicalThreadContext.Properties["TraceId"] = Activity.Current?.TraceId.ToString();
【ThreadContext と LogicalThreadContext の違い】★
   ThreadContext        … スレッド単位。【async をまたぐと失われる】
   LogicalThreadContext … 論理的な呼び出しコンテキスト単位。引き継がれる
     → 非同期処理があるなら【必ず LogicalThreadContext】

時刻のタイムゾーンも重要である。

【ローカル時刻だけで記録すると】
   ・サーバが海外にある/UTC 設定だと、時差で混乱する
   ・サマータイムで【同じ時刻が 2 回現れる】
      → [国際化対応項目](MS_InternationalizationItems) 参照
   【対策】 オフセット付き(ISO 8601)で出す。または UTC で統一する

ログ レベル

定義ファイルでロガー(Logger)を定義することにより、
出力するログ レベルのフィルタを設定できる。

ログ レベルには次の5つのレベルがあり、
ロガー(Logger)のログ出力 API を使い分ける。

レベル 説明
Fatal システム停止するような致命的な障害
Error システム停止はしないが、問題となる障害
Warn 障害ではない注意警告
Info 操作ログなどの情報
Debug 開発用のデバッグメッセージ

補足(レベルの対応表): .NETのログ で挙げた
ILogger のレベルとの対応を示しておく。

log4net NLog ILogger(標準)
Trace Trace
Debug Debug Debug
Info Info Information
Warn Warn Warning
Error Error Error
Fatal Fatal Critical
【log4net には Trace がない】
   → [NLog](MS_NLog) が「Traceが増設されている」と述べている点
   → ILogger には Trace / None がある

レベルの使い分けの原則.NETのログ と共通):

【判断基準】
   Fatal/Critical … 【即座に人を起こす】必要があるか?
   Error          … 【調査が必要】か?(アラートの対象)
   Warn           … 続いたら問題になるか?(傾向を見る)
   Info           … 【平常時に残す】業務の節目
   Debug/Trace    … 【本番では出さない】

【よくある誤り】
   ・何でも Error にする → アラートが鳴りっぱなしで無視される ★
   ・想定内の例外を Error にする(入力エラー等)→ Warn か Info
   ・本番で Debug を出しっぱなし → ディスク圧迫、性能低下、情報漏洩

ログの設定

ローリング

  • 既定(日付でローリング、バックアップ数管理無し)
<!-- ローリング・ログファイル出力用アペンダ -->
<appender name="ACCESS" type="log4net.Appender.RollingFileAppender">
    <param name="File" value="C:\root\files\resource\Log\ACCESS" />
    <!-- ローリングの設定 -->
    <param name="StaticLogFileName" value="false" />
    <param name="RollingStyle" value="date " />
    <param name="DatePattern" value='"."yyyy-MM-dd".log"' />
    <!-- 書き込み時の設定(追加 or 上書き、出力エンコーディング) -->
    <param name="AppendToFile" value="true" />
    <encoding value="utf-8" />
    <!-- メッセージのフォーマット -->
    <layout type="log4net.Layout.PatternLayout">
        <param name="ConversionPattern" value="[%date{yyyy/MM/dd HH:mm:ss,fff}],[%-5level],[%thread],%message%newline" />
    </layout>
    <!-- フィルタ(範囲)の設定 -->
    <filter type="log4net.Filter.LevelRangeFilter">
        <levelMin value="DEBUG" />
        <levelMax value="FATAL" />
    </filter>
</appender>
  • バックアップ数が固定となるローリング
    指定のサイズを超えている場合にローリングを行う。
    ファイル サイズは必ず、この設定値未満になるわけではない。
<!-- ローリングの設定-->
<param name="StaticLogFileName" value="true" />
<param name="RollingStyle" value="size" />
<param name="MaximumFileSize" value="10MB" />
<param name="MaxSizeRollBackups" value="2" />
<param name="CountDirection" value="-1" />
  • 付与される番号の順番は、CountDirection パラメタ値により制御する。

  • サイズ 10MB(MaximumFileSize)毎にローリングし、
    下記のように、2 つ (MaxSizeRollBackups) のバックアップを保持する 。

    • (ログファイル名)   → 現在出力中のログ
    • (ログファイル名).1  → 過去のログバックアップ(古い)
    • (ログファイル名).2  → 過去のログバックアップ(最も古い)
  • 日付とサイズを合わせたローリング

<!-- ローリングの設定 -->
<param name="StaticLogFileName" value="false" />
<param name="RollingStyle" value="composite" />
<param name="DatePattern" value='"."yyyy"-"MM"-"dd".log"' />
<param name="MaximumFileSize" value="10MB" />
<param name="MaxSizeRollBackups" value="10" />
<param name="CountDirection" value="-1" />
  • MaxSizeRollBackups パラメタ値は、サイズによるローリングにのみ適用される。
  • 日付とサイズを合わせたローリング(composite)では、
    ログファイル数を一定数に保つ役割を果たさない。

補足(この節が本ページで最も価値がある): ローリングの落とし穴
実際に検証して記録している点が有用である。要点を整理・補強する。

① 原文が指摘する 2 つの落とし穴(どちらも実際に起きる)

【落とし穴 1】「ファイル サイズは必ず設定値未満になるわけではない」
   ・log4net は【1 行を書き終えてからサイズを判定する】
   ・10MB を超えた「後」にローリングする
     → 10MB ちょうどには収まらない
     → ディスク容量の見積りでは【余裕を持つ】★

【落とし穴 2】「composite ではファイル数を一定に保てない」★
   ・MaxSizeRollBackups は【同一日付内】のバックアップ数にしか効かない
   ・日付が変わると新しい系列が始まり、【古い日付は消えない】
     → 放置すると【ディスクを食い潰す】
     → 実際にディスク枯渇の障害原因になる

② composite を使う場合の対策

・OS 側で古いファイルを削除する(タスク スケジューラ / cron)★
・ログ収集基盤に転送し、ローカルは短期保持にする
・監視で【ディスク使用率にアラート】を設定する
・そもそも【日付のみ】または【サイズのみ】にする

CountDirection の意味(分かりにくいので明記)

CountDirection = -1(既定)
   ログ.1 が【最新のバックアップ】、番号が大きいほど古い
   → ローリングのたびに【全ファイルの名前を変える】(.1→.2、.2→.3)
   → ファイル数が多いと【リネームのコストが増える】

CountDirection = 1 以上
   番号が大きいほど新しい(インクリメンタル)
   → リネームが発生しない= 速い ★
   → ただし「.1 が最新」という直感には反する

④ 複数プロセスからの書き込み(原文にない重要な点)

【問題】
   IIS のワーカー プロセスが複数(Web ガーデン)
   あるいは複数のサービスが【同じログ ファイルに書く】
     → 既定ではファイルがロックされ、
       後発のプロセスが【ログを出せない】★

【対策】
   <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
     → 書き込みのたびに開閉する(安全だが【遅い】)

   あるいは【プロセスごとにファイルを分ける】(推奨)
     <file type="log4net.Util.PatternString"
           value="Log\app_%processid.log" />

⑤ 文字コード

<encoding value="utf-8" />
   → 原文の設定は正しい
   → 【BOM が付かない】ので、grep 等での扱いも良い
   → 指定しないと環境依存(CP932)になり、
     Linux で収集した際に化ける
     ([エンコーディング](MS_Encoding) 参照)

⑥ 設定ファイルの読み込み

// AssemblyInfo.cs に書くのが伝統的なやり方
[assembly: log4net.Config.XmlConfigurator(
    ConfigFile = "log4net.config", Watch = true)]
//                                  ↑ 【実行中の設定変更を反映】★
【Watch = true の価値】
   本番で「調査のため一時的に DEBUG にしたい」場合、
   【再起動せずに】設定ファイルを書き換えるだけで反映される
     → 障害調査で極めて有用

【注意】 .NET Core 以降は AssemblyInfo の属性が使いにくいため、
         起動時にコードで読み込む方式が一般的
           var repo = LogManager.GetRepository(Assembly.GetEntryAssembly());
           XmlConfigurator.ConfigureAndWatch(repo, new FileInfo("log4net.config"));

詳細・例

ログ出力方式 (log4net) - Open 棟梁 Wiki(OTR_LoggingLog4net.md

参考

Microsoft Learn


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

NetDevInfraWiki

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

(未着手)

開発基盤部会 Wiki

移行管理: DONETODO

Clone this wiki locally