1. 程式人生 > >ASP.NET Core 實戰:使用 NLog 將日誌資訊記錄到 MongoDB

ASP.NET Core 實戰:使用 NLog 將日誌資訊記錄到 MongoDB

 一、前言

  在專案開發中,日誌系統是系統的一個重要組成模組,通過在程式中記錄執行日誌、錯誤日誌,可以讓我們對於系統的執行情況做到很好的掌控。同時,收集日誌不僅僅可以用於診斷排查錯誤,由於日誌同樣也是大量的資料,通過對這些資料進行集中分析,可以產生極大的價值。
  在微服務的系統架構中,由於一個系統會被拆成很多個功能模組,每個模組負責不同的功能,對於日誌系統的要求也會更高,比較常見的有 EFLK(ElasticSearch + Filebeat + LogStash + Kibana) 方案,而對於我們這種單體應用來說,由於程式的程式碼比較集中,所以我們主要採用手寫日誌幫助類或是使用第三方元件的形式進行日誌資訊的記錄。

  系列目錄地址:ASP.NET Core 專案實戰
  倉儲地址:https://github.com/Lanesra712/Grapefruit.VuCore

 二、Step by Step

  1、為什麼選擇 NLog 和 MongoDB

  在 ASP.NET Core 中,巨硬為我們提供了一個 ILogger 介面,通過 ILogger 介面,我們可以很方便的將日誌資訊輸出到控制檯中,不過,在控制檯中檢視日誌資訊會顯得不太方便,因此,我們可以通過實現該介面或是直接使用第三方的框架來實現將日誌資訊記錄到別的儲存介質中。

  在 .NET Framework 時代,對於第三方的日誌框架的選擇,絕大多數童鞋首選的都會是 log4net 這一根據 Log4j 移植的日誌框架,不過,由於 log4net 目前已經接近有3年的時間沒更新了,所以就不在考慮範圍內。綜合比較下官方文件中推薦的幾款第三方日誌框架,最終還是選擇 NLog 這一目前使用人數相對來說比較多的框架,畢竟使用者多的話,遇到什麼問題也好找資料。

  通常,我們會將日誌資訊記錄到 txt or log 檔案中,雖然你可以通過修改日誌佈局讓日誌資訊具有良好的可讀性,不過在資訊多的情況下查閱時還是會顯得不太方便。因為不僅做到對於錯誤資訊做到記錄,還需要記錄程式在執行時的訪問日誌,所以將日誌資訊寫入到關係型資料庫中就不是特別合適了。

  而 MongoDB 作為一個文件型的 NoSQL 資料庫,相比於傳統的關係型資料庫,NoSQL 資料庫具有更好的擴充套件性、以及能提供更出色的效能,因此,我最終選擇將日誌資訊記錄到 MongoDB 中。當然,最主要的原因還是目前在工作中有開始嘗試用 MongoDB 儲存使用者上傳的檔案,在找資料的過程中看到有使用 MongoDB 儲存日誌的案例,Grapefruit.VuCore 既然作為一個學習專案,所以就要多嘗試嘗試啊。

  2、安裝 MongoDB(Windows)

  因為是第一次使用 MongoDB,所以我們需要提前安裝 MongoDB Server,我是直接安裝到我的開發機上(Windows 10),所以這裡只是演示如何在 Windows 上進行 MongoDB 的安裝與配置,如何在 Linux or Docker 中進行安裝配置,我將在後面的文章中進行演示。畢竟,這個專案的最終準備通過 Docker 部署到 Linux 上的,總在 Windows 上玩是不合適滴。

  首先,開啟 MongoDB 官網獲取到我們的安裝包下載地址(MongoDB Community Download),選擇 Server tab 後按照我們的作業系統選擇安裝包下載即可。

  雙擊下載好的 msi 檔案,開始安裝,這裡我選擇 Complete(完整)安裝,如果你想要指定安裝的元件和安裝的位置,你可以選擇 Custom(自定義安裝)。

  在 MongoDB 之前的版本中,如果我們需要將 MongoDB Server 作為 Windows 服務,需要我們在安裝完成之後進行配置,但是從 MongoDB 4.0 開始,我們就可以在安裝期間直接配置和啟動我們的 MongoDB 作為 Windows 服務了,當我們安裝成功後就會自動啟動 MongoDB 服務。嗯,相信我,如果你上網搜尋 Windows 下的 MongoDB 安裝,你會發現 90% 的文章因為是針對 MongoDB 之前版本的,都會在安裝完成之後需要你指定日誌地址、指定儲存地址,配置 Windows 服務啊,而如果你和我一樣,安裝的是 MongoDB 4.0 以上的版本,這些統統都不要,是不是很超值。

  這裡勾選上 Install MongoD as a Service,當我們安裝完成後就會自動啟動 MongoDB 服務,同時,對於這裡的配置項,我們不做任何的改動。

  Service Name:建立的 Windows 服務名稱,如果已經存在了,則需要更換名稱

  Data Directory:儲存資料的目錄

  Log Directory:儲存 MongoDB Log 日誌的目錄

  點選 Next 之後,安裝程式會詢問你是否需要安裝 MongoDB Compass,MongoDB Compass 是官方的一個視覺化管理工具,畢竟總是用黑乎乎的 shell 還是不太方便的,這裡看你自己的需求,決定是否安裝這個工具。

  當我們安裝完成後,MongoDB 的服務也就已經啟動了,此時,你就可以連線上你的 MongoDB Server 了,這裡我是使用 Navicat 進行連線。對於這個服務,你同樣可以在計算機管理中對這個服務進行管理。

  在預設情況下,當我們安裝好 MongoDB 後是不允許遠端訪問以及不存在任何的使用者許可權的。而這些,在我們正式使用中都是需要考慮的。

  首先,配置我們的 MongoDB Server 以允許使用者進行遠端訪問。找到程式安裝路徑下面的 mongod.cfg 檔案(如果你使用的是預設配置,則該檔案位於 C:\Program Files\MongoDB\Server\4.0\bin),修改 bindIp 屬性值為 0.0.0.0,重啟 MongoDB 服務,確保 27017 埠外界可以訪問後,則可以遠端訪問我們的 MongoDB 服務。

  當我們允許遠端訪問我們的 MongoDB 服務後,我們更應該為 MongoDB 配置許可權。與我們經常使用的 SQL Server 或是 MySQL 不同,MongoDB 中的許可權是針對每一個數據庫的,也就是說我們需要為使用到的資料庫建立使用者並配置許可權。

  開啟 Navicat,連線安裝好的 MongoDB 服務。

  第一步將預設資料庫切換到 admin 資料庫,建立一個管理員使用者,這裡我就將管理員使用者的角色設定為 root 使用者。

