前言
一直以來對於.NETCore微服務相關的技術棧都處於一個淺嘗輒止的了解階段,在現實工作中也對於微服務也一直沒有使用的業務環境,所以一直也沒有整合過一個完整的基於.NETCore技術棧的微服務項目。正好由於最近剛好辭職,有了時間可以寫寫自己感興趣的東西,所以在此想把自己了解的微服務相關的概念和技術框架使用實現記錄在一個完整的工程中,由於本人技術有限,所以錯誤的地方希望大家指出。
目錄
為什么需要分布式日志
在項目的運行運行過程中,不可避免的是由於系統原因或者業務原因產生的警告或異常,這時我們需要根據產生的異常或警告信息快速排查出現的問題並修復,但是由於多個服務產生的過於龐雜的信息使那些以往通過直接寫入日志文件的方式已經無法滿足快速排查的需求了,因為直接寫入日志文件只能根據事先制定好的規則查看日志信息,但是由於體量過大導致排查起來異常麻煩,例如,如果問題出現在 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.Console 實現了控制台日志
- 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
好了 ,以上我就我分享的 建立分布式日志的內容了,如果有紕漏及錯誤 希望大家指出,謝謝