Azure App Service 系列 (4) - 整合 NLog 與 Application Insights

在專案中,我們常常會使用一些第三方套件協助我們紀錄追蹤一些我們自訂的事件,以我自己為例子我通常使用 NLog 作為我的記錄工具。
而 Application Insight 也有提供 TrackEvent 讓我們在程式中添加我們的自訂事件。

然而舊的專案可能本身就有自己一套 Logger 在使用了,全部改寫耗時又費力。好在微軟推出了一套 Application Insights NLog Target 套件協助我們解決這個問題。
本篇紀錄如何在原有的程式碼中加入 Nlog 並整合至 Application Insights 使用。

安裝 NLog

1. 下載範例程式碼後,我們將 DemoWebAPI 加入NLogNLog.Config兩個套件。

2. 打開套件為我們新增的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"
      xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
      autoReload="true"
      throwExceptions="false"
      internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">

  <!-- optional, add some variables
  https://github.com/nlog/NLog/wiki/Configuration-file#variables
  -->
  <!--[變數] 文字樣板 -->
  <variable name="Layout" value="${longdate} | ${level:uppercase=true} | ${logger} | ${message} ${newline}" />
  <variable name="LayoutFatal" value="${longdate} | ${level:uppercase=true} | ${logger} | ${message} | ${exception:format=tostring} ${newline}" />

  <!--[變數] 檔案位置 -->
  <variable name="LogTxtDir" value="${basedir}/App_Data/Logs/${shortdate}/" />
  <variable name="LogTxtLocation" value="${LogTxtDir}/${logger}.log" />
  <variable name="LogTxtLocationFatal" value="${LogTxtDir}/FatalFile.log" />

  <!--
  See https://github.com/nlog/nlog/wiki/Configuration-file
  for information on customizing logging rules and outputs.
   -->
  <targets>

    <!--
    add your targets here
    See https://github.com/nlog/NLog/wiki/Targets for possible targets.
    See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers.
    -->

    <!--
    Write events to a file with the date in the filename.
    <target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
            layout="${longdate} ${uppercase:${level}} ${message}" />
    -->

    <!--[設定] 寫入目標-->
    <target name="File" xsi:type="File" fileName="${LogTxtLocation}" layout="${Layout}"
        encoding="utf-8" maxArchiveFiles="30" archiveNumbering="Sequence"
        archiveAboveSize="1048576" archiveFileName="${LogTxtDir}/${logger}.log{#######}" />
    <target name="FileFatal" xsi:type="File" fileName="${LogTxtLocationFatal}" layout="${LayoutFatal}"
            encoding="utf-8" maxArchiveFiles="30" archiveNumbering="Sequence"
            archiveAboveSize="1048576" archiveFileName="${LogTxtDir}/FatalFile.log{#######}" />
  </targets>

  <rules>
    <!-- add your logging rules here -->

    <!--[設定] 紀錄規則-->
    <logger name="*" levels="Trace,Debug,Info,Warn" writeTo="File" />
    <logger name="*" levels="Error,Fatal" writeTo="FileFatal" />
    <!--
    Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace)  to "f"
    <logger name="*" minlevel="Debug" writeTo="f" />
    -->
  </rules>
</nlog>

3. 將專案中的DemoController.cs檔案修改如下:

using System.Web.Http;
using NLog;

namespace DemoWebAPI.Controllers
{
    /// <summary>
    /// DemoController
    /// </summary>
    /// <seealso cref="System.Web.Http.ApiController" />
    public class DemoController : ApiController
    {
        /// <summary>
        /// The logger
        /// </summary>
        private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

        /// <summary>
        /// Gets the demo.
        /// </summary>
        /// <returns>
        /// IHttpActionResult
        /// </returns>
        [HttpGet]
        [Route("Demo")]
        public IHttpActionResult GetDemo()
        {
            Logger.Trace("Using nLog to write a 'trace' message");
            Logger.Debug("Using nLog to write a 'debug' message");
            Logger.Info("Using nLog to write an 'info' message");
            Logger.Warn("Using nLog to write a 'warning' message");
            Logger.Error("Using nLog to write an 'error' message");
            Logger.Fatal("Using nLog to write a 'fatal' message");
            return this.Ok("Hello World!");
        }
    }
}

4. 執行專案後,實際操作一遍 GET /Demo 這個 API,可以發現 NLog 在我們指定的目錄({basedir}/App_Data/Logs/${shortdate}/)下建立了兩份Log。

將 Nlog 整合至 Application Insights

1. 在專案中加入Application Insights 遙測,確認相關資訊並按下註冊

你可以為你的專案建立新的資源,或選擇已有的資源使用。在這裡我是選擇前一篇文章中建立的資源做使用。

2. 等待 VS 協助你安裝多個套件與新增ApplicationInsights.config後,會發現參考中多了一個Microsoft.ApplicationInsights.NLogTarget。它會協助我們將原本記錄在 NLog 裡的資訊送到 Application Insights 上。

3. 再次執行我們的 Application 並調用一次 API,稍後會發現我們的記錄也被寫進到 Application Insights 上了。

4. 如果你依舊需要機器上也留一份 Log 的話,請將NLog.config中的targets,extensionsrules這幾個 Tag 中的設定刪除並搬移至你的Web.ConfigApp.Config底下。如下:

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"
      xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
      autoReload="true"
      throwExceptions="false"
      internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
</nlog>

Web.config(或App.Config)中的nlog tag:

<nlog>
    <extensions>
      <add assembly="Microsoft.ApplicationInsights.NLogTarget" />
    </extensions>

    <!--[變數] 文字樣板 -->
    <variable name="Layout" value="${longdate} | ${level:uppercase=true} | ${logger} | ${message} ${newline}" />
    <variable name="LayoutFatal" value="${longdate} | ${level:uppercase=true} | ${logger} | ${message} | ${exception:format=tostring} ${newline}" />

    <!--[變數] 檔案位置 -->
    <variable name="LogTxtDir" value="${basedir}/App_Data/Logs/${shortdate}/" />
    <variable name="LogTxtLocation" value="${LogTxtDir}/${logger}.log" />
    <variable name="LogTxtLocationFatal" value="${LogTxtDir}/FatalFile.log" />

    <targets>
      <!--[設定] 寫入目標-->
      <target type="ApplicationInsightsTarget" name="aiTarget" />
      <target name="File" type="File" fileName="${LogTxtLocation}" layout="${Layout}"
          encoding="utf-8" maxArchiveFiles="30" archiveNumbering="Sequence"
          archiveAboveSize="1048576" archiveFileName="${LogTxtDir}/${logger}.log{#######}" />
      <target name="FileFatal" type="File" fileName="${LogTxtLocationFatal}" layout="${LayoutFatal}"
              encoding="utf-8" maxArchiveFiles="30" archiveNumbering="Sequence"
              archiveAboveSize="1048576" archiveFileName="${LogTxtDir}/FatalFile.log{#######}" />
    </targets>

    <rules>
      <!--[設定] 紀錄規則-->
      <logger name="*" minlevel="Trace" writeTo="aiTarget" />
      <logger name="*" levels="Trace,Debug,Info,Warn" writeTo="File" />
      <logger name="*" levels="Error,Fatal" writeTo="FileFatal" />
    </rules>
  </nlog>
如果你不需要保留原本寫在 Local 的 Log,你可以直接將 NLog.Config 套件移除,並手動刪除 NLog.config 及 NLog.xsd 檔案即可。