C# 將資料寫入 Elastic stack (ELK) 示範

以前在 Serilog 上要將 Log 傳送到 Elastic stack 都是使用 Serilog.Sinks.Elasticsearch 這個套件,不過後來 Elastic 官方推出了 Elastic.Serilog.Sinks 套件,專門支援 Elasticsearch 8.x 和以上的版本,本次將會使用新版的 Elastic.Serilog.Sinks 套件做示範,如果是 Elasticsearch 8 以前的版本請使用由社區維護的 Serilog.Sinks.Elasticsearch 套件。

安裝

先使用 NuGet 安裝 Elastic.Serilog.Sinks 套件,或是使用 .NET CLI 執行以下指令安裝
	
dotnet add package Elastic.Serilog.Sinks
    

基礎使用示範

在上一篇 在 Docker 上快速安裝 Elastic stack (ELK) 安裝的 Logstash monitoring API 連結是 http://localhost:9200 ,在下面程式碼中 WriteTo.Elasticsearch 中就填入這個網址
    
using Elastic.Channels;
using Elastic.Ingest.Elasticsearch;
using Elastic.Ingest.Elasticsearch.DataStreams;
using Elastic.Serilog.Sinks;
using Serilog;


// 初始化 log
Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Information()
    .Enrich.FromLogContext()
    .WriteTo.Elasticsearch(new[] { new Uri("http://localhost:9200") }, opts =>
    {
        opts.DataStream = new DataStreamName("logs", "dataSet", "namespace");
        opts.BootstrapMethod = BootstrapMethod.Failure;
        opts.ConfigureChannel = channelOpts =>
        {
            channelOpts.BufferOptions = new BufferOptions
            {
                ExportMaxConcurrency = 10,
            };
        };
    })
    .CreateLogger();

// 傳送 log
Log.Information("Hello, world!");

string? input = Console.ReadLine();

// 關閉 log 
Log.CloseAndFlush();
    

註:在 DataStreamName 中的三個參數 logs, dataSet, namespace 在 Elasticsearch 的規範中不允許出現「-」,雖然可以傳送但是因為在資料儲存中使用 {類型}-{資料集}-{命名空間} 的方式儲存,所以會造成資料分析困難

上面的程式碼在執行的時候很可能出現錯誤:
    
Unhandled exception. System.Exception: Failure to create component template `ecs_8.6.0_agent` for logs-data-set-*: Invalid Elasticsearch response built from a unsuccessful () low level call on PUT: /_component_template/ecs_8.6.0_agent
 Exception: Failed to ping the specified node. Call: unknown resource

# Audit trail of this API call:
 - [1] AllNodesDead: Took: 00:00:00.0000026
 - [2] Resurrection: Node: http://localhost:9200/ Took: 00:00:00.0000002
 - [3] PingFailure: Node: http://localhost:9200/ Exception: PipelineException Took: 00:00:00.0027939
# OriginalException: Elastic.Transport.TransportException: Failed to ping the specified node. Call: unknown resource

    

原因是將資料傳送到 http://localhost:9200 時需要驗證,而我們上面沒有輸入帳號密碼,但是新版的 Elastic.Serilog.Sinks 套件沒有可以輸入帳號密碼的位置,不過還好我們可以透過在 URL 中塞入帳號密碼的方式通過驗證:

把上面的網址替換為下面這樣即可:
    
http://elastic:changeme@localhost:9200
    

註:預設帳號: elastic 預設密碼: changeme

然後開啟 http://localhost:5601/app/logs/stream ,此頁面位於側邊欄 Observability > Logs 中的 Stream,調整為最近的時間後應該就會看到 log 了

如果有修改程式碼中 DataStreamName 的第一個參數 logs ,則很可能會接收不到資料,是因為有預設的過濾條件,如果有修改的話記得在右上角的 Settings 然後修改 Log sources 中的 Log indices ,將程式碼中的內容加上「-*」即可。



參考資料:
GitHub - Serilog.Sinks.Elasticsearch
GitHub - elastic/ecs-dotnet
elastic - Elastic.Serilog.Sinks
elastic - An introduction to the Elastic data stream naming scheme
stack overflow - Serilog + serilog-sinks-elasticsearch +ElasticSearch Auth

留言