//切換到 admin 資料庫
use admin

//建立一個管理員使用者
db.createUser(
   {
     user: "user name",
     pwd: "user password",
     roles: [ { role: "root", db: "admin" } ]
   }
)

  當我們建立好管理員使用者後,我們就可以為資料庫配置使用者與許可權了。右擊連線名稱,新建一個數據庫 GrapefruitVuCore,切換到 GrapefruitVuCore 資料庫後,新建一個可以讀寫的使用者 grapefruit。使用者都建立完成後,關閉我們的 MongoDB 連線。

//切換到 admin 資料庫
use GrapefruitVuCore

//建立一個管理員使用者
db.createUser(
   {
     user: "grapefruit",
     pwd: "grapefruit",
     roles: [ { role: "readWrite", db: "GrapefruitVuCore" } ]
   }
)

  當用戶已經建立完成之後,我們就可以修改配置檔案,啟用許可權控制。還是在 mongod.cfg 中,取消 security 節點的註釋,新增授權配置,修改完成後,重啟服務,此時,MongoDB 就必須通過賬戶密碼登入了。

  當服務重啟之後,如果你還是按照之前的方式連線,則會提示你許可權不足,你需要修改 Navicat 的連線配置。將驗證方式修改成 Password,輸入賬戶、密碼,並指定需要登入的資料庫,重新連線即可。

  PS:這裡,我使用賬戶、密碼登入進入 GrapefruitVuCore 後,右側的連線下面是沒有顯示這個資料庫的,但這個資料庫是真實存在的,不曉得這是個啥問題。

  MongoDB 內建的使用者角色許可權:

  read:允許使用者讀取授權的資料庫

  readWrite:允許使用者讀寫授權的資料庫

  dbAdmin:允許使用者在授權的資料庫中執行管理操作,如索引建立、刪除,檢視統計或訪問system.profile

  userAdmin:允許使用者向 system.users 集合寫入,可以在指定資料庫裡建立、刪除和管理使用者

  clusterAdmin:只在 admin 資料庫中可用,賦予使用者所有分片和複製集相關函式的管理許可權。

  readAnyDatabase:只在 admin 資料庫中可用,賦予使用者所有資料庫的讀許可權

  readWriteAnyDatabase:只在 admin 資料庫中可用,賦予使用者所有資料庫的讀寫許可權

  userAdminAnyDatabase:只在 admin 資料庫中可用,賦予使用者所有資料庫的 userAdmin 許可權

  dbAdminAnyDatabase:只在 admin 資料庫中可用,賦予使用者所有資料庫的 dbAdmin 許可權。

  root:只在admin資料庫中可用。超級賬號,超級許可權

  3、使用 NLog 記錄日誌資訊

  當我們安裝配置好 MongoDB 後,有了儲存日誌資訊的介質,我們就可以使用 NLog 來記錄我們的程式日誌資訊了。首先,我們需要為專案中新增對於 NLog 的引用,右擊 Grapefruit.WebApi 開啟管理 Nuget 程式包頁面或是使用程式包管理器控制檯選中預設專案為 Grapefruit.WebApi,新增 NLog、NLog.Web.AspNetCore、NLog.Mongo。

