NLog整合Exceptionless

yi念之間發表於2021-02-19

前言

    在實際的.Net Core相關專案開發中,很多人都會把NLog作為日誌框架的首選,主要是源於它的強大和它的擴充套件性。同時很多時候我們需要集中式的採集日誌,這時候僅僅使用NLog是不夠的,NLog主要是負責程式碼中日誌的落地,也就是收集程式中的日誌。類似的使用ELK(Elasticsearch+Logstash+Kibana)或EFK(Elasticsearch+Filebeat+Kibana)的集中式日誌管理平臺負責統一採集各個應用端通過日誌框架手機的日誌並統一的管理和展示。但是無論是ELK還是EFK,操作都有一定的複雜度,而且這是重型武器,有時候可能還不需要這麼大的排場,這時候就需要一種輕量級的解決方案,而Exceptionless正式這種輕量級的分散式日誌管理平臺。

概念

可能有的同學對於Exceptionless或者是NLog還不是很瞭解,這裡我們們就簡單的介紹一下。

Exceptionless

    簡單的來說Exceptionless就是一款分散式日誌管理框架,它可以統一收集管理並展示出來程式的日誌,這樣的話減少了傳統開發過程中還需要去伺服器查詢日誌的痛苦,大大提升對程式的運維效率。接下來我們先亮出來自學三件套

目前支援JavaScript, Node, .NET Core, .NET相關應用程式的異常資訊採集。為何僅支援.Net .Net Core和JS相關的?原因很簡單,Exceptionless是基於.NET Core開發的。如果你有別的語言的開發需求也想使用Exceptionless,這個時候不要氣餒,因為Exceptionless本質是基於http介面的形式上報資料的,這個可在官方文件上找到如何使用http上報日誌資訊相關

以上文件有針對Exceptionless通過http介面對接的所有資訊,通過它可以封裝自己的sdk。

NLog

    相信很多同學對NLog已經相當熟悉了,它是一款日誌框架,完美的支援.Net和.Net Core,它在.Net Core的流行度和使用廣泛度完全不亞於之前的Log4Net,最重要的它功能很強大,而且擴充套件起來非常方便,它支援將日誌輸入到多種target形式,比如txt檔案、Sql Server、MySQL、Redis、Mq、MongoDb、ElasticSearch等,幾乎能想到的所有儲存相關的元件,而且還支援過時日誌打包壓縮自動刪除等高階功能,也是我個人非常推薦的一款日誌框架,而且它可以直接對接到.Net Core Logger元件上,廢話不多說自學N件套地址

NLog最大的優勢就是強大,強大到你能用到的它幾乎都支援,而且你想不到的它可能也支援了,而且使用起來也是非常的簡單。作為日誌框架,我覺得它是最值得一試的一款。

環境搭建

    上面我們已經分別介紹了Exceptionless和NLog知道了他們的概念。Exceptionless支援直接採集日誌資訊上報到Exceptionless,也就是原始的方式,這個官方文件上都有相關的介紹,這裡我們們就不過多介紹這種方式了,使用原始方式的的時候可能會存在許多的問題,比如上報形式單一採集格式的問題等。許多時候我們是使用日誌框架記錄程式日誌相關的,它的優勢在於target豐富,而且支援自定義日誌格式等等,恰恰NLog足夠強大,支援直接將Log資料上報到Exceptionless,接下來我們就來看一下它們之間的整合方式。

Exceptionless搭建

官網提供了兩種使用的方式

  • 一種是在官方網站註冊賬號然後獲取apiKey,這樣的話不用自己搭建Exceptionless,而是將日誌直接收集上報到Exceptionless伺服器上。但是,一般基於安全和效能考慮,這種方式並不常用。
  • 另一種則是自建Exceptionless服務,也是本篇我們要使用的方式。之前的低版本支援在window伺服器上自建服務,但是高版本已經是基於docker的方式構建了。而使用docker的方式也是我個人日常學習中比較喜歡的方式。

