前言
在實際的.Net Core相關項目開發中,很多人都會把NLog作為日志框架的首選,主要是源於它的強大和它的擴展性。同時很多時候我們需要集中式的采集日志,這時候僅僅使用NLog是不夠的,NLog主要是負責代碼中日志的落地,也就是收集程序中的日志。類似的使用ELK(Elasticsearch+Logstash+Kibana)或EFK(Elasticsearch+Filebeat+Kibana)的集中式日志管理平台負責統一采集各個應用端通過日志框架手機的日志並統一的管理和展示。但是無論是ELK還是EFK,操作都有一定的復雜度,而且這是重型武器,有時候可能還不需要這么大的排場,這時候就需要一種輕量級的解決方案,而Exceptionless正式這種輕量級的分布式日志管理平台。
概念
可能有的同學對於Exceptionless或者是NLog還不是很了解,這里咱們就簡單的介紹一下。
Exceptionless
簡單的來說Exceptionless就是一款分布式日志管理框架,它可以統一收集管理並展示出來程序的日志,這樣的話減少了傳統開發過程中還需要去服務器查找日志的痛苦,大大提升對程序的運維效率。接下來我們先亮出來自學三件套
- 官網地址:https://exceptionless.com/
- 官方文檔地址:https://exceptionless.com/docs/
- 官方Github地址:https://github.com/exceptionless/Exceptionless
目前支持JavaScript, Node, .NET Core, .NET相關應用程序的異常信息采集。為何僅支持.Net .Net Core和JS相關的?原因很簡單,Exceptionless是基於.NET Core開發的。如果你有別的語言的開發需求也想使用Exceptionless,這個時候不要氣餒,因為Exceptionless本質是基於http接口的形式上報數據的,這個可在官方文檔上找到如何使用http上報日志信息相關
- 官方文檔api地址:https://exceptionless.com/docs/api/api-getting-started/
- api官方文檔地址:https://api.exceptionless.io/
- api官方swagger地址:https://api.exceptionless.io/docs/index.html
以上文檔有針對Exceptionless通過http接口對接的所有信息,通過它可以封裝自己的sdk。
NLog
相信很多同學對NLog已經相當熟悉了,它是一款日志框架,完美的支持.Net和.Net Core,它在.Net Core的流行度和使用廣泛度完全不亞於之前的Log4Net,最重要的它功能很強大,而且擴展起來非常方便,它支持將日志輸入到多種target形式,比如txt文件、Sql Server、MySQL、Redis、Mq、MongoDb、ElasticSearch等,幾乎能想到的所有存儲相關的組件,而且還支持過時日志打包壓縮自動刪除等高級功能,也是我個人非常推薦的一款日志框架,而且它可以直接對接到.Net Core Logger組件上,廢話不多說自學N件套地址
- 官方GitHub地址:https://github.com/NLog/NLog
- 官方文檔地址:https://nlog-project.org/
- 文檔地址:https://github.com/NLog/NLog/wiki
- 配置相關地址:https://nlog-project.org/config/
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程序的核心,而且基於這些內置的核心抽象操作可以很輕松的擴展許多操作,使得模塊之間的耦合性變得非常低,而這種設計的思想才是我們真正在編程中應該學習的。