Install-Package NLog
Install-Package NLog.Web.AspNetCore
Install-Package NLog.Mongo

  NLog 和 NLog.Web.AspNetCore 為 ASP.NET Core 添加了對於 NLog 的平臺支援,在 NLog 中,我們可以通過繼承 NLog.Targets.TargetWithLayout 來為 NLog 新增更多的輸出介質支援,而 NLog.Mongo 就是為 NLog 新增輸出日誌資訊到 MongoDB 的支援。嗯,嘗試了自己寫,一直有問題,最後還是用的別人寫好的,哈哈哈,水平太菜。

  當我們新增好引用後,在 Grapefruit.WebApi 下新增一個 NLog 的配置檔案 nlog.config(檔名全部需要小寫),右鍵 nlog.config,開啟屬性視窗,將複製到輸出目錄修改成較新才複製或是總是複製都可以。

  在配置檔案中,nlog 節點必須是 xml 檔案的根節點,同時包含三個主要的子節點:extensions、targets、rules。

  extensions:當你不僅僅只使用 NLog 這一個基礎的 dll ,並使用了一些基於 NLog 擴充套件的工具時,你就需要在 extensions 節點下面新增引用的程式集名稱。例如,這裡,我添加了 NLog.Web.AspNetCore 這個程式集從而達到 NLog 對於 ASP.NET Core 的支援,以及添加了 NLog.Mongo 這個程式集用來將日誌資訊輸出到 MongoDB 中。

  targets:targets 節點下包含了我們需要輸出的日誌的資訊內容以及日誌資訊的佈局,例如,這裡我按照日期輸出兩個檔案 nlog-all-date.log 和 nlog-own-date.log,分別記錄所有的日誌資訊以及我們自定義記錄的資訊。因為我們是需要將日誌資訊寫入 MongoDB 中的,這裡我也添加了一個子節點用來設定寫入 MongoDB 資料庫中的資料欄位。

  rules:rules 節點是將需要記錄的日誌級別關聯到記錄日誌的方式上。這裡,我是將只要是 Trace 以上的都進行日誌記錄。