官方也是提供了兩種方式去基於docker構建Exceptionless,一種是基於原始碼自行構建,另一種則是通過官方docker映象直接執行容器。因為Exceptionless依賴Elasticsearch儲存所以官方也是也是直接提供了docker-compose的方式去執行容器。
如果使用基於原始碼的方式構建,首先是找到Exceptionless的官方GitHub地址https://github.com/exceptionless/Exceptionless去clone原始碼,或者直接下載原始碼的Release包https://github.com/exceptionless/Exceptionless/releases。下載完成之後進入專案根目錄找到docker-compose.dev.yml檔案,檔案內容如下

version: '3.7'

services:
  #通過原始碼自行構建映象
  app:
    #依賴elasticsearch和redis
    depends_on:
      - elasticsearch
      - redis
    build:
      context: .
      target: app
    image: exceptionless/app:latest
    environment:
      EX_AppMode: Production
      EX_ConnectionStrings__Cache: provider=redis
      EX_ConnectionStrings__Elasticsearch: server=http://elasticsearch:9200
      #redis的作用是訊息匯流排、訊息佇列和快取
      EX_ConnectionStrings__MessageBus: provider=redis
      EX_ConnectionStrings__Queue: provider=redis
      EX_ConnectionStrings__Redis: server=redis,abortConnect=false
      EX_RunJobsInProcess: 'false'
    #暴露訪問埠
    ports:
      - 5000:80
      - 5001:443
    volumes:
      - appdata:/app/storage
      - ssldata:/https

  jobs:
    depends_on:
      - app
    image: exceptionless/job:latest
    build:
      context: .
      target: job
    environment:
      EX_AppMode: Production
      EX_BaseURL: http://localhost:5000
      EX_ConnectionStrings__Cache: provider=redis
      EX_ConnectionStrings__Elasticsearch: server=http://elasticsearch:9200
      EX_ConnectionStrings__MessageBus: provider=redis
      EX_ConnectionStrings__Queue: provider=redis
      EX_ConnectionStrings__Redis: server=redis,abortConnect=false
      EX_ConnectionStrings__Storage: provider=folder;path=/app/storage
    volumes:
      - appdata:/app/storage

  elasticsearch:
    image: exceptionless/elasticsearch:7.10.0
    environment:
      discovery.type: single-node
      xpack.security.enabled: 'false'
      ES_JAVA_OPTS: -Xms1g -Xmx1g
    ports:
      - 9200:9200
      - 9300:9300
    volumes:
      - esdata7:/usr/share/elasticsearch/data

  kibana:
    depends_on:
      - elasticsearch
    image: docker.elastic.co/kibana/kibana:7.10.0
    ports:
      - 5601:5601

  redis:
    image: redis:6.0-alpine
    ports:
      - 6379:6379

volumes:
  esdata7:
    driver: local
  appdata:
    driver: local
  ssldata:
    driver: local

通過上面的docker-compose檔案我們可以看出目前Exceptionless依賴elasticsearch和redis,大致可以看出Exceptionless儲存是依賴elasticsearch,而提升效能的則是redis,比如訊息匯流排防止併發的緩衝佇列都是依賴redis的,具體實現細節我們這裡就不做過多套路了。因為使用dev的方式構建映象的方式依賴Exceptionless原始碼,所以不建議移動該docker-compose檔案位置,使用docker-compose的指令直接執行該檔案

docker-compose -f docker-compose.dev.yml up

上面的方式雖然可以直接依靠原始碼去構建,但是其實大可不必這麼複雜比如kibana這種完全就是多餘的,而且他的這種方式是依賴原始碼的,生產環境我們不可能把程式碼直接copy過去,所以我們需要精簡一下,如下所示

version: '3.7'

