.NETCore微服務探尋(三) – 分佈式日誌

前言

一直以來對於.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.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

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

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準

您可能也會喜歡…