監控、日誌與版本管理

是什麼?

這三件事常被混為一談,但解決的是不同問題:

ℹ️一句話定位

監控是「儀表板上的紅燈」,日誌是「黑盒子飛行記錄器」,版本是「貨物上的批號」。三者搭配,才有辦法在凌晨三點被叫起來時,快速判斷「壞了沒、為什麼壞、是哪一版壞的」。

監控怎麼做

Metrics(指標)

指標是隨時間變化的數值量測,通常分三類:

Health Check(健康檢查)

提供一個端點(如 /health)讓負載平衡器、Kubernetes、監控系統定期探測。通常分兩種:

四大黃金訊號(Four Golden Signals)

Google SRE 提出的監控起手式,不知道要監控什麼就先盯這四個:

  1. 延遲(Latency):請求花多久。要區分「成功請求的延遲」與「失敗請求的延遲」。
  2. 流量(Traffic):系統承受多少需求,如 RPS(每秒請求數)。
  3. 錯誤(Errors):失敗請求的比率(HTTP 5xx、逾時)。
  4. 飽和度(Saturation):系統有多滿(CPU、記憶體、磁碟、連線池)。

SLI / SLO / SLA

SLO 與實際的差距就是錯誤預算(Error Budget)——預算還夠就大膽發版,燒光了就先穩定再說。

APM 與告警(Alerting)

APM(Application Performance Monitoring)工具(Application Insights、Datadog、New Relic)能做分散式追蹤,把一個請求穿過多個微服務的路徑串起來,找出是哪一段慢。告警則是設定門檻,超標就通知(Slack、PagerDuty、Email)。原則是對症狀告警,而非對原因告警——使用者在意的是「網頁打不開」,不是「某台機器 CPU 80%」。

日誌怎麼做

結構化日誌 vs 純文字

純文字日誌 "User 123 failed login at 10:30" 人看得懂,但機器難以查詢。結構化日誌把它寫成帶欄位的事件(JSON):

json
{ "Level": "Warning", "Event": "LoginFailed", "UserId": 123, "Timestamp": "2026-06-05T10:30:00Z" }

之後就能在集中平台用 UserId == 123 AND Event == "LoginFailed" 直接查詢、聚合、畫圖。對資深開發者來說,這是現代日誌的及格線。

Log Level(日誌等級)

由低到高:TraceDebugInformationWarningErrorCritical。生產環境通常開到 Information,問題排查時才臨時調低。重點是選對等級:可預期的使用者錯誤(如表單驗證失敗)是 InformationWarning,不該用 Error 把告警淹掉。

Correlation ID(關聯追蹤)

一個請求可能跨好幾個服務與非同步流程。給每個請求一個 Correlation ID(如 X-Correlation-ID 標頭),讓它貫穿所有相關日誌,事後就能用一個 ID 把整條鏈路的日誌全撈出來。在 .NET 中常搭配 ILogger.BeginScopeActivity.Current.TraceId(W3C Trace Context)。

⚠️絕對不要記進日誌的東西

密碼、API 金鑰、JWT、信用卡號、身分證字號等 PII(個人可識別資訊)。日誌往往保存數月、被多人存取、還可能外送到第三方平台——記了敏感資料等於資料外洩。台灣《個資法》與 GDPR 都會找上門。記得脫敏(masking)或乾脆不記。

.NET 程式碼範例

ILogger 結構化記錄

關鍵:用訊息範本(message template)的具名參數,而不是字串插值。前者保留了結構化欄位,後者只剩一坨字串。

csharp
// 正確:OrderId、Amount 會成為可查詢的結構化欄位
logger.LogInformation("訂單建立成功 {OrderId},金額 {Amount}", order.Id, order.Total);
 
// 錯誤:插值會破壞結構化,欄位全黏成一個字串
logger.LogInformation($"訂單建立成功 {order.Id},金額 {order.Total}");

Serilog 設定(寫入 Console 與 Seq)

csharp
builder.Host.UseSerilog((ctx, cfg) => cfg
    .MinimumLevel.Information()
    .Enrich.FromLogContext()                 // 讓 scope/correlation 自動帶進每筆 log
    .WriteTo.Console(new JsonFormatter())    // 結構化輸出
    .WriteTo.Seq("http://localhost:5341"));  // 集中式平台

Health Checks 註冊

csharp
builder.Services.AddHealthChecks()
    .AddDbContextCheck<AppDbContext>(name: "database")
    .AddUrlGroup(new Uri("https://payment-api/health"), name: "payment");
 
var app = builder.Build();
app.MapHealthChecks("/health");

Correlation ID Middleware

csharp
app.Use(async (context, next) =>
{
    var correlationId = context.Request.Headers["X-Correlation-ID"].FirstOrDefault()
                        ?? Activity.Current?.TraceId.ToString()
                        ?? Guid.NewGuid().ToString();
 
    // 之後此 scope 內的每一筆 log 都會自動帶上 CorrelationId
    using (LogContext.PushProperty("CorrelationId", correlationId))
    {
        context.Response.Headers["X-Correlation-ID"] = correlationId;
        await next();
    }
});