<?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"
      internalLogFile="c:\Temp\GrapefruitVuCore\internal-nlog.txt">

  <!-- enable asp.net core and mongodb layout renderers -->
  <extensions>
    <add assembly="NLog.Web.AspNetCore"/>
    <add assembly="NLog.Mongo"/>
  </extensions>

  <!--internal-nlog:NLog啟動及載入config資訊-->
  <!--nlog-all:所有日誌記錄資訊-->
  <!--nlog-own:自定義日誌記錄資訊-->

  <!-- the targets to write to -->
  <targets>
    <!-- write logs to file  -->
    <target xsi:type="File" name="allfile" fileName="c:\Temp\GrapefruitVuCore\nlog-all-${shortdate}.log"
            layout="日誌記錄時間:${longdate}${newline}日誌級別:${uppercase:${level}}${newline}日誌來源:${logger}${newline}日誌資訊:${message}${newline}錯誤資訊:${exception:format=tostring}${newline}==============================================================${newline}" />

    <!-- another file log, only own logs. Uses some ASP.NET core renderers -->
    <target xsi:type="File" name="ownFile-web" fileName="c:\Temp\GrapefruitVuCore\nlog-own-${shortdate}.log"
            layout="日誌記錄時間:${longdate}${newline}日誌級別:${uppercase:${level}}${newline}日誌來源:${logger}${newline}日誌資訊:${message}${newline}錯誤資訊:${exception:format=tostring}${newline}url: ${aspnet-request-url}${newline}action: ${aspnet-mvc-action}${newline}==============================================================${newline}" />

    <!-- write log to mongodb-->
    <target xsi:type="Mongo"
            name="mongo" databaseName="GrapefruitVuCore"
            collectionName="Logs"
            connectionString="mongodb://grapefruit:[email protected]:27017/GrapefruitVuCore"
            cappedCollectionSize="26214400">
      <property name="LongDate" layout="${longdate}" bsonType="DateTime" />
      <property name="Level" layout="${level}" />
      <property name="Logger" layout="${logger}"/>
      <property name="Message" layout="${message}" />
      <property name="Exception" layout="${exception:format=tostring}" />
      <property name="Url" layout="${aspnet-request-url}" />
      <property name="Action" layout="${aspnet-mvc-action}" />
      <property name="UserName" layout="${windows-identity}" />
    </target>

  </targets>

  <!-- rules to map from logger name to target -->
  <rules>
    <!--All logs, including from Microsoft-->
    <logger name="*" minlevel="Trace" writeTo="allfile" />

    <!--Skip non-critical Microsoft logs and so log only own logs-->
    <logger name="Microsoft.*" maxLevel="Info" final="true" />
    <!-- BlackHole without writeTo -->
    <logger name="*" minlevel="Trace" writeTo="ownFile-web" />

    <!--Add logs to mongodb-->
    <logger name="*" minlevel="Trace" writeTo="mongo"/>
  </rules>
</nlog>    

  當我們設定好配置檔案後就可以在 Program.cs 中啟用 NLog 去記錄日誌。執行我們的專案後,就可以檢視記錄的日誌資訊了,這裡我在 txt 檔案中和 MongoDB 中都有記錄日誌資訊,具體看你自己的需求了。

public class Program
{
    public static void Main(string[] args)
    {
        //載入日誌配置資訊檔案後去捕獲所有的錯誤
        var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
        try
        {
            logger.Info("Init Log API Information");
            CreateWebHostBuilder(args).Build().Run();
        }
        catch (Exception ex)
        {
            logger.Error(ex, "Stop Log Information Because Of Exception");
        }
        finally
        {
            LogManager.Shutdown();
        }
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .ConfigureLogging(logging =>
            {
                logging.ClearProviders();//移除其它已經註冊的日誌處理程式
                logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);//記錄最小日誌級別
            })
            .UseNLog();//注入 NLog 服務
}

  另外,在 appsettings.json 中指定的 Logging 配置會覆蓋任何對於 SetMinimumLevel 方法的呼叫。因此,你可以刪除配置檔案中的 default 屬性,或是根據你自己的需要進行調整。

{
    "Logging": {
        "LogLevel": {
            "Default": "Trace",
            "Microsoft": "Information"
        }
    }
}

 三、總結

   本章主要是演示如何在 Windows 上安裝 MongoDB Server 以及在 ASP.NET Core 專案中使用 NLog 將日誌資訊記錄到 MongoDB 中。在我們使用這些這些第三方開源框架時,可能會遇到很多問題,當你無法解決的時候,專案的 Issue 是個好地方,多搜搜,很大可能你就會得到解決方案。