ASP.NET CoreでNLogを設定する(EF Migration付き)

#ASP.NET CoreでNLogを設定する

## 背景
.NETCoreに触れる機会があったので、
EntityFrameworkを使用したログDBのMigrationを含めた
NLogのDB出力設定サンプルを製造してみました。
(とかかっこつけながら、備忘録として記録します)　φ(-ω-)ﾒﾓﾒﾓ

## 環境
- TargetFramework
  - NetCore2.0
- ライブラリ  

|ライブラリ名|バージョン|  
|-----------|---------|  
|Microsoft.AspNetCore.All|2.0.3|
|NLog.Web.AspNetCore|4.5.0-rc2|
|Swashbuckle.AspNetCore|1.1.0|
|Microsoft.EntityFrameworkCore.Tools.DotNet|2.0.0|

## ログDBのMigration
サーバ端末が変更される度にログのDB環境を構築するのは面倒なので、
EF Coreのマイグレーション機能を使いサーバ起動時に 
ログDBを自動でマイグレーションするよう設定した。

```c#:LogDbContext.cs
    public class LogDbContext : DbContext
    {

        public LogDbContext(DbContextOptions<LogDbContext> options)
        {
            // DBがなかった場合にマイグレーションする
            Database.EnsureCreated();
        }
```
しかし、これだと以下の点が問題になりました。 
> 1. マイグレーション履歴テーブルができずEntity変更履歴が取れないので、DB更新ができない *1
> 2. DBContextがDIされたときにマイグレーションされるので、LOGDBのDBContextを使用するAPIを呼び出さないとDBができない  

サーバ起動時にはDBが出来ていてほしいのでスタートアップでマイグレーション処理を呼ぶように変更する。 


```c#:Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        { 

            // Swaggerミドルウェアの登録
            app.UseSwagger();
            // SwaggerUIミドルウェアの登録
            app.UseSwaggerUI(option =>
            {
                option.SwaggerEndpoint("/swagger/orders/swagger.json", "Order APIs sandbox.");
            });
            loggerFactory.AddNLog();
            // DBマイグレーション処理
            DbInitailizer.InitializeDatabase(app);
            LogManager.Configuration.Variables["ConnectionStrings"] = Configuration.GetConnectionString("NLogDb");
            
            app.UseMvc();
		}
 ```

DB初期設定クラス

```c#:DbInitailizer.cs
using AspNetCoreNlog.Model;
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System.Linq;

namespace AspNetCoreNlog
{
    public static class DbInitailizer
    {
        public static void InitializeDatabase(IApplicationBuilder app)
        {
            using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
            {

                var logDbContext = serviceScope.ServiceProvider.GetRequiredService<LogDbContext>();
                logDbContext.Database.Migrate();

            }
        }
    }
}
```

## Nlog設定
"Program.cs"にて、
"Nlog.conf"(ログ設定ファイル)を読込みます。
＊[NetCore2.0で読込み方法が代わりました。](https://github.com/NLog/NLog.Web/wiki/Getting-started-with-ASP.NET-Core-2#4-update-programcs)

```c#:Program.cs
    public class Program
    {
        public static void Main(string[] args)
        {
            
            var logger = NLogBuilder.ConfigureNLog("NLog.config").GetCurrentClassLogger();
          
            try
            {
                logger.Debug("init main");
                BuildWebHost(args).Run();
            }
            catch (Exception e)
            {
                logger.Error(e, "Stopped program because of exception");
                throw;
            }
        }
```
*"Nlog.conf"*はこんな感じ

```xml:NLog.config
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true"
      internalLogLevel="Warn"
      internalLogFile="Logs\internal-nlog.txt">

  <extensions>
    <add assembly="NLog.Web.AspNetCore"/>
  </extensions>

  <targets>
    <target name="database" xsi:type="Database" connectionString="${var:ConnectionStrings}" >
      <!--LogDB用インサート文-->
      <commandText>
        insert into Logs (
        Application, Logged, Level, Message,
        Logger, CallSite, Exception, KBN
        ) values (
        @Application, @Logged, @Level, @Message,
        @Logger, @Callsite, @Exception, @kbn
        );
      </commandText>
      <!--[レイアウトレンダラー一覧](https://github.com/NLog/NLog/wiki/Layout-Renderers) -->
      <parameter name="@application" layout="AspNetCoreNlog" />
      <parameter name="@logged" layout="${date}" />
      <parameter name="@level" layout="${level}" />
      <parameter name="@message" layout="url: ${aspnet-request-url} | action: ${aspnet-mvc-action} | ${message}" />
      <parameter name="@logger" layout="${logger}" />
      <parameter name="@callSite" layout="${callsite:filename=true}" />
      <parameter name="@exception" layout="${exception:tostring}" />
      <parameter name="@kbn" layout="${mdlc:item=kbn}" />
    </target>

  </targets>

  <rules>
    <logger name="*" minlevel="Trace" writeTo="database" />
  </rules>
</nlog>
```
DB接続先は、appsettings.jsonに持つようにしています。

```json:appsettings.json
{
  "ConnectionStrings": {
    "NLogDb": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=logdb1;Integrated Security=True;Connect Timeout=1;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"
    //"NLogDb": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=logdb;"
  }
}

```


実行するとDBがマイグレーションされます。
![Debug.PNG](https://qiita-image-store.s3.amazonaws.com/0/149822/7dfaf3d0-4e87-cd1f-e10b-802c953ed607.png)

DBが勝手にできた！v(｀皿´)ﾉ~'' ﾔｯﾀｰ!!

[ソースコード](https://github.com/GCnaoto/NetCoreNlog)を公開しているので、 
参考になればと思います。

次は、ロガーでのログ出力時にパラメータを渡してDBに登録する方法とか書ければなぁ
あと、EFの発行クエリをパラメータ代入してログに出力する方法とか書ければなぁ

*1: どうもマイグレーションコマンドを実行しマイグレーションデザインを作成するのではなく、
  [OnModelCreatingメソッド](https://msdn.microsoft.com/ja-jp/library/gg679267(v=vs.113).aspx)が呼ばれるっぽい


参考資料： 
- ASP.NET Coreの Nlogのスタートアップ
https://github.com/NLog/NLog.Web/wiki/Getting-started-with-ASP.NET-Core-2