一套標準的ASP.NET Core容器化應用日誌收集分析方案

有態度的小碼甲 發表於 2020-11-27

講故事

關注我公眾號的朋友,應該知道我寫了一些雲原生應用收集和分析相關的文章,其中內容大多聚焦某個具體的元件:

  • 超級有用的TraceId,快點用起來吧!
  • 如何利用NLog輸出結構化日誌,並在Kibana優雅分析日誌? |
  • 既然能直接向ElasticSearch寫日誌,為什麼還要logstash日誌攝取器?

本文記錄一套標準的、無侵入的的容器化應用日誌收集方案:

  1. 什麼樣的日誌應該被收集?
  2. 如何輸出為結構化日誌?
  3. 使用EFK無侵入的收集分析日誌
    一套標準的ASP.NET Core容器化應用日誌收集分析方案

定製ASP.NET Core日誌; 將結構化日誌輸出到stdout;Fluentbit無侵入式轉發容器日誌;儲存在Es並在Kibana上分析日誌

定製ASP.NET Core日誌

面向網際網路的經典應用,不外乎三部分日誌:請求、業務處理、資料庫操作。
在實際採集日誌時,關注[特定日誌場景]:

  • 提供給第三方呼叫的API(有撕逼可能性)
  • 核心流程業務 (996排障)
  • 資料庫操作(刪庫跑路可能性)
  • 應用內部http請求
  • Warn、Error、Fatal級別日誌(持續關注)

ASP.NETCore靈活的配置系統、可插拔的元件系統,讓我們輕鬆配置日誌、管理日誌元件。

日誌配置

ASP.NET Core應用的日誌配置取決於appsettings.{Environment}.json檔案的Logging配置節,
支援多個LogProvider、過濾日誌、定製特定種類日誌的收集級別。

 "Logging": {
    "LogLevel": {
      "Microsoft": "Warning",
      "Microsoft.AspNetCore.Hosting.Diagnostics": "Information",    // 提供給第三方呼叫API日誌
      "Microsoft.Hosting.Lifetime": "Information",
      "Microsoft.EntityFrameworkCore.Database.Command": "Information",  //資料庫操作sql日誌
      "System.Net.Http.HttpClient": "Information",    // 記錄內部http請求
      "Default": "Warning"    // 除以上日誌之外,記錄Warning+級別日誌
    }
  }

以上Logging配置針對[特定日誌場景],滿足經典網際網路應用的日誌採集需求。

NLog Provider

結構化日誌提出[MessageTemplate]來解決傳統文字日誌對機器不友好的問題。

① 這裡使用NLog Provider接管所有的日誌輸出

// Please  install-package NLog.Web.AspNetCore
internal static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
              .ConfigureLogging((hostBuilder, loggerBuilder) =>
              {
                  loggerBuilder.ClearProviders();
                  loggerBuilder.AddNLog("nlog.production.config");
              })
              .ConfigureWebHostDefaults(webBuilder =>
              {
                  webBuilder.UseStartup<Startup>();
              });

② 編寫NLog[JsonLayout]將傳統文字日誌轉換為JSON格式日誌:

<?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" autoReload="true" internalLogFile="logs/nlog-internal.log" internalLogLevel="Info" >
  <targets async="true">
    <target name="console" xsi:type="Console">
      <layout xsi:type="JsonLayout" includeAllProperties="true" excludeProperties="EventId_Id,EventId_Name,EventId">
        <attribute name="time" layout="${date:format=yyyy/MM/dd HH\:mm\:ss.fff zzz}" />
        <attribute name="category" layout="${logger}" />
        <attribute name="log_level" layout="${level:lowerCase=true}" />
        <attribute name="message" layout="${message}" />
        <attribute name="trace_id" layout="${aspnet-TraceIdentifier:ignoreActivityId=true}" />
        <attribute name="user_id" layout="${aspnet-user-identity}" />
        <attribute name="exception" layout="${exception:format=tostring}" />
      </layout>
    </target>
  </targets>

  <rules>
    <logger name="*" minlevel="Info" writeTo="console"   ruleName="console" />
  </rules>
</nlog>

與業務緊密相關的日誌字元:

  • includeAllProperties="true" 輸出日誌條目的所有屬性
  • trace_id=${aspnet-TraceIdentifier:ignoreActivityId=true} 取得trace_id,排障時很有用
  • user_id=${aspnet-user-identity} 取得該條日誌生產者的名字

啟動應用日誌長這樣:
一套標準的ASP.NET Core容器化應用日誌收集分析方案

請保持所有應用日誌的輸出目標為stdout,讓Fluent-bit無侵入採集!

....【TODO: 容器製作映象!!!!】 ...

Fluent-Bit收集容器日誌

Fluent-bit採集日誌,小巧夠用!

採集容器日誌需要將容器應用的Logging Driver改為[Fluentd]
Fluentd Driver預設會在宿主機24224埠監聽Forward訊息 。

一個簡單的容器Docker-compose示例:

version: "3.7"

services:
  website:
    image: ${DOCKER_REGISTRY}/eap/website:0.1
    ports:
      - "80:80"
    environment:
      - TZ=Asia/Shanghai
    networks:
      - webnet
    logging:
      driver: fluentd
      options:
#       fluentd-address: localhost:24224
        tag: eap-website
    restart: always
networks:
  webnet:
    external: true
    name: eap-net

Fluentd Driver採集的格式如下 :

{
"container_id": "...",
"container_name": "...",
"source": "stdout",
"log": "This is log content"
}

容器應用產生的json日誌(log欄位)會被編碼,這就很尷尬了,處心積慮的結構化日誌沒有萃取出日誌欄位!!
一套標準的ASP.NET Core容器化應用日誌收集分析方案

一套標準的ASP.NET Core容器化應用日誌收集分析方案

多番搜尋,在Fluentbit上找到Decoders外掛, 能將被編碼的JSON字串解碼:

完整的fluent-bit.conf 如下:

[SERVICE]
    flush            1
    log_Level        info
    daemon           off
    http_server      on    // 在宿主機作為http server啟動
    http_listen      0.0.0.0
    http_port        2020
    storage.metrics  on
    Parsers_File     parsers.conf
[INPUT]
    name             forward
    max_chunk_size   1M
    max_buffer_size  5M
[FILTER]
    Name  parser
    Match *
    Key_Name log            // 要解析的欄位
    Parser  docker          // 以docker日誌格式解析在parser.conf檔案
    Preserve_Key   True     // 保留原解析的欄位
    Reserve_Data   True     // 保留原始其他欄位
[OUTPUT]
    name             es
    match            *
    host             es01
    port             9200
    logstash_format  on
    replace_dots     on
    retry_limit      false

這樣輸出的結果就是:

一套標準的ASP.NET Core容器化應用日誌收集分析方案

nice,後面就請自由在Kibana中分析日誌吧。

完整的EFK收集容器日誌的原始碼配置,github傳送門:https://github.com/zaozaoniao/dockercompose-efk

以上就是小碼甲總結的使用EFK收集分析容器化ASP.NET Core應用的全過程, 可學習可商用。