.NET 9 のプロジェクトで「Microsoft.Extensions.Logging.Log4Net.AspNetCore(log4net 用の Logging Provider)」をそのまま使ってよいのか、さらに Debug 実行では動くのに Release 実行/発行では動かないのは相性問題なのか――。本記事では NuGet の読み方から、Release だけで起きやすい落とし穴、実務での切り分け手順までまとめます。
結論:.NET 9 でも「動く可能性は高い」が、“公式サポート”と“動作”は分けて考える
先に結論から言うと、Microsoft.Extensions.Logging.Log4Net.AspNetCore 8.0.0 は主に .NET 8 世代の依存関係を持つ一方で、.NET は後方互換性を強く意識したプラットフォームなので、.NET 9 のアプリで参照しても多くのケースでそのまま動作する可能性が高い、という整理になります。実際に NuGet の詳細では、パッケージに含まれるターゲットフレームワークとして net8.0 を含み、互換として net9.0 が「computed」として表示されます。
ただし注意点として、「動く」と「サポートされている」は同義ではありません。Microsoft のログの公式ドキュメントでも、サードパーティのログプロバイダーは Microsoft ではサポートされない旨が明記されています。つまり、.NET 9 で動いたとしても、トラブル時に Microsoft 公式サポートの範囲外になり得ます。
| 観点 | 何を意味するか | 今回の判断ポイント | 確認方法 |
|---|---|---|---|
| 対象フレームワーク(TFM) | パッケージがビルドして同梱している実体(例:net8.0 / netstandard2.0) | net8.0 を含むなら、net9.0 からは通常“使える側” | NuGet の「Included target framework(s)」を見る |
| 互換性(computed) | NuGet が計算した「この TFM でも参照できるはず」 | net9.0 が computed なら、参照自体は自然 | NuGet の「computed target framework versions」 |
| 公式サポート | 不具合時に “誰が責任を持って面倒を見るか” | このパッケージは OSS(メンテナ)側の領域になりがち | NuGet のサポート表記や公式ドキュメントの注意書き |
| 実運用での安定性 | Release 発行、トリミング、単一ファイル化などを含めた現実の動き | Debug で動いても Release で破綻することがある | Release での実行・発行で検証し、差分を潰す |
まず確認すべき:NuGet で「何向けに作られているか」を読む
「.NET 9 で使えるか?」を短時間で判断するなら、まず NuGet の詳細ページを確認します。Microsoft.Extensions.Logging.Log4Net.AspNetCore 8.0.0 は net8.0 を含む複数の TFM を同梱し、依存関係として log4net(2.0.15 以上)や Microsoft.Extensions.Logging 8.0.0 以上などを持ちます。
また、バージョン履歴から見ると、現状の最新版は 8.0.0 で、公開時期は 2023 年 12 月です(= 少なくとも “net9.0 を名乗る” 更新は入っていない)。この点は「動く可能性」とは別に、運用観点で気にしておく価値があります。
「.NET 8 向け」と「.NET 9 で使える」は矛盾しない
NuGet で “net8.0 が入っている” = “.NET 8 時代に合わせて作られている” という意味ですが、.NET 9 のアプリは通常 net8.0 向けのライブラリを読み込めます(もちろん 100% ではなく、破壊的変更が絡むと別)。Microsoft も .NET 9 への移行に伴う破壊的変更をまとめており、移行時は breaking changes を確認すべきという立て付けになっています。
つまり、判断の基本は次の 2 段階です。
- 参照できるか(TFM/互換)
- 実行形態(Release/発行/トリミング)まで含めて安定して動くか
.NET 9 での最小構成:Program.cs と log4net.config
このパッケージは Microsoft.Extensions.Logging のプロバイダーとして log4net をぶら下げる形です。リポジトリの README でも、.NET 6 以降の最小ホスティングモデルでは builder.Logging.AddLog4Net(); を Program.cs に追加する形が案内されています。
Program.cs(Minimal hosting の例)
using Microsoft.Extensions.Logging;
var builder = WebApplication.CreateBuilder(args);
// 既定のログ設定は残してもいいですが、まず切り分けでは「何が出ているか」を明確にするために
// Console を残した上で Log4Net を足す、という形が扱いやすいです。
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
// log4net provider を追加(log4net.config を読みます)
builder.Logging.AddLog4Net();
var app = builder.Build();
app.MapGet("/", (ILogger logger) =>
{
logger.LogInformation("Hello from .NET 9 + log4net provider");
return "OK";
});
app.Run();
log4net.config(まずは動作確認用)
README には DebugAppender を使う最小例が載っています。これは「デバッグ出力」に流れるため、デバッグなし実行だと“出ていないように見える”代表例です。後述の Release 差分の落とし穴にも直結するので、検証では便利ですが、本番の出力先としてはファイルや外部ストアを用意するのが無難です。
<?xml version="1.0" encoding="utf-8" ?>
<log4net>
<appender name="DebugAppender" type="log4net.Appender.DebugAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
</layout>
</appender>
<root>
<level value="ALL" />
<appender-ref ref="DebugAppender" />
</root>
</log4net>
「Debug だと動くのに Release だと動かない」現象は、互換性より“実行形態の差”が原因になりやすい
質問でよく出てくるのが、デバッグ実行だと動くのに、デバッグなし(≒Release実行/発行)だと動かないというパターンです。この現象は「.NET 9 未サポートだから」だけで説明できるケースは少なく、実務では次のような差分が原因であることが多いです。
| Release で変わる要素 | 起こりがちな症状 | 対策の方向性 |
|---|---|---|
| 環境名(Development → Production) | ログレベルが上がり、Debug/Trace が出ない | appsettings.* の Logging 設定を見直す |
| 出力先(Debug 出力が見えない) | 「ログが出ない」と誤認する | ファイル/コンソール/外部サービスなど“見える”出力先に切替 |
| 発行時のファイル配置 | log4net.config が見つからず設定が読み込まれない/起動時に例外 | CopyToPublishDirectory の設定を追加 |
| トリミング(PublishTrimmed) | 反射依存のコードが削られて動作不良/一部機能だけ欠ける | まず trimming を無効化して原因切り分け、必要なら保持設定 |
| 最適化・AOT・Linker(特に MAUI) | Debug では問題ないのに Release で落ちる | MAUI の発行設定・Linker 設定も含めて検証 |
落とし穴:DebugAppender を使っていると「デバッグなし」ではログが見えない
log4net.config の例に出てくる DebugAppender は、名前の通りデバッグ出力に向きます。Visual Studio で F5 実行していると Output ウィンドウに出ますが、Ctrl+F5(デバッグなし)や発行物の実行では、出力を見失いやすいです。まず「Release で動かない」なのか「動いているがログを見ていないだけ」なのかを切り分けるため、次のいずれかに変更して確認するのがおすすめです。
- RollingFileAppender などでファイルに出す
- ConsoleAppender を追加してコンソールに出す(Web/サービスでは見え方に注意)
- 開発中は Console ロガーも併用し、ログが出る経路を 2 本持つ
落とし穴:Release(Production)ではログレベル設定が変わる
ASP.NET Core の標準テンプレートでは、環境ごとに appsettings.{Environment}.json の Logging 設定が変わります。Microsoft のドキュメントでも、Logging は設定(Configuration)から制御するのが基本であることが説明されています。Debug 実行時だけ appsettings.Development.json が適用され、Release 実行時は appsettings.json(または Production)が適用される、という差分が起こり得ます。
例えば「Debug では LogDebug が出るのに Release では出ない」なら、アプリ側のフィルターが Information 以上になっている可能性があります。log4net 側で ALL にしていても、Microsoft.Extensions.Logging 側のフィルターで落とされることがあるので、まずは appsettings のログレベルを確認します。
落とし穴:log4net.config が発行先にコピーされていない
Debug 実行では bin\Debug\net9.0\ などに手元でファイルが置かれているのに、発行(publish)先には log4net.config が入っていない、というのは非常に多いパターンです。log4net の設定は外部ファイルなので、配置が崩れると当然動作も変わります。
プロジェクトファイル(.csproj)に次のように追記すると、ビルド出力と発行出力の両方に確実に含められます。
<ItemGroup>
<None Update="log4net.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
</ItemGroup>
加えて、log4net.config の中でファイル出力をしている場合は、フォルダが存在するか/書き込み権限があるかも確認してください。Docker、Windows サービス、MAUI など実行環境が変わると、相対パスや書き込み先は簡単に変わります。
落とし穴:PublishTrimmed(トリミング)で挙動が変わる
Release での発行設定によっては、アプリサイズ削減のためにトリミング(ILLink)が有効になっていることがあります。Microsoft のドキュメントでも、<PublishTrimmed>true</PublishTrimmed> を指定するとトリミングが有効になり、デバッグ体験が変わったり、最終成果物で追加のバグに遭遇し得ることが注意されています。
log4net や一部の Logging Provider は、設定ファイルやリフレクションを経由する都合で「静的解析だけでは使う型が特定しにくい」ことがあります。トリミングを疑う場合は、まず次の順番で切り分けると安全です。
- いったん
<PublishTrimmed>false</PublishTrimmed>で発行し、問題が消えるか確認する - 消えるなら、保持したいアセンブリや型の指定(TrimmerRootAssembly 等)を検討する
- それでも難しいなら、トリミング前提の設計(別ロガーへ移行等)を検討する
推奨:ログ実装は Microsoft.Extensions.Logging の標準に寄せると .NET 9 でも破綻しにくい
「log4net を使う/使わない」と「アプリのログ設計」は分けて考えるのがコツです。アプリ側は ILogger<T>(Microsoft.Extensions.Logging)に寄せ、実装や出力先はプロバイダーの差し替えで吸収できる構造にしておくと、.NET 9 以降でも破綻しにくくなります。Microsoft のドキュメントでも、プロバイダー追加は基本的に NuGet を入れて拡張メソッドで追加する形で説明されています。
具体的には次の点を意識します。
- アプリコードは
ILoggerだけに依存し、log4net 固有 API(ILog など)を直接呼ばない - ログレベルは appsettings(Configuration)で制御し、環境差分を明示する
- プロバイダーは “差し替え可能” として登録し、障害時に切り戻せるようにする
環境別 appsettings の例(Release で Debug を落とさない)
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
この設定にしてもログが出ない場合は、log4net.config 側の <root> レベルや appender の設定、そして「出力先が見えているか」を改めて確認します。
リポジトリ更新が止まっている場合の現実解:フォークして net9.0 を追加して検証する
Microsoft Q&A では、パッケージは .NET 8 をサポートし、一般に .NET 9 は後方互換という整理が示されています。
一方で別回答として、リポジトリの更新が活発でない場合は「動くはずだが、動かなければ自分で clone して net9 ターゲットを追加してビルドし、可能なら PR を投げる」という現実的な提案も出ています。
ここで重要なのは、“net9.0 を名乗っていない”=即 NG ではないことです。特にこのパッケージは netstandard2.0/2.1 も含むため、.NET 9 で動く素地はあります。とはいえ運用では「いつ更新されるか分からない依存」を抱えるリスクもあるので、次のように方針を決めると迷いが減ります。
| 状況 | おすすめ対応 | 狙い |
|---|---|---|
| 今のところ動いている | 固定バージョンで継続、Release 発行も含めた回帰テストを用意 | “動いている”状態を守る |
| Release/発行でだけ問題が出る | まず設定・ファイル配置・ログレベル・トリミングを疑って切り分け | 原因の多くは互換性以外 |
| .NET 9 特有の破壊的変更に当たった | フォークして修正、または別ロガーへの移行を検討 | 長期的な保守性を確保 |
それでも解決しないとき:根本原因が「ログ対応可否」ではない可能性を疑う
Debug/Release の差分はログ以外にも無数にあります。特に .NET MAUI を含むケースでは、Linker/AOT、プラットフォームごとのファイルアクセス、スレッドやタイミングの違いで「現象がログに見える」ことがあります。
Microsoft Q&A のスレッドでも、複数のスレッドにまたがって情報が散らばっていてフォーラムでは追いきれないとして、MAUI サポート(サポートチケット)に誘導されています。ログ周りは症状の一部で、根本原因が別にある可能性が高い、というニュアンスです。
「ログが出ない」だけでなく「アプリが動かない/落ちる」場合は、次の情報を揃えると原因特定が速くなります。
- Release 実行時の例外ログ(標準出力、イベントログ、クラッシュレポート)
- 発行設定(self-contained / trimming / single-file / ReadyToRun / AOT の有無)
- 実行ユーザー権限、書き込み先フォルダ、作業ディレクトリ
- appsettings の環境別差分(Development/Production)
- log4net.config の配置場所と、パスが相対/絶対どちらか
まとめ:まずは NuGet を確認し、Release 差分(配置・ログレベル・出力先・トリミング)を潰す
- Microsoft.Extensions.Logging.Log4Net.AspNetCore 8.0.0 は net8.0 を含み、net9.0 互換として表示されるため、.NET 9 でも“動く可能性は高い”
- ただし第三者プロバイダーは Microsoft の公式サポート対象外になり得るため、運用では自己防衛(回帰テスト・切り戻し手段)が重要
- Debug と Release の差は、未サポート問題よりも「DebugAppender」「ログレベル」「設定ファイル配置」「PublishTrimmed」などの要因が本命になりやすい
- 更新が止まり気味なら、フォークして対応するか、Microsoft.Extensions.Logging に寄せた設計のまま他ロガーへ移行できる状態にしておく
- どうしても追いきれない場合は、ログ以外(特に MAUI の発行/実行差分)も疑い、サポートチケットも視野に入れる

コメント