あるSEのつぶやき・改

ITやシステム開発などの技術に関する話題を、取り上げたりしています。

.NET Core, コンソールアプリでNLogを使ってログを出力する

はじめに

技術調査の段階では軽視されがちなログ出力ですが、実運用となるとログがなければ問題解決の話にならないこの現実は厳しいなと思う今日この頃、みなさまにおかれましてはログをどのように扱っているでしょうか?

さて、ちょっと変わった出だしですが大事ですよね。ログ。

では、.NET Core でログ出力をどのようにすればよいのかですが、いくつか候補はあるものの最近は NLog が人気があるようです。ですので、この記事では NLog でどのようにログ出力すればよいのかをご紹介します。

なお、環境は、Mac (macOS High Sierra 10.13.3) で .NET Core SDK 2.1.4、Visual Studio Code になります。

とは言っても、Windowsでもパスを読み替えれば使えると思います。もともと Windows のツールですので。

コンソールアプリケーションの作成

まずは、コンソールアプリケーションを以下のコマンドで作成しましょう。

$ dotnet new console -o ConsoleLog

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

NLog 関連のパッケージを NuGet でインストールします。.NET Core 2.0 では NLog 4.5.x 以上が必要とのことなので、正式版ではありませんが最新版をインストールします。対象は、NLog.Extensions.LoggingNLog になります。

なお、ASP.NET Core では NLog.Extensions.Logging ではなく NLog.Web.AspNetCore をインストールする必要があります。間違えないようにしましょう。

DI (Dependency Injection) を使用してログを出力するのが .NET Core 時代の普通らしいので、DI のパッケージもインストールします。対象は、Microsoft.Extensions.DependencyInjection になります。

$ dotnet add package Microsoft.Extensions.DependencyInjection --version 2.0.0
$ dotnet add package NLog.Extensions.Logging --version 1.0.0-rtm-rc7
$ dotnet add package NLog --version 4.5.0-rc07

NLog の設定ファイル作成

NLog の設定ファイルを nlog.config という名称で作成します。ファイル名は全て小文字になります。

<?xml version="1.0" encoding="utf-8" ?>
<!-- XSD manual extracted from package NLog.Schema: https://www.nuget.org/packages/NLog.Schema-->
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xsi:schemaLocation="NLog NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true"
      internalLogFile="/tmp/log/console-internal.log"
      internalLogLevel="Info" >


  <targets>
    <!-- Eventlog -->
    <target xsi:type="File" name="event" fileName="/tmp/log/event-${shortdate}.log"
            layout="${date}|${level:uppercase=true}|${message} ${exception}|${logger}|${all-event-properties}" />
 
    <!-- Errorlog -->
    <target xsi:type="File" name="error" fileName="/tmp/log/error.log"
            layout="${date}|${level:uppercase=true}|${message} ${exception}|${logger}|${all-event-properties}" />

  </targets>

  <rules>
    <logger name="*" maxlevel="Warn" writeTo="event" />
    <logger name="*" minlevel="Error" writeTo="error" />
  </rules>
</nlog>

ログレベルが Trace, Debug, Warn の場合は event-日付.log ファイルに、Error, Fatal の場合は error.log ファイルにログを出力するようになってます。なお、${shortdate} とすることでファイル名に日付を入れることができます。

ソースコードの修正

今回は、Program.cs を以下のように修正します。

using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NLog.Extensions.Logging;

namespace ConsoleLog
{
    class Program
    {
        static void Main(string[] args)
        {
            //Set log provider
            var servicesProvider = BuildDi();
            var runner = servicesProvider.GetRequiredService();

            //Write log
            runner.DoAction("LogAction");

            // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
            NLog.LogManager.Shutdown();
        
        }

         private static IServiceProvider BuildDi()
        {
            var services = new ServiceCollection();

            //Runner is the custom class
            services.AddTransient<Runner>();

            services.AddSingleton<ILoggerFactory, LoggerFactory>();
            services.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
            services.AddLogging((builder) => builder.SetMinimumLevel(LogLevel.Trace));
            
            var serviceProvider = services.BuildServiceProvider();

            var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();

            //configure NLog
            loggerFactory.AddNLog(new NLogProviderOptions { CaptureMessageTemplates = true, CaptureMessageProperties =true });
            loggerFactory.ConfigureNLog("nlog.config");

            return serviceProvider;
        }
    }

    public class Runner
    {
        private readonly ILogger<Runner> _logger;

        public Runner(ILogger<Runner> logger)
        {
            _logger = logger;
        }

        public void DoAction(string name)
        {
            //ログの出力
            _logger.LogTrace("trace message {Action}", name);
            _logger.LogDebug("debug message {Action}", name);
            _logger.LogWarning("warning message {Action}", name);
            _logger.LogError("error message {Action}", name);
            _logger.LogCritical("critical message {Action}", name);
        }
    }
}

まあ、ほぼ見たままなのですが、Runner クラスの DoAction メソッドでログを出力しています。実際に使う場合は、DoXxxx という感じでメソッドを増やしていって各メソッドで1つのログを出力することになると思います。

ログを出力したい場合は、その場所で Main メソッドに書かれている手順でログ出力の記述をするのでしょう。

DI を使っているためちょっと直感的ではないのですが、.NET Core ではこのようにログ出力を行うようです。

ビルドと実行

ここまでできたら、ビルドと実行ですね。

Visual Studio Code のいつものように、下記コマンドを実行します。

すると、なんということでしょう。例外が発生してしまいました。

Unhandled Exception: System.IO.FileNotFoundException: Could not find file '/Users/xxx/Projects/ConsoleLog/bin/Debug/netcoreapp2.0/nlog.config'.

nlog.config が見つからないというエラーですね。

このエラーですが、ネットで調べてもなかなか情報がなかったのですが、なんのことはないビルド「先」に nlog.config が存在していないだけなんですよね。なので、ビルド先に nlog.config をコピーすれば問題解消です。

最初と nlog.config を変更した場合は以下のようにコマンドを実行します。

$ dotnet build
$ cp nlog.config bin/Debug/netcoreapp2.0
$ dotnet run

ちなみに、Visual Studio では nlog.config ファイルのプロパティで「常にコピー」を設定しておけばよいようです。

では、実行結果を確認します。

$ cat event-2018-03-20.log
2018/03/20 14:43:02.707|TRACE|trace message LogAction |ConsoleLog.Runner|Action=LogAction
2018/03/20 14:43:02.797|DEBUG|debug message LogAction |ConsoleLog.Runner|Action=LogAction
2018/03/20 14:43:02.800|WARN|warning message LogAction |ConsoleLog.Runner|Action=LogAction

$ cat error.log
2018/03/20 14:43:02.800|ERROR|error message LogAction |ConsoleLog.Runner|Action=LogAction
2018/03/20 14:43:02.801|FATAL|critical message LogAction |ConsoleLog.Runner|Action=LogAction

おわりに

.NET Core で人気があるらしい NLog を使ってログを出力する方法をご紹介してみました。

ちょっとクセはありますが、 ログの出力先をファイルだけではなくデータベースやメールなども選択できるようですし、ログのローテートもできるみたいなので、なかなか良い感じではないでしょうか。

参考サイト