Seq — 結構化日誌聚合工具

Seq 是一個具有超強大的集中式日誌文件,日誌事件以 Json格式存儲,可以透過 UI 查詢過濾日誌事件、圖表儀表資料可視化、整合其他應用發送警報通知。

Joe Chiu
appxtech
Published in
14 min readMay 25, 2023

--

Get started

mac 用戶可以從Docker hub 拉取官方提供的 image

PH=$(echo '123456' | docker run --rm -i datalust/seq config hash)

docker run \
--name seq \
-d \
--restart unless-stopped \
-e ACCEPT_EULA=Y \
-e SEQ_FIRSTRUN_ADMINPASSWORDHASH="$PH" \
-v /Users/softbro/Seq/data:/data \
-p 80:80 \
-p 5341:5341 \
datalust/seq
  • :80 as the UI port. This port allows both ingestion and API requests.
  • :5341 as the ingestion only port. This port doesn't allow API requests.
  • /data as the Seq data directory.

container 啟動以後,可以在本地 80 port 開起 Seq 服務UI畫面

設定 Api Key

點擊右上方 settings 進到設定頁,點擊下方 add api key 輸入 title, Attached properties,然後 save change 會得到一個 apiKey token,Seq平台會認這個apiKey 來區分發送事件的是哪一個服務。

ILogggerProvider 串接 Seq

比較多人使用的 .net logger 都有支援 Seq串接,基本上只要設定 serverUrl 跟 apiKey,和重置原本預設框架寫好的 logger provider。

以下範例都是會由 DI 注入 Ilogger 到 controller 在 action 中打印這幾個log資訊,程式:

[HttpGet("Index")]
public IActionResult Index()
{
var ex = new NullReferenceException();

_logger.LogInformation("Hello, {Name}!", "world");
_logger.LogDebug("Hello, again!");
_logger.LogError("Joe Error");
_logger.LogError(ex, "空引用異常");
_logger.LogCritical("伺服器錯誤");
_logger.LogWarning("警告");
using (_logger.BeginScope("Process {OrderId}", Guid.NewGuid().ToString()))
using (_logger.BeginScope(new Dictionary<string, object> { ["MessageId"] = 100 }))
{
_logger.LogInformation("Events in this block have additional properties attached");
}
return Ok();
}

1.Microsoft.Extensions.Logging

Install Seq extension

dotnet add package Seq.Extensions.Logging --version 6.1.0

Add service (logging provider)

// Program.cs

var builder = WebApplication.CreateBuilder(args);

builder.Logging
.ClearProviders()
.AddConsole()
.AddSeq();

// ....

Logger config

// appsettings.json
{
"Logging": {
"Seq": {
"ServerUrl": "<http://localhost:5341>",
"ApiKey": "0m9KWRKaH0ezpigfcXSR"
}
},
// ...
}

結果

2.NLog

NLog 基本設定

Install package

dotnet add package NLog --version 5.1.4
dotnet add package NLog.Web.AspNetCore --version 5.2.3

nlog.config example

<?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"
internalLogLevel="Info"
internalLogFile="c:\temp\internal-nlog-AspNetCore.txt">

<!-- enable asp.net core layout renderers -->
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>

<!-- the targets to write to -->
<targets>
<!-- File Target for all log messages with basic details -->
<target xsi:type="File" name="allfile" fileName="c:\temp\nlog-AspNetCore-all-${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}" />

<!-- File Target for own log messages with extra web details using some ASP.NET core renderers -->
<target xsi:type="File" name="ownFile-web" fileName="c:\temp\nlog-AspNetCore-own-${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />

<!--Console Target for hosting lifetime messages to improve Docker / Visual Studio startup detection -->
<target xsi:type="Console" name="lifetimeConsole" layout="${MicrosoftConsoleLayout}" />
</targets>

<!-- rules to map from logger name to target -->
<rules>
<!--All logs, including from Microsoft-->
<logger name="*" minlevel="Trace" writeTo="allfile" />

<!--Output hosting lifetime messages to console target for faster startup detection -->
<logger name="Microsoft.Hosting.Lifetime" minlevel="Info" writeTo="lifetimeConsole, ownFile-web" final="true" />

