Use NLog to log

Page creation date :

environment

Visual Studio
  • Visual Studio 2019
ASP.NET Core
  • 5.0 (MVC, Razor page)

At first

NLog allows you to output logs to files, emails, and databases depending on your settings.

NLog ASP.NET be incorporated into core's standard logging system. It is possible to output logs according to the settings while using the default logger.

About log levels

Microsoft logs and NLogs are divided into six stages, and the output levels are roughly the same:

Level MicrosoftNLog
0 Trace Trace
1 Debug Debug
2 Information Info
3 Warning Warn
4 Error Error
5 Critical Fatal
(6) (None) (Off)

The higher the level, the more important the log is, and the more likely it is to be written regardless of the log output constraints.

Logging instructions

NLog Package Deployment

ASP.NET you've created a Core project, add a package.

Right-click Dependencies for your project and select Manage NuGet Packages.

Click the Browse tab and type NLog in the search field to display NLog-related packages.

Select NLog, and then click the install button with the latest stable version selected.

Click OK.

Also install NLog.Web.AspNetCore.

The package has been added to the project.

Add nlog.config

Add nlog.config, the output definition of the NLog log, to the project. The contents are in XML format, so I created it as an XML file. The file name should be nlog.config (lowercase).

Create the file as follows: Detailed settings will be discussed later.

<?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="Info"
      throwConfigExceptions="true"
      internalLogFile="${basedir}/internal-nlog-AspNetCore.txt">

  <!-- ログの出力レイアウトを変数 layoutDefine で定義 -->
  <variable name="layoutDefine"
            value="${longdate} [${event-properties:item=EventId_Id:whenEmpty=0}][${level:padding=-5}] ${message} ${exception:format=tostring} (${callsite:includeNamespace=false:fileName=true:includeSourcePath=false})" />

  <!-- 書き込むターゲット -->
  <targets>
    <!-- Visual Studio の出力 (デバッグ) に書き込みます -->
    <target xsi:type="Trace" name="TraceOutput" rawWrite="true" layout="${layoutDefine}" />

    <!-- 基本的な詳細を含むすべてのログメッセージのファイルターゲット -->
    <target xsi:type="File" name="FileOutput" fileName="${aspnet-appbasepath}/Log-${shortdate}.log" layout="${layoutDefine}" />

    <!-- Docker / Visual Studio の起動検出を改善するためにライフタイムメッセージをホストするためのコンソールターゲット  -->
    <target xsi:type="Console" name="LifetimeConsole" layout="${level:truncate=4}\: ${logger}[0]${newline}      ${message}${exception:format=tostring}" />
  </targets>

  <!-- ロガー名からターゲットにマップするルール -->
  <rules>
    <!-- Microsoft からのものを含むすべてのログ -->
    <logger name="*" writeTo="TraceOutput" />

    <!-- 起動の検出を高速化するために、ホスティングライフタイムメッセージをコンソールターゲットに出力します。Microsoft.Hosting.Lifetime はここより下の定義には出力しません -->
    <logger name="Microsoft.Hosting.Lifetime" minlevel="Info" writeTo="LifetimeConsole" final="true" />

    <!-- 重要でない Microsoft ログをスキップして、自分のログのみをログに記録する。システムが出す Warning 以上のログ以外はここより下の定義には出力されません -->
    <logger name="Microsoft.*" maxlevel="Info" final="true" />
    <logger name="System.Net.Http.*" maxlevel="Info" final="true" />

    <!-- 上記で除外したもの以外をファイルに出力 -->
    <logger name="*" writeTo="FileOutput" />
  </rules>
</nlog>

Make sure that the properties of nlog.config are set to Build Action: Content, Copy to Output Directory: Copy if New.

Edit appsetting.json

By default, no matter how you set nlog.config, Information only these levels are output. This is because logging also relies on appsetting.json.

Open appsetting.json Logging.LogLevel.Default and change the value to Trace . If you use NLog, you can adjust the level on the NLog side, so you can output virtually all levels Trace by setting appsetting.json.