services:
  app:
    depends_on:
      - elasticsearch
      - redis
    image: exceptionless/exceptionless:latest
    environment:
      EX_AppMode: Production
      EX_ConnectionStrings__Cache: provider=redis
      EX_ConnectionStrings__Elasticsearch: server=http://elasticsearch:9200
      EX_ConnectionStrings__MessageBus: provider=redis
      EX_ConnectionStrings__Queue: provider=redis
      EX_ConnectionStrings__Redis: server=redis:6379,abortConnect=false
      EX_RunJobsInProcess: 'false'
    ports:
      - 5000:80
    volumes:
      - appdata:/app/storage

  jobs:
    depends_on:
      - app
    image: exceptionless/job:latest
    environment:
      EX_AppMode: Production
      EX_BaseURL: http://localhost:5000
      EX_ConnectionStrings__Cache: provider=redis
      EX_ConnectionStrings__Elasticsearch: server=http://elasticsearch:9200
      EX_ConnectionStrings__MessageBus: provider=redis
      EX_ConnectionStrings__Queue: provider=redis
      EX_ConnectionStrings__Redis: server=redis:6379,abortConnect=false
      EX_ConnectionStrings__Storage: provider=folder;path=/app/storage
    volumes:
      - appdata:/app/storage

  elasticsearch:
    image: exceptionless/elasticsearch:7.10.0
    environment:
      discovery.type: single-node
      xpack.security.enabled: 'false'
      xpack.ml.enabled: 'false'
      ES_JAVA_OPTS: -Xms1g -Xmx1g
    ports:
      - 9200:9200
      - 9300:9300
    volumes:
      - esdata7:/usr/share/elasticsearch/data

  redis:
    image: redis:6.0-alpine
    ports:
      - 6379:6379

volumes:
  esdata7:
    driver: local
  appdata:
    driver: local

將上面的yml內容直接複製到一個新建的docker-compose.yml的空檔案中就可以直執行了,無任何額外的依賴,在yml檔案所在路徑直接執行以下命令

docker-compose up -d

如果你的伺服器已經擁有了elasticsearch和redis服務,也就是不需要使用以上docker-compose的方式進行構建,那麼可以直接使用官方docker映象的方式直接啟動Exceptionless容器,可以使用docker原生的方式直接執行

sudo docker run -d -e EX_AppMode=Production -e EX_ConnectionStrings__Cache="provider=redis" -e EX_ConnectionStrings__Elasticsearch="server=http://10.255.198.168:9200" -e EX_ConnectionStrings__MessageBus="provider=redis" -e EX_ConnectionStrings__Queue="provider=redis" -e EX_ConnectionStrings__Redis="server=10.255.198.168:6379,abortConnect=false" -e EX_RunJobsInProcess=false -e EX_Html5Mode=true -p 5000:80 exceptionless/exceptionless:latest

這裡注意修改下相關服務的ip地址,因為我貼上的是我本機的地址,而且注意elasticsearch的版本必須是7.x版本的,否則的話會報錯。程式啟動完成後再瀏覽器輸輸入http://ip:5000後會自動跳轉到登入介面如果沒有登入賬戶需要註冊一個新的使用者後,登入到首頁如圖所示因為Exceptionless每個專案的日誌資訊是根據apiKey去區分的,所以要在Exceptionless中新增你需要採集日誌的專案,具體操作如以下步驟

  • 首先,點選所有專案--->建立專案
  • 然後,輸入組織名稱和專案名稱
  • 然後,選擇專案型別,這裡以Asp.Net Core程式為例
  • 完成之後,點選專案管理,這裡的API祕鑰正是我們上傳到Exceptionless服務所需要的憑證

到了這一步Exceptionless搭建基本上就完成了。

整合NLog

新建一個名叫ProductApi的Asp.Net Core的專案,專案名稱任意。然後新增Exceptionless.NLog包,這個包就是將NLog資料上報到Exceptionless的包

<PackageReference Include="Exceptionless.NLog" Version="4.6.2" />
<PackageReference Include="NLog.Web.AspNetCore" Version="4.10.0" />

Exceptionless.NLog的Github專案地址位於https://github.com/exceptionless/Exceptionless.Net/tree/master/src/Platforms/Exceptionless.NLog這個地址相當隱蔽不太容易被發現,而且說明文件也是很低調幾乎沒啥內容,可能是覺得NLog的文件寫的太完善了,不用多說大家就能知道怎麼用。新增完nuget包引用之後,修改Program.cs程式新增NLog的Logging擴充套件。僅僅新增UseNLog即可,因為我們使用了NLog.Web.AspNetCore擴充套件包,所以NLog會整合到Asp.Net Core自帶的Microsoft.Extensions.Logging中去,不得不說.Net Core的整體擴充套件性還是非常強的,這樣的話我們可以設定預設的Logging的配置即可,幾乎感知不到NLog的存在

public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                }).UseNLog();

接下來需要在專案根目錄中新建nlog.config用來配置nlog相關資訊,新建完成之後新增以下配置