<!--Skip non-critical Microsoft logs and so log only own logs (BlackHole) -->
<logger name="Microsoft.*" maxlevel="Info" final="true" />
<logger name="System.Net.Http.*" maxlevel="Info" final="true" />

<logger name="*" minlevel="Trace" writeTo="ownFile-web" />
</rules>
</nlog>

NLog write to Seq

Install package

dotnet add package NLog.Targets.Seq

Add extensions, target, rules to nlog.config


<extensions>
<add assembly="NLog.Targets.Seq"/>
</extensions>

<targets>
<target name="seq" xsi:type="BufferingWrapper" bufferSize="1000" flushTimeout="2000">
<target xsi:type="Seq" serverUrl="http://localhost:5341" apiKey="55mvJW87ZneipLvlTU3q" />
</target>
</targets>

<rules>
<logger name="*" minlevel="Info" writeTo="seq" />
</rules>

Add service

// Program.cs

// 使用 NLog, 停用 builder 預設 logger
builder.Logging.ClearProviders();
builder.Host.UseNLog();

結果

3.Serilog

Install package

dotnet add package Serilog.AspNetCore --version 6.1.0
dotnet add package Serilog.Sinks.Seq --version 5.2.2

Set logger provider

builder.Host.UseSerilog((hostContext, loggerConfiguration) =>
loggerConfiguration.ReadFrom.Configuration(hostContext.Configuration)
);

appsettings.json

 "Serilog": {
"Using": [
"Serilog.Sinks.Console",
"Serilog.Sinks.Seq"
],
"MinimumLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
},
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "Seq",
"Args": {
"serverUrl": "http://localhost:5341",
"apiKey": "lXF2gmkcqNnvBWG2QrHp"
}
}
]
},

結果

Alert

seq 服務示意圖

事前準備

slack workspace & slack webhook url

// 測試

curl -X POST -H 'Content-type: application/json' --data '{"text":"Hello, World!"}' [Webhook URL]

設定 seq alert

  1. install app

在 Seq 裡面想要整合其他應用做警報通知,有很多開源的套件,在UI上操作即可安裝擴展套件

擴展套件有非常多選擇,在下方有條列和 more 會傳送到 Nuget 平台可以看更多; 點選 slack 以及 Install 按鈕

2. add instance

安裝完 Slack 擴展套件之後,Slack App 會出現在UI上,點選新增 instance

填寫 Instance Title, Webhook URL

Instance 會出現 Slack App 裡面,這時候也代表我們能在 Alert 設定中,綁定我們的 Instance,也就是對應到我們 Instance 設定 Webhook url 的 slack workspace

3. add alert

設定 Alert Title, Trigger, Notifications && 保存

Notifications 可以加上自己需要的 properties

啟動有綁好 Seq Api Key的服務,trgger 我們寫好有 log error 的 api endpoint

!! 通知警報 !!

簡單快的設定不用任何程式就將警報發送給 Slack了!

結論

這次學習讓我明白,結構化日誌比一般文字更適合有效查詢和過濾,而且能讓日誌事件有統一的規範和標準; 日誌集成工具對於集群服務來說是必不可少的,不像本地文件那樣有局限性; 另外,Seq作為一個統一的日誌管理服務,可以設定和監看所有服務的警報通知,集中中心化管理,而不會讓警告規則零散在各地!

這次學習讓我感受到了 .net 的日誌開發框架的強大,.net 日誌框架提供了易用的 api 基礎,即便不借助其他套件也能實現日誌功能,而且不需要過多的配置,高默認低配置的理念讓開發人員享受非常愉快的體驗; 根據更複雜需求的情境,可以使用第三方開發者提供的擴充套件,只要更換 logger provider 和配置檔案就可以完成套件切換,完全不影響業務邏輯的部分,這要歸功於 .net 框架在設計時就遵循了物件導向的開放封閉和依賴倒置的原則,我從來沒有想過有一天能夠在同一個功能上使用三個不同的套件,如果沒有 .net 這樣的 log 基礎建設 ,我要花更多時間才能熟悉上手,學習了 .net 框架近三個月,真的讓我越來越喜歡這個框架,它充滿了前人的智慧!

Depend on stable abstractions and modify system’s behavior by providing different realizations

--

--