WPF(C#) .NET6.0 で Log4net を実装　

[前回まで](https://qiita.com/noriseto/items/12fc00eff0df9ac11043)で、ざっとWPF アプリを作成したので、今回は Log4net を実装します。

### Log4net インストール
NuGet パッケージマネージャーで Log4net を取得します。
![1.NuGET_Log4net.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184036/01772bc5-4aaa-3745-0371-606614aced0b.png)

### AssemblyInfo.cs に Log4net の定義を追加
:::note info
AssemblyInfo.cs ファイルがない場合は追加しましょう。
[SampleWPF]プロジェクトを右クリック⇒[追加...]⇒[アセンブリ構成ファイル]
:::
- 設定ファイルのパスを指定する。ここではプロジェクト直下の log4net.xmlと指定。

```AssemblyInfo.cs
using System.Windows;

[assembly: ThemeInfo(
    ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
                                     //(used if a resource is not found in the page,
                                     // or application resource dictionaries)
    ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
                                              //(used if a resource is not found in the page,
                                              // app, or any theme specific resource dictionaries)
)]

[assembly: log4net.Config.XmlConfigurator(ConfigFile = @"log4net.xml", Watch = true)]
```

### Log4net 設定ファイルを作成
- log4net.config.xml ファイルを追加する
  [SampleWPF]プロジェクトを右クリック⇒[追加...]⇒[XML ファイル]
![2.Log4net_config.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184036/73c7612f-7f0e-5563-5bc1-e38b83d32df9.png)
以下、ログレベルによる出力ファイルを切り分ける設定のサンプルです。
```log4net.xml
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<log4net>
		<!-- Appender 設定 -->
		<!-- DEBUG level -->
		<appender name="DebugAppender" type="log4net.Appender.RollingFileAppender">
			<param name="File" value=".\logs\DEBUG" />
			<param name="AppendToFile" value="true" />
			<param name="RollingStyle" value="Date" />
			<param name="StaticLogFileName" value="false" />
			<param name="DatePattern" value="_yyyyMMdd'.log'" />
			<layout type="log4net.Layout.PatternLayout">
				<param name="ConversionPattern" value="%date{ISO8601} [%-5level] %message%n" />
			</layout>
			<filter type="log4net.Filter.LevelRangeFilter">
				<levelMin value="DEBUG" />
				<levelMax value="DEBUG" />
			</filter>
		</appender>

		<!-- INFO/WARN level -->
		<appender name="InfoWarnAppender" type="log4net.Appender.RollingFileAppender">
			<param name="File" value=".\logs\INFO" />
			<param name="AppendToFile" value="true" />
			<param name="RollingStyle" value="Date" />
			<param name="StaticLogFileName" value="false" />
			<param name="DatePattern" value="_yyyyMMdd'.log'" />
			<layout type="log4net.Layout.PatternLayout">
				<param name="ConversionPattern" value="%date{ISO8601} [%-5level] %message%n" />
			</layout>
			<filter type="log4net.Filter.LevelRangeFilter">
				<levelMin value="INFO" />
				<levelMax value="WARN" />
			</filter>
		</appender>

		<!-- ERROR/FATAL level -->
		<appender name="ErrorFatalAppender" type="log4net.Appender.RollingFileAppender">
			<param name="File" value=".\logs\ERROR" />
			<param name="AppendToFile" value="true" />
			<param name="RollingStyle" value="Date" />
			<param name="StaticLogFileName" value="false" />
			<param name="DatePattern" value="_yyyyMMdd'.log'" />
			<layout type="log4net.Layout.PatternLayout">
				<param name="ConversionPattern" value="%date{ISO8601} [%-5level] %message%n" />
			</layout>
			<filter type="log4net.Filter.LevelRangeFilter">
				<levelMin value="ERROR" />
				<levelMax value="FATAL" />
			</filter>
		</appender>

		<!--　Appender 指定 -->
		<root>
			<level value="DEBUG" />
			<appender-ref ref="DebugAppender" />
			<appender-ref ref="InfoWarnAppender" />
			<appender-ref ref="ErrorFatalAppender" />
		</root>

	</log4net>
</configuration>
```
- ビルド時にlog4net.xmlを出力するように、プロパティの設定「出力ディレクトリにコピー」を「常にコピーする」に変更
![3.Build_Copy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184036/2cf531d9-2f08-e6fd-9b54-18a1c228826d.png)

### MainWindowVM に実装してみる
- Log4net.ILog を定義

```
 private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
```
- 「CS8603 null 参照の可能性」警告を抑制

```
#pragma warning disable CS8603
```
- テストなので、MainWindowVM コンストラクタで確認

```
// Log4net テスト
logger.Debug("DEBUG level log");
logger.Info("INFO level log");
logger.Warn("WARN level log");
logger.Error("ERROR level log");
logger.Fatal("FATAL level log");
```

以下、MainWindowVM.cs のソースです。

```ViewModels\MainWindowVM.cs
using SampleWPF.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace SampleWPF.ViewModels
{
    public class MainWindowVM    {

#pragma warning disable CS8602 // null 参照の可能性があるものの逆参照です。
        private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
#pragma warning restore CS8602

        public static ObservableCollection<User>? userList { get; set; }
        public ObservableCollection<User> UserList
        {
#pragma warning disable CS8603 // Null 参照戻り値である可能性があります。
            get => userList;
#pragma warning restore CS8603
            set
            {
                userList = value;
            }
        }

        // Insert用データ
        private string insertFirstName;
        public string InsertFirstName
        {
            get => insertFirstName;
            set
            {
                insertFirstName = value;
            }
        }
        private string insertLastName;
        public string InsertLastName
        {
            get => insertLastName;
            set
            {
                insertLastName = value;
            }
        }

        private void UpdateUserList()
        {
            using (var dbcx = new SampleDbContext())
            {
                UserList.Clear();
                ObservableCollection<User> uList = new ObservableCollection<User>(dbcx.Users.ToList());
                foreach (var user in uList) this.UserList.Add(user);
            }

        }

        // DelegateCommand
        public ICommand InsertCommand { get; private set; }
        public void ExecuteInsert()
        {
            using (var dbcx = new SampleDbContext())
            {
                // LocalDB 更新
                User insertUser = new()
                {
                    // Id は LocalDB で自動的に付与される
                    FirstName = InsertFirstName,
                    LastName = InsertLastName
                };
                dbcx.Users.Add(insertUser);
                dbcx.SaveChanges();
            }
            UpdateUserList();

        }

        public ICommand ListTrashCommand { get; private set; }
        public void ExecuteTrash(int id)
        {
            using (var dbcx = new SampleDbContext())
            {
                // LocalDB 更新
                var user = dbcx.Users.Single(x => x.Id == id);
                dbcx.Users.Remove(user);

                dbcx.SaveChanges();
            }
            UpdateUserList();
        }

        public MainWindowVM()
        {
            UserList = new ObservableCollection<User>();

            InsertCommand = new ForwardedCommand(ExecuteInsert);
            ListTrashCommand = new ForwardedCommand<int>(ExecuteTrash);

            // CS8618 対応
            insertFirstName = "";
            insertLastName = "";

            // UserList 初期化
            UpdateUserList();

            // Log4net テスト
            logger.Debug("DEBUG level log");
            logger.Info("INFO level log");
            logger.Warn("WARN level log");
            logger.Error("ERROR level log");
            logger.Fatal("FATAL level log");
        }

    }
}
```
### 実行してログを確認
- 実行ファイルフォルダの logs フォルダに以下のファイルが作成されるはず
![4.Log4net_output.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184036/304efaa9-a2ba-c85e-cbb3-926aa3275cf0.png)
- ログ内容

```DEBUG_20220405.log
2022-04-05 22:24:04,732 [DEBUG] DEBUG level log
```

```ERROR_20220405.log
2022-04-05 22:24:04,741 [ERROR] ERROR level log
2022-04-05 22:24:04,741 [FATAL] FATAL level log
```

```INFO_20220405.log
2022-04-05 22:24:04,740 [INFO ] INFO level log
2022-04-05 22:24:04,741 [WARN ] WARN level log
```
以上で、Log4net の実装を完了します。

### 次回予告
次回は、一通りアプリっぽくなったので、WiX を使って Windows Installer を作成する予定です。