<?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"
      throwExceptions="true" internalLogFile="internal-nlog.log" internalLogLevel="Debug" >
  <extensions>
    <!--新增擴充套件Exceptionless​程式集-->
    <add assembly="Exceptionless.NLog"/>
  </extensions>
  <targets async="true">
    <!--寫入本地檔案-->
    <target name="File" xsi:type="File" fileName="${basedir}/logs/${shortdate}.log"
            layout=" ${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}| ${newline}" >
    </target>
    <!--上報Exceptionless-->
    <!--xsi:type:固定是Exceptionless-->
    <!--apiKey:即我們在Exceptionless中新增完專案後得到的apiKey-->
    <!--serverUrl:Exceptionless的地址-->
    <target xsi:type="Exceptionless" name="Exceptionless" apiKey="d66B6fXD6sz3kAuqdc5Fe04td7iIygunkDa5GoUt"
            serverUrl="http://10.255.52.93:5000/">
      <!--堆疊資訊-->
      <field name="StackTrace" layout="${stacktrace}"/>
      <!--Message資訊-->
      <field name="Message" layout="${message}"/>
      <field name="LogLevel" layout="${level}"/>
      <field name="CreateDate" layout="${date}"/>
      <!--物理名稱-->
      <field name="MachineName" layout="${machinename}" />
      <!--執行緒ID-->
      <field name="ThreadId" layout="${threadid}"/>
      <!--發生源-->
      <field name="CallSite" layout="${callsite}"/>
      <field name="AppdomainVersion" layout="${assembly-version}"/>
      <field name="Appdomain" layout="${appdomain}"/> 
    </target>
  </targets>
  <rules>
    <!--本地檔案-->
    <logger name="*" writeTo="File"/>
    <!--上報Exceptionless-->
    <logger name='*' writeTo='Exceptionless'/>
  </rules>
</nlog>

新建完nlog.config之後不要忘了將右擊該檔案 屬性--->複製到輸出路徑--->始終複製,或修改該專案的csproj檔案新增

<ItemGroup>
    <Content Update="nlog.config">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
</ItemGroup>

到這裡為止關於NLog整合Exceptionless的環境搭建就已經完成了,是不是非常的簡單,拋開環境搭建工作量其實並不大,這一切都是源於.Net Core的強大和它那靈活的可擴充套件性。

簡單測試一下

通過上面的操作我們已經把NLog整合Exceptionless的環境搭建起來了,接下來我們隨便寫點程式碼測試一波隨便建個類,就是為了演示異常,程式碼無任何實質意義,不喜勿噴。。。,這裡我是模擬了一個ApiController丟擲異常,然後用Logger記錄了資訊

[Route("productapi/[controller]")]
public class ProductController : ControllerBase
{
    private readonly ILogger _logger;
    public ProductController(ILogger<ProductController> logger)
    {
        _logger = logger;
    }

    [HttpGet("exceptiontest")]
    public string ExceptionTest()
    {
        try
        {
            throw new Exception("發生了未知的異常");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex,$"{HttpContext.Connection.RemoteIpAddress}呼叫了productapi/product/exceptiontest介面返回了失敗");
        }
        return "呼叫失敗";
    }
}

執行起來專案呼叫一下這段程式碼之後,檢視Exceptionless,如果環境配置啥的都是正確的話,會展示出一下效果,點選All選單展示出來的資訊會比較全可以點選檢視詳情,詳情資訊記錄的非常詳細,不得不說Exceptionless還是非常強大非常人性非常實用的還能檢視更詳細的資訊到這裡為止,關於NLog整合Exceptionless的操作就全部完成了,感嘆一句就是不僅簡單而且強大。

總結

    通過本次整合NLog和Exceptionless,我們既感受到Exceptionless的簡單和強大,也感受到了NLog的擴充套件性之強,希望更多地人能夠嘗試一下NLog。這一切還是得益於.Net Core自身的擴充套件性,特別是它內建的一些抽象,完全成為了構建.Net Core程式的核心,而且基於這些內建的核心抽象操作可以很輕鬆的擴充套件許多操作,使得模組之間的耦合性變得非常低,而這種設計的思想才是我們真正在程式設計中應該學習的。

?歡迎掃碼關注我的公眾號? NLog整合Exceptionless

相關文章