監控/日誌資料流

應用程式把「日誌事件」與「指標」往外送,匯集到集中平台,再分流到儀表板與告警通道。集中化的價值在於:跨多個服務實例的資料能被統一查詢與關聯。

應用程式 (ILogger / Serilog / Metrics)
寫出事件
結構化日誌 (JSON)
匯入
指標 (Counter / Gauge / Histogram)
匯入
集中平台 (Seq / ELK / App Insights)
視覺化
儀表板 (Grafana / Kibana)
告警 (PagerDuty / Slack)

版本更新指的是什麼

「版本更新」這個詞被濫用,但對工程師而言它其實涵蓋好幾種不同層次的版本,各有各的規則。

語意化版本 SemVer:MAJOR.MINOR.PATCH

業界共識的版本號規則,格式為 主版本.次版本.修訂號,例如 2.4.1

1

MAJOR 主版本

破壞性變更(breaking change),舊使用者升級後會壞掉。例:移除 API、改方法簽章。2.x.x → 3.0.0

2

MINOR 次版本

向後相容的新功能。舊程式不改照樣能跑。例:新增一個可選參數、新增端點。2.4.x → 2.5.0

3

PATCH 修訂號

向後相容的錯誤修正。沒有新功能、沒有破壞性。例:修 bug、補安全漏洞。2.4.1 → 2.4.2

規則重點:升級 MAJOR 時,MINOR 與 PATCH 歸零(2.4.1 → 3.0.0);升 MINOR 時 PATCH 歸零。0.x.y 屬於開發初期,此時可視為不保證相容。預先發行版用 1.0.0-rc.12.0.0-beta 等後綴標示。

四種要分清楚的「版本」

ℹ️它們各自獨立演進

應用程式從 5.2.0 升到 5.3.0(新功能),底下 API 可能還停在 v2,資料庫剛跑完第 87 號 migration,而 EF Core 套件從 8.0.3 升到 8.0.4。四個版本號互不相干、各管各的相容性——把它們混在一起談,正是溝通混亂的根源。

常見誤區

⚠️這些坑很多老手也會踩

  • Log 太多沒人看:什麼都記 Information,真正的 Error 被淹沒在雜訊裡。日誌要有取捨,不是愈多愈好。
  • 記了敏感資料:把整個 request body(含密碼、token)原封不動塞進日誌,變成資安與法遵風險。
  • 只監控機器不監控業務:盯著 CPU、記憶體很滿足,卻沒人發現「過去一小時結帳成功率掉到 0」。要監控業務指標(訂單量、付款成功率),那才是使用者真正在意的。
  • 版本號亂跳:明明是破壞性變更卻只升 PATCH,害下游升級後炸掉;或是版號跟著心情跳,失去 SemVer 的溝通價值。

實戰補充

Q:監控(Monitoring)和可觀測性(Observability)差在哪? A:監控是「監看你事先知道會出問題的已知指標」——你預先定義好儀表板與告警。可觀測性則更廣,是「系統對外輸出的資料,足以讓你事後探查從未預期過的問題」。它建立在三大支柱:Metrics(指標)、Logs(日誌)、Traces(追蹤)。簡言之:監控回答「已知的問題現在如何」,可觀測性讓你回答「連我都還不知道的問題到底怎麼回事」。監控是可觀測性的子集。

Q:到底該設哪些告警? A:少而精。優先對症狀可付諸行動的事件告警:對應 SLO 的指標(成功率、p99 延遲),以及四大黃金訊號中對使用者有感的部分。避免對「單一機器 CPU 高」這類原因告警——那容易誤報、半夜吵醒人卻沒事可做。一個好原則:每個告警響起時,接手的人都該明確知道要做什麼;如果不知道,這個告警就該刪掉或改寫。

理解測驗

🤔 一個原本沒有 ProductId 參數的查詢方法,新增了一個「可選」的 ProductId 參數,舊呼叫端完全不用改。依照 SemVer,版本號該怎麼變?

🤔 關於結構化日誌,下列哪個 ILogger 寫法是「正確」的做法?

🤔 監控(Monitoring)與可觀測性(Observability)的關係,下列敘述何者最貼切?

重點整理

💡一句話記住

監控告訴你「壞了」,日誌告訴你「為什麼壞」,版本告訴你「是哪一版壞的」——三者到位,凌晨三點才睡得著。

主題核心問題.NET 工具關鍵實務
監控 Metrics現在正常嗎?App Insights / Prometheus看 p95/p99、四大黃金訊號
健康檢查服務活著且就緒?AddHealthChecks分 Liveness / Readiness
告警 SLI/SLO是否違反目標?Grafana / PagerDuty對症狀告警、可付諸行動
日誌發生了什麼?ILogger / Serilog結構化、Correlation ID、不記 PII
版本 SemVer是哪一版?相容嗎?NuGet / EF MigrationsMAJOR.MINOR.PATCH、四種版本分開管

你可能也想看

測試策略與測試報告SRE 入門:從後端 RD 到網站可靠性工程

按 ← → 鍵切換課程