ASP.NET Core2のStartup.csでJson.NETの設定を行う

ASP.NET CoreでJson.NET - Newtonsoftの設定をStartup.csに行うには以下の通りConfigureServicesで初期化を行うことができます。

public void ConfigureServices(IServiceCollection services)
        {
             ...
            services.AddMvc().AddJsonOptions(options =>
            {
                options.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
                options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
            });
            ....
}

今回は値が設定されていない項目については出力しないように設定するようにしました。

なお、この設定は各フィールドに属性を以下の通り設定することでも対応できます。

[JsonProperty("property_name", NullValueHandling=NullValueHandling.Ignore)]
public string Hoge{get;set;}

ASP.NET MVCプログラミング入門 (マイクロソフト関連書)

ASP.NET MVCプログラミング入門 (マイクロソフト関連書)

ASP.NET Core2でSerilogを利用する

これまではNLogを利用することが多かったのですが、パフォーマンスやお手軽さからSerilog — simple .NET logging with fully-structured eventsを使用してみようと思います。

前提条件

対象のプロジェクトはASP.NET Core2で作成しました。

設定方法

ASP.NET Core2でserilog/serilog-aspnetcore: Serilog integration for ASP.NET Core 2+の設定は以下の通りとなります。

  1. NuGetパッケージのインストール
  2. Program.csの変更

NuGetパッケージのインストール

NuGetパッケージは以下のものをインストールします。

  • Serilog.AspNetCore -DependencyVersion Highest
  • Serilog.Sinks.Console
  • Serilog.Sinks.RollingFile

インストール後のプロジェクトファイルは以下の通りとなります。

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <Folder Include="wwwroot\" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0" />
    <PackageReference Include="Npgsql" Version="3.2.5" />
    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.0.0" />
    <PackageReference Include="Serilog.AspNetCore" Version="2.0.0" />
    <PackageReference Include="Serilog.Sinks.Console" Version="3.1.1-dev-00757" />
    <PackageReference Include="Serilog.Sinks.RollingFile" Version="3.3.1-dev-00771" />
  </ItemGroup>
  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
    <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" />
  </ItemGroup>
</Project>

Program.csの変更

Program.csは以下の通り変更します。

        public static int Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
                .Enrich.FromLogContext()
                .WriteTo.Console()
                .WriteTo.RollingFile("logs/beagle-customer-{Date}.log")
                .CreateLogger();

            try
            {
                var host = BuildWebHost(args);
                host.Run();

                return 0;
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "Host terminated unexpectedly");
                return 1;
            }
            finally
            {
                Log.CloseAndFlush();
            }
        }

        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
               // ここでログの利用を宣言する
                .UseSerilog()
                .Build();
    }

ポイントはMainメソッド内でLoggerの設定を行い、例外がスローされたときにもログを出力するように設定していることです。

// ロガーの設定を行う
Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
                .Enrich.FromLogContext()
               // コンソールへの出力を行う
                .WriteTo.Console()
               // ファイルへの出力を行う
                .WriteTo.RollingFile("logs/beagle-customer-{Date}.log")
                .CreateLogger();

なお、出力先についてはNuGetで公開されているSink(NuGet Gallery | Packages matching Tags:"serilog")を利用することで柔軟に対応ができます。

設定した内容はUseSerilog()メソッドを設定することで利用可能となります。

public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
               // ここでログの利用を宣言する
                .UseSerilog()
                .Build();

感想

最初はASP.NET Core2についてもよく知らなかったので戸惑うことが多かった(特にテストプロジェクトでの出力とか)ですが、慣れてくるとログの出力先などSinkが色々とあって便利だなと感じました。

参考

Serilog vs NLog Benchmarks

Pro ASP.NET Core MVC 2

Pro ASP.NET Core MVC 2

EF Core2で実行したSQLの引数をログに出力する

EntityFramework Coreで実行したクエリのログを出力する設定は以下の通りDbContextクラスのOnConfiguringをオーバーライドしてoptionsBuilder.EnableSensitiveDataLogging();を設定すればOKです。ただ、SensitiveDataLoggingというパラメーター名からも分かる通りログにパラメターが出力されることがNGなケースもあるのでそのへんは注意が必要ですね。

public class BeagleBaseContext : DbContext
 {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            // この設定により引数のログが出力される
            optionsBuilder.EnableSensitiveDataLogging();
        }
}

出力されるログ

出力されるログは正直あまり使い勝手がいいログではありません。せめてJSONとかならParseし易いのですが、そのままだと以下のように出力されます。

[14:56:28 INF] Executed DbCommand (0ms) [Parameters=[@__get_Item_0='b0d0d15e-8d35-4cf0-afcf-ffe3580ba264' (Size = 36)], CommandType='Text', CommandTimeout='30']
SELECT "e"."id", "e"."application_id", "e"."building_name", "e"."celler_phone_no", "e"."city_name", "e"."code", "e"."contractor_id", "e"."created_at", "e"."display_flg", "e"."email", "e"."fax_no", "e"."location", "e"."name", "e"."name_kana", "e"."phone_no", "e"."pref_code", "e"."street_name", "e"."town_name", "e"."unit_name", "e"."url", "e"."zip_code", "e"."xmin"
FROM "organizations" AS "e"
WHERE "e"."id" = @__get_Item_0
LIMIT 1

おそらく出力処理をカスタマイズできると思いますが、調査してみないとですね。

参考

c# - Lost parameter value during SQL trace in EF Core - Stack Overflow

DbContextOptionsBuilder Class (Microsoft.EntityFrameworkCore) | Microsoft Docs

実戦で役立つ C#プログラミングのイディオム/定石&パターン

実戦で役立つ C#プログラミングのイディオム/定石&パターン