{
  "Logging": {
    "LogLevel": {
      "Default": "Trace",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Also, during development, appsettings. Development.json settings are loaded, so we're changing them as well.

{
  "DetailedErrors": true,
  "Logging": {
    "LogLevel": {
      "Default": "Trace",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}

Preparing programs for NLog

Add a program so that you can log in the NLog mechanism.

Open .cs program file and fix it as follows:

using NLog.Web;  // 追加

// 省略

public class Program
{
  public static void Main(string[] args)
  {
    var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
    try
    {
      CreateHostBuilder(args).Build().Run();
    }
    catch (Exception exception)
    {
      // NLog:セットアップエラーをキャッチ
      logger.Error(exception, "例外のためにプログラムを停止しました。");
      throw;
    }
    finally
    {
      // アプリケーションを終了する前に、内部タイマー/スレッドをフラッシュして停止するようにしてください
      // (Linux でのセグメンテーション違反を回避してください)
      NLog.LogManager.Shutdown();
    }
  }

  public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
      .ConfigureWebHostDefaults(webBuilder =>
      {
        webBuilder.UseStartup<Startup>();
      })
      .ConfigureLogging(logging =>
      {
        logging.ClearProviders();                 // NLog 以外で設定された Provider の無効化.
        logging.SetMinimumLevel(LogLevel.Trace);  // 最小ログレベルの設定
      })
      .UseNLog();  // NLog:依存性注入のための NLog のセットアップ
}

Logging

For MVC projects HomeComtroller , for Razor pages, IndexModel you'd have been ILogger<IndexModel> logger passed to the constructor. It is also set to _logger in the private field, so you can use it to log.

The following is an example of output from a Razor page, but MVC can output with the same code.

// 省略

public class IndexModel : PageModel
{
  private readonly ILogger<IndexModel> _logger;
  
  public IndexModel(ILogger<IndexModel> logger)
  {
    _logger = logger;
    
    _logger.LogTrace("Trace で出力します。");
    _logger.LogDebug("Debug で出力します。");
    _logger.LogInformation("Information で出力します。");
    _logger.LogWarning("Warning で出力します。");
    _logger.LogError("Error で出力します。");
    _logger.LogCritical("Critical で出力します。");
    
    _logger.LogInformation(1, "EventID ありで出力します。");
  }
  
  public void OnGet()
  {
    _logger.LogInformation("ページを表示するタイミングでログを出力します。");
  }
}

When debugging, I think that a log file can be made in the folder of the project.

You can check the log by looking at the contents of the file.

Sample nlog.config commentary

I'm explaining it in order, but I'm not explaining the less important parts.

<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true"
      internalLogLevel="Info"
      throwConfigExceptions="true"
      internalLogFile="${basedir}/internal-nlog-AspNetCore.txt">

There are parameters, such as the log output settings for NLog itself.

internalLogFile will be the destination of the log. This can be helpful, for example, if there is an error in the output of the log itself. ${basedir} refers to the program's execution folder.

internalLogLevel is the output level of the NLog log. Off if it is, it will not be output at all.

<!-- ログの出力レイアウトを変数 layoutDefine で定義 -->
<variable name="layoutDefine"
          value="${longdate} [${event-properties:item=EventId_Id:whenEmpty=0}][${level:padding=-5}] ${message} ${exception:format=tostring} (${callsite:includeNamespace=false:fileName=true:includeSourcePath=false})" />

The variable is set to what layout the contents of the log will be output in. You can type directly into later targets, but if you specify multiple of the same layout, it is easier to manage them in one variable.

It is better to refer to the official website to see what parameters can be specified in the layout.

For reference, the parameters specified here are output in the following format:

Parameter output content
longdate Date and time minutes such as "2021-03-17 11:46:36.5034"
event-properties Displays the event ID, etc. of the log output specified by the program
level Levels such as Trace and Error
message Message specified in program logging
exception What if you pass exception in program logging
callsite Logging location, file name, etc.

Output example

2021-03-17 11:46:37.3537 [0][Info ] ページを表示するタイミングでログを出力します。  (IndexModel.OnGet(Index.cshtml.cs:26))
<!-- 書き込むターゲット -->
<targets>
  <!-- Visual Studio の出力 (デバッグ) に書き込みます -->
  <target xsi:type="Trace" name="TraceOutput" rawWrite="true" layout="${layoutDefine}" />

  <!-- 基本的な詳細を含むすべてのログメッセージのファイルターゲット -->
  <target xsi:type="File" name="FileOutput" fileName="${aspnet-appbasepath}/Log-${shortdate}.log" layout="${layoutDefine}" />

  <!-- Docker / Visual Studio の起動検出を改善するためにライフタイムメッセージをホストするためのコンソールターゲット  -->
  <target xsi:type="Console" name="LifetimeConsole" layout="${level:truncate=4}\: ${logger}[0]${newline}      ${message}${exception:format=tostring}" />
</targets>

Specifies the target to output. Multiple can be specified.

The first xsi:type="Trace" specifies , and the log is printed in the Visual Studio output window. It can be used primarily for debugging execution.

The second xsi:type="File" specifies and logs to a file in the specified path. If you ${shortdate} specify in the path, you can write the log to the file of the date of the log at the time of the log. Also ${aspnet-appbasepath} specifies the root folder for the Web project. However, when operating, it is better to output log files outside the Web directory for program replacement and security.

The third is xsi:type="Console" specified and can be displayed in the console in the console app. ASP.NET Core itself is not a console app, but it can also be used in environments such as Docker and Azure because the state can be displayed in the console. This is the description on the NLog official website.

<!-- ロガー名からターゲットにマップするルール -->
<rules>
  <!-- Microsoft からのものを含むすべてのログ -->
  <logger name="*" writeTo="TraceOutput" />

  <!-- 起動の検出を高速化するために、ホスティングライフタイムメッセージをコンソールターゲットに出力します。Microsoft.Hosting.Lifetime はここより下の定義には出力しません -->
  <logger name="Microsoft.Hosting.Lifetime" minlevel="Info" writeTo="LifetimeConsole" final="true" />

  <!-- 重要でない Microsoft ログをスキップして、自分のログのみをログに記録する。システムが出す Warning 以上のログ以外はここより下の定義には出力されません -->
  <logger name="Microsoft.*" maxlevel="Info" final="true" />
  <logger name="System.Net.Http.*" maxlevel="Info" final="true" />

  <!-- 上記で除外したもの以外をファイルに出力 -->
  <logger name="*" writeTo="FileOutput" />
</rules>

Here you specify what level and what type of logs to output to where. This description is applied from top to top.

The first is to name="*" output all logs to the target because the level is writeTo specified while specifying . This target is the specified in , so it TraceOutput appears in the Visual Studio output.

The second is Microsoft.Hosting.Lifetime output to the log output in the writeTo (コンソール) library. ConsoleLifetime associations can be checked in consoles such as Azure and Docker. minlevel="Info" because it Trace Debug specifies , and is not logged. Also, subsequent definitions do not output final="true" Microsoft.Hosting.Lifetime related logs.

The third name="Microsoft.*" is to name="System.Net.Http.*" stop logging final="true" by specifying for and logs. name *is a wildcard, which means Microsoft System.Net.Http that it refers to all related libraries. , but stop here maxlevel="Info" Trace , Debug Information because WArningThe Error , , log is also output in subsequent Critical definitions.

The fourth is output to final="true" a file other than the log stopped above.

Archive logs

There is also a method of archiving the old log file in a separate folder for each date with only one main log file.

For example:

<target xsi:type="File"
        name="FileOutput"
        fileName="${aspnet-appbasepath}/Log.log"
        archiveNumbering="Date"
        archiveEvery="Day"
        archiveFileName="${aspnet-appbasepath}/Archive/Log_{#}.log"
        archiveDateFormat="yyyy-MM-dd"
        maxArchiveFiles="7"
        layout="${layoutDefine}" />

The parameters used here mean:

Parameter description
archiveNumbering Date creates an archive file by date.
archiveEvery Day is used to archive on a daily basis. There are also ways to specify "Month", "Hour", "Sunday", etc.
archiveFileName Path to archive to. {#} changes on an archive-by-archive basis.
archiveDateFormat Date-by-date archive date format of the file name.
maxArchiveFiles Specifies how many files to archive up to.

There are also ways to archive non-date methods. For more information, see the official website.

When executed, it looks like this:

Send emails per log

NLog also allows you to send emails when logging. However, if you send an email such as a debug log, it will become too much to send, It is a good idea to target only limited log levels, such as Error and Fatal. In addition, to avoid the risk of failure, such as blocking the mail server due to overs transmission, We recommend that you specify a log-only email account.

The following is an example of a configuration. If you want to actually send mail, follow the SMTP server you want to use.

<targets>
  <target xsi:type="Mail"
          name="SendMail"
          smtpServer="SMTP Server Name"
          smtpPort="25"
          subject="XXXX システムでエラーが発生しました"
          from="aaaa@example.com"
          to="bbbb@example.com"
          enableSsl="False"
          smtpAuthentication="None"
          smtpUserName=""
          smtpPassword=""
          layout="${layoutDefine}"/>
</targets>

<rules>
  <logger name="*" minlevel="Error" writeTo="SendMail" />
</rules>

Write logs to the database

NLog can also write logs to the database. The following is an example, so please refer to the official website for details.

This section describes the steps to write to SQL Server on a different server.

First, create a table for logging in the target SQL Server. Since you can choose the value to write, define what you need as a log as a column.

The following is an example of table creation SQL.

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Log](
  [Id] [int] IDENTITY(1,1) NOT NULL,
  [Application] [nvarchar](50) NOT NULL,
  [Logged] [datetime] NOT NULL,
  [Level] [nvarchar](50) NOT NULL,
  [User] [nvarchar](250) NOT NULL,
  [Message] [nvarchar](max) NOT NULL,
  [Logger] [nvarchar](250) NULL,
  [Callsite] [nvarchar](max) NULL,
  [Exception] [nvarchar](max) NULL,
  CONSTRAINT [PK_dbo.Log] PRIMARY KEY CLUSTERED 
(
  [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

A database client library System.Data.SqlClient is required for database processing. Let's install it from NuGet.

Writes database information to appsettings.json. nlog.config allows you to load the information in appsettings.json. The settings are tentative, so please set them according to the actual database. The key name is arbitrary, but is used by nlog.config.

{
  "": "省略",

  "NlogConnection": {
    "DbHost": "ServerName\\SQLEXPRESS",
    "Database": "TestDatabase",
    "User": "UserName",
    "Password": "********"
  }
}

Set nlog.config as follows:

<targets>
  <target xsi:type="Database"
          name="DatabaseOutput"
          dbProvider="sqlserver"
          dbHost="${configsetting:name=NlogConnection.DbHost}"
          dbDatabase="${configsetting:name=NlogConnection.Database}"
          dbUserName="${configsetting:name=NlogConnection.User}"
          dbPassword="${configsetting:name=NlogConnection.Password}">
    <commandText>
      insert into dbo.Log (
        Application, Logged, [Level], [User], Message, Logger, CallSite, Exception
      ) values (
        @Application, @Logged, @Level, @User, @Message, @Logger, @Callsite, @Exception
      );
    </commandText>
    <parameter name="@application" layout="XXXX System" />
    <parameter name="@logged" layout="${date}" />
    <parameter name="@level" layout="${level}" />
    <parameter name="@user" layout="${aspnet-user-identity}" />
    <parameter name="@message" layout="${message}" />
    <parameter name="@logger" layout="${logger}" />
    <parameter name="@callSite" layout="${callsite:filename=true}" />
    <parameter name="@exception" layout="${exception:tostring}" />
  </target>
</targets>

<rules>
  <logger name="*" writeTo="DatabaseOutput" />
</rules>

If there are no errors to run, it will be written as follows:

By the way.cs note that attempting to write logs to the database at an early stage, such as before program .cs CreateHostBuilder methods, may fail.

Get loggers from RequestServices and log them

It can be tedious to add to the constructor each time you ILogger create a new controller or page model. Alternatively, RequestServices you can get from .

public void OnGet()
{
  // RequestServices から ILogger を取得する
  var logger = (ILogger<IndexModel>)HttpContext.RequestServices.GetService(typeof(ILogger<IndexModel>));

  logger.LogInformation("ページを表示するタイミングでログを出力します。");
}

If specifying your own controller or page model is also cumbersome, you can create extension methods or basic classes.

public static class AspNetExtention
{
  /// <summary>
  /// ロガーを取得する拡張メソッドです。
  /// </summary>
  public static ILogger<T> GetLogger<T>(this T self) where T : PageModel
    => (ILogger<T>)self.HttpContext.RequestServices.GetService(typeof(ILogger<T>));

  // MVC の場合
  //public static ILogger<T> GetLogger<T>(this T self) where T : Controller
  //  => (ILogger<T>)self.HttpContext.RequestServices.GetService(typeof(ILogger<T>));
}

Use cases

public void OnGet()
{
  // RequestServices から ILogger を取得する (this. は必要)
  var logger = this.GetLogger();

  logger.LogInformation("ページを表示するタイミングでログを出力します。");
}