1. 程式人生 > 實用技巧 >.NETCore微服務探尋(三) - 分散式日誌

.NETCore微服務探尋(三) - 分散式日誌

前言

一直以來對於.NETCore微服務相關的技術棧都處於一個淺嘗輒止的瞭解階段,在現實工作中也對於微服務也一直沒有使用的業務環境,所以一直也沒有整合過一個完整的基於.NETCore技術棧的微服務專案。正好由於最近剛好辭職,有了時間可以寫寫自己感興趣的東西,所以在此想把自己瞭解的微服務相關的概念和技術框架使用實現記錄在一個完整的工程中,由於本人技術有限,所以錯誤的地方希望大家指出。

目錄

專案地址:https://github.com/yingpanwang/fordotnet/tree/dev

為什麼需要分散式日誌

在專案的執行執行過程中,不可避免的是由於系統原因或者業務原因產生的警告或異常,這時我們需要根據產生的異常或警告資訊快速排查出現的問題並修復,但是由於多個服務產生的過於龐雜的資訊使那些以往通過直接寫入日誌檔案的方式已經無法滿足快速排查的需求了,因為直接寫入日誌檔案只能根據事先制定好的規則檢視日誌資訊,但是由於體量過大導致排查起來異常麻煩,例如,如果問題出現在 6月20日的凌晨1點 日誌檔案對應的是 log-2020-06-20 ,那麼導致這個問題產生的原因可能20日之前的前置問題已經產生,如果我們需要排查的話,由於無法巨集觀分析問題的出現原因,那麼需要日誌檔案逐個檢視導致效率低下。

如果採用分散式日誌的話,首先由於日誌由日誌中心統一儲存,不需要寫入本地檔案減少了IO(不包括由於專案與日誌收集中心通訊失敗而導致本地補償產生的日誌),其次搭配其他的可視乎,管理,分析元件,可以有一個良好的日誌管理與視覺化,排查時可以通過相關的資訊篩選,過濾無關資訊,從巨集觀資訊中精準查詢指定資訊,從而提高排查效率。

怎麼給專案接入分散式日誌系統

  • Exceptionless Asp.Net Core 開源分散式日誌元件

  • Log元件+ Elasticsearch+ Kibana 這種模式採用的時通過擴充套件已有日誌元件(Log4Net,Serilog,NLog等),通過Elasticsearch使用或不適用佇列的模式收集日誌,然後通過Kibana視覺化管理分析元件 實現日誌的收集分析

    目前我所瞭解的搭建分散式日誌系統的方式有兩種,但他們的方式其實底層都差不多 主要依賴Elasticsearch作為日誌的收集,然後搭配視覺化的外掛,這裡我選擇的時採用的是第二種方式,因為對程式碼的侵入性較小,可以比較靈活的根據實際的業務需要新增日誌元件的相關外掛。

首先安裝並執行Elasticsearch(es) 和 Kibana

這裡不詳細講 es/kibana 的配置,安裝好jdk以後 直接執行bin目錄下的elasticsearch.bat/kibana.bat 就可以了

注意 :

1.es 執行依賴jdk

2.Kibana 執行需要 對應es 對應的版本

其次擴充套件已有日誌元件使其通過es支援日誌收集

由於專案中我採用的時Serilog所以下面的程式碼都是以Serilog為主,但由於是實現Asp.Net Core中的ILogger,使用實際差距不大\

1.根據需要安裝依賴元件

必須(二選一)

  • Serilog Serilog 基本庫
  • Serilog.AspNetCore AspNetCore框架整合庫,包含Serlog基本庫和控制檯日誌實現

可選

  • Serilog.Extensions.Logging 包含了注入Serilog的擴充套件方法
  • Serilog.Sinks.Async 實現了日誌非同步收集
  • Serilog.Sinks.Consul 實現了控制檯日誌
  • Serilog.Settings.Configuration 如果需要通過json配置檔案配置Serilog的話需要安裝此庫
  • Serilog.Sinks.Elasticsearch 實現了Elasticsearch收集

2.初始化Serilog,並新增至AspNetCore的ILoggerFactory

這裡要新增serilog的方式有幾種,常用的是通過硬編碼的情況,另外是通過 xml,json等配置檔案的方式,這裡都做一個簡單的示例,更詳細的配置資訊可以檢視Serilog.Sinks github倉庫中的介紹,非常詳細,這裡不做過多介紹

