由於原本的環境是將log寫至文字檔.但後期希望在新的開發上.
能保留現有的log機制但只保留最新的舊的能夠自動刪除.
當網路環境不穩定時,能夠先保留著本機的log檔,等到網路正常時可以正常傳送log資訊.
可以依照不同的專案存入指定的地方.
所以需求情境上整理如下
- 當網路環境延宕時,重啟後能夠做到續傳
- log文字檔能夠保留最新,自動刪除舊有的
- 能依不同專案將log區分
在腦補兼google的情況下,當然是用serilog嚕,
除了他整合至Microsoft.Extensions.Logging以外
擴充套件的完整與彈性也是相當完善
如下圖
安裝環境與ELK版本 (小弟在此就不多贅述如何安裝,會在參考連結中擺放相關資訊)
Ubuntu 18.04
Elasticsearch v7.9.1
Kibana v7.9.1
Logstash v7.91
net core 3.1 所需安裝套件
Serilog.AspNetCore v3.4.0
dotnet add package Serilog.AspNetCore --version 3.4.0
Serilog.Settings.Configuration v3.1.0
dotnet add package Serilog.Settings.Configuration --version 3.1.0
Serilog.Sinks.Async v1.4.0
dotnet add package Serilog.Sinks.Async --version 1.4.0
Serilog.Sinks.Http v 7.0.1
dotnet add package Serilog.Sinks.Http --version 7.0.1
流程如下圖
接下來就是實作的部分嚕
Program.cs
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog((ctx,config) =>
{
//設定只有Error時才紀錄
config.MinimumLevel.Error();
//設定送至logstash位置
config.WriteTo.Http("http://localhost:8080/project_a");
})
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
}
很簡單易用,透過http這個方法,就可以將log資訊送往Logastash.
不過依照Serilog.Sinks.Http官方說明.當網路延宕重啟時他並不會保留數據如下圖說明
這樣就無法滿足第1,2點的需求
所幸Serilog.Sinks.Http 內提供另外兩個方法.
相同的是都會在本機儲存檔案,當網路延宕重啟時,會傳送尚未傳送的log
差異如下
透過bufferFileSizeLimitBytes 此參數(預設是1G=1024*1024*1024 ) 設定Log檔案大小,
當網路延宕發生時,會將Log紀錄保存至實體硬碟中,如果當單一檔案大小超過所設定或預設的數值,
則需等待下一次產生檔案的時間.
bufferPathFormat參數範例:
Buffer-{Date}.json -30-60分鐘之間自動產生
Buffer-{HalfHour}.json -每30分鐘產生
Buffer-{Hour}.json -每小時產生
簡單來說呢...就是當你log檔案超出你所設定的檔案大小,你就要等待新的log檔產生,才會繼續寫入log並往Logstash送
DurableHttpUsingTimeRolledBuffers
bufferFileSizeLimitBytes參數設定(預設是1G=1024*1024*1024 )單一Log檔案大小,
當網路延宕發生時,會將Log紀錄保存至實體硬碟中,如果當單一檔案大小超過所設定或預設的數值,
則會產生 {bufferBaseFileName 參數所定義的名稱}-{日期}_001.json 以此類推.....
當網路恢復正常時,會將未寄送的紀錄送出至Logstash,只保留兩個最新的檔案.舊的則會移除
DurableHttpUsingFileSizeRolledBuffers
兩相比較之後DurableHttpUsingFileSizeRolledBuffers確實能滿足原有需求1,2點.動動手改一下XD
Program.cs
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog((ctx,config) =>
{
config.MinimumLevel.Error();
config.WriteTo
.DurableHttpUsingFileSizeRolledBuffers(
"http://localhost:8080/project_a",
"test"
);
})
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
}
關於第三點,就從logstash 設定檔動手,依照request path去區分該往哪個index放
logstash.conf
filter {
split {
field => "events"
target => "e"
}
mutate {
add_field => {"project_name" => "%{[headers][request_path]}"}
remove_field => ["events","headers"]
}
}
output {
stdout { codec => rubydebug }
if [project_name] == "/project_a" {
elasticsearch {
hosts => ["http://localhost:9200/"]
index => "test-logs"
document_type => "log"
user => "elastic"
password => "1qaz2wsx"
}
}
}
最後總是得測試一下.
LogSampleTestController.cs
[ApiController]
[Route("[controller]")]
public class LogSampleTestController : ControllerBase
{
private readonly ILogger<LogSampleTestController> _logger;
public LogSampleTestController(ILogger<LogSampleTestController> logger)
{
_logger = logger;
}
[HttpGet]
public IActionResult Get()
{
var logObj = new
{
name="toast",
height=170,
weight="secret",
address="secret"
};
_logger.LogError(JsonConvert.SerializeObject(logObj));
return Ok();
}
}
最後開啟Kibana的網頁確認資料是否有寫入ES中
看樣子有正確寫入至ES.
Serilog 在使用上相對簡單,也省去自己或團隊上在重刻輪子的時間 XD.