Serilog.Settings.Configuration專案地址

1.硬編碼的方式


using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ForDotNet.Common.Consul.Extensions;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Sinks.Elasticsearch; namespace ForDotNet.Web.Api
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration; //初始化Serilog
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}")
.WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://localhost:9200"))
{
AutoRegisterTemplate = true,
IndexFormat = "Api1-{0:yyyy-MM-dd}",// es index模板
})
.CreateLogger();
} public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// 新增當前專案服務發現
services.AddConsulServiceDiscovery(); services.AddControllers();
} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,ILoggerFactory loggerFactory,IHostApplicationLifetime life)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} // 新增serilog
loggerFactory.AddSerilog(); app.UseConsulServiceDiscovery(life); app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}

2.通過配置檔案的方式

準備需要配置的資訊,這裡我們使用的是appsettings.json檔案


{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ServiceOptions": {
"ServiceIP": "localhost",
"ServiceName": "Auth",
"Port": 5800,
"HealthCheckUrl": "/api/health",
"ConsulOptions": {
"Scheme": "http",
"ConsulIP": "localhost",
"Port": 8500
}
},
"Serilog": {
"WriteTo": [
{
"Name": "Elasticsearch",
"Args": {
"nodeUris": "http://localhost:9200;http://remotehost:9200/",
"indexFormat": "auth-{0:yyyy-MM-dd}",
"autoRegisterTemplate": true
}
}
]
}
}

然後更改Serilog的相關初始化程式碼為


public Startup(IConfiguration configuration,IHostEnvironment hostEnvironment)
{ // 讀取配置檔案
var builder = new ConfigurationBuilder()
.SetBasePath(hostEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostEnvironment.EnvironmentName}.json", true, true)
.AddEnvironmentVariables(); Configuration = builder.Build(); Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(Configuration)
.CreateLogger();
}

3.將日誌記錄操作轉為非同步

由於serilog.sinks實現大多都是同步的方式實現,所以如果需要以非同步的方式收集日誌的話需要引用Serilog.Sinks.Async這個庫,並更改相關程式碼。詳細請見Serilog.Sinks.Async官方倉庫,

同樣,非同步的方式也可以通過配置檔案實現,可以檢視官方倉庫,這裡只使用硬編碼的方式。


public Startup(IConfiguration configuration,IHostEnvironment hostEnvironment)
{ // 讀取配置檔案
var builder = new ConfigurationBuilder()
.SetBasePath(hostEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostEnvironment.EnvironmentName}.json", true, true)
.AddEnvironmentVariables(); Configuration = builder.Build(); Log.Logger = new LoggerConfiguration()
.WriteTo.Async(configure =>
{
configure
.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code); configure
.Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://localhost:9200"))
{
AutoRegisterTemplate = true,
IndexFormat = "auth-{0:yyyy-MM-dd}", });
})
.CreateLogger();
}

3.執行並檢視

啟動Elasticserach,Kibana,專案後

檢視控制檯,發現同樣的日誌輸出了兩遍,這是因為AspNetCore預設實現的LoggerProvider 沒有清除所以會導致 列印輸出,我們在啟動時清除預設Provider即可

清除LoggerProvider

然後執行並檢視日誌,是不是清爽了很多

然後我們訪問Kiabana檢視我們剛剛收集的日誌

訪問 http://localhost:5601 Kibana預設專案地址,不同Kibana版本頁面會有差異

點選Management建立我們的日誌收集模型

這裡由於我已經建立過其他的模組的資訊,所以可以看到我已經建立的資訊,這裡我們點選建立新的資訊

這裡需要輸入 正則表示式 匹配收集的資訊,這裡我們輸入我們定義的模板開頭的api1並點選下一步

這裡選擇@timestamp作為索引模式,也可以選擇不新增,然後建立

建立完成後 去到 Discover 模組

在左邊選擇需要檢視的index

就可以看到我們的日誌已經收集到es中了,可以通過kibana查看了

如果覺得欄位過於複雜的話 可以在左邊選擇過濾的欄位檢視 我這裡已經只選擇了 leve 和 message

好了 ,以上我就我分享的 建立分散式日誌的內容了,如果有紕漏及錯誤 希望大家指出,謝謝