CodeGym /課程 /C# SELF /效能監控與指標蒐集

效能監控與指標蒐集

C# SELF
等級 64 , 課堂 3
開放

1. 介紹

假設你是網站管理員,每天有一千個使用者來訪。在日誌中你看到一切正常,幾乎沒有錯誤(最多有人偶爾忘記密碼或輸入 captcha 錯誤)。「一切都很好!」——你這麼想。

然後有人寫信到客服:網站卡得要死,頁面要 10 秒才開。你去看日誌——沒有錯誤!太棒了?不,因為日誌告訴你發生了什麼(或沒發生),但不會告訴你有多快或多慢、為此用了多少資源,以及在負載增加時這些行為如何變化。

這時候就輪到指標上場——可以量化的應用運行特性。它們不只是錯誤數,還有平均響應時間、記憶體使用量、每秒請求數等,可以據此判斷系統健康狀況。

比較:
日誌 — 是 "發生了什麼"
指標 — 是 "系統運作得好還是不好"
追蹤 — 是 "為何系統會這樣運作(細節)"

有哪些指標,該蒐集哪些?

主要的指標類型:

指標類型 範例 用途
計數器 (Counters) 請求數、錯誤、失敗次數 趨勢、告警、負載
直方圖 (Histograms) 回應時間、封包大小 值分布、百分位
量測表 (Gauge) 記憶體使用、CPU 資源當前狀態
總和 (Sum) 資料總量、位元組 一段時間內的操作總量

範例:

  • GET 請求的平均和 95 百分位回應時間。
  • 當前線上使用者數。
  • 記憶體使用 (Private Bytes, Working Set)。
  • 500/503 類錯誤的發生頻率 (500/503)。
  • 每分鐘的資料庫請求數。

這些指標不僅幫你找問題,還能幫你預防問題——例如伺服器負載升高或回應時間逐漸上升可能預示將來會有故障。

2. .NET 與 OpenTelemetry 指標蒐集架構

整體架構

在現代 .NET(從 .NET 6 起,特別是 .NET 8/9)有一套標準的指標蒐集系統,基於 OpenTelemetry

運作流程如下:

  1. 應用程式程式碼 呼叫方法來增加 counters、註冊 gauges、記錄 histograms。
  2. OpenTelemetry Metrics SDK 在記憶體中蒐集這些指標,並定期送出。
  3. 指標匯出器 把指標傳到選定的監控系統(Prometheus、Application Insights、Grafana Cloud、Datadog 等)。
  4. 監控後端 聚合、儲存、視覺化,並建立告警與儀表板。
示意流程圖:

[你的應用程式] 
       ⬇ 
 [指標蒐集 (OpenTelemetry SDK)]
       ⬇
 [指標匯出器 (Prometheus, AI, Datadog, ...)]
       ⬇
 [監控系統/儀表板/告警]

3. 實作:在 C# 中使用指標的基礎

簡單的內部指標: System.Diagnostics.Metrics

.NET 提供內建的指標機制 — System.Diagnostics.Metrics

主要角色: Meter, Counter<T>, Histogram<T>, ObservableGauge<T>

範例:首頁訪問計數器


// 建立 Meter(通常整個應用只有一個)
using System.Diagnostics.Metrics;

static Meter meter = new Meter("MyCompany.MyApp", "1.0");

// 註冊計數器
static Counter<long> homePageVisits = meter.CreateCounter<long>("HomePageVisits");

// 在 controller 或 service 的某處...
public void HomePageRequested()
{
    homePageVisits.Add(1);
    // 其他頁面處理程式碼
}

註解:

  • Meter 是用來產生指標的「工廠」,使用唯一名稱(應用/公司命名空間)。
  • CreateCounter<long> 建立計數器;用 Add(1) 做遞增。

範例:測量回應時間


static Histogram<double> pageLoadTime = meter.CreateHistogram<double>("PageLoadTimeMs");

// 在請求處理器中:
public void OnRequest()
{
    var stopwatch = System.Diagnostics.Stopwatch.StartNew();

    // ...這裡是實際的請求處理...

    stopwatch.Stop();
    pageLoadTime.Record(stopwatch.Elapsed.TotalMilliseconds);
}

為動態值建立 Gauge

Gauge 是會隨時間變動的度量:線上使用者數、目前記憶體大小等。


static ObservableGauge<int> onlineUserGauge = meter.CreateObservableGauge(
    "OnlineUsers", 
    () => GetOnlineUserCount());

// GetOnlineUserCount 是回傳即時值的方法
static int GetOnlineUserCount()
{
    // 這裡放你實際的邏輯!
    return ActiveUserList.Count;
}

在實務上這些都是非同步運作:應用程式報告指標值,匯出器去抓取它們(例如 Prometheus 會 scrape "/metrics" 端點)。

在現代 ASP.NET Core 應用中加入指標

對於 ASP.NET Core,很多東西開箱就支援。只要加上套件 OpenTelemetry.Instrumentation.AspNetCore,就會有 HTTP 請求、回應時間、錯誤數等指標。

Program.cs 的設定範例:


using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenTelemetry()
    .WithMetrics(metrics =>
    {
        metrics
            .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("MyApp"))
            .AddAspNetCoreInstrumentation() // HTTP 指標
            .AddRuntimeInstrumentation()    // .NET CLR 的運行時指標
            .AddProcessInstrumentation()    // 程序的 CPU/記憶體
            .AddMeter("MyCompany.MyApp")    // 你的自訂指標來源
            .AddPrometheusExporter();       // 匯出到 Prometheus
    });

var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();

現在你的應用會在 /metrics 提供指標,Prometheus 或其他系統可以去 scrape。

4. 指標使用的實務範例

在真實專案中監控效能

連上指標來回答:

  • API 的平均與峰值 RPS (requests per second) 是多少?
  • 瓶頸在哪:一個 endpoint 是 300 ms,另一個是 2000 ms?
  • 呼叫 DB 花了多少時間?(自訂 histograms)

範例:監控資料庫查詢時間


static Histogram<double> dbQueryDuration = meter.CreateHistogram<double>("DbQueryDurationMs");

public async Task<List<Product>> GetProductsAsync()
{
    var sw = Stopwatch.StartNew();
    var result = await _db.Products.ToListAsync();
    sw.Stop();
    dbQueryDuration.Record(sw.Elapsed.TotalMilliseconds);
    return result;
}

範例:計算錯誤數


static Counter<long> apiErrors = meter.CreateCounter<long>("ApiErrors");

public IActionResult SomeEndpoint()
{
    try
    {
        // 某些操作
        return Ok();
    }
    catch (Exception)
    {
        apiErrors.Add(1);
        throw;
    }
}

使用 labels (tags) 來標註指標

把資料依有意義的維度分組很重要:endpoint、錯誤類型、使用者類型等。


homePageVisits.Add(
    1, 
    KeyValuePair.Create<string, object>("UserType", "Admin"));

或者對直方圖:


dbQueryDuration.Record(
    sw.Elapsed.TotalMilliseconds, 
    KeyValuePair.Create<string, object>("QueryType", "GetProducts"));

靠 tags 在 Grafana 就能做更細的切片分析,而不只看整個應用的總體數據。

5. 與 Prometheus、Application Insights、Datadog、Grafana 的整合

匯出器與整合

  • Prometheus — 流行的 open-source 監控系統,已成為雲端與 Kubernetes 的事實標準。
  • Application Insights — Azure 的雲端整合方案。
  • Datadog, Grafana Cloud — 適合專業級基礎設施。

這些系統都可以透過 OpenTelemetry 的匯出器來接收 .NET 的指標。OTel 匯出器文件

Prometheus(步驟):

  1. 加入 NuGet 套件: OpenTelemetry.Exporter.Prometheus
  2. 在指標註冊時加上 .AddPrometheusExporter()
  3. 在 Grafana 設定 datasource 指向 Prometheus,並建立儀表板。

實用連結:

6. 特性、陷阱與常見錯誤

常見錯誤之一是 tags 過於細緻。如果給 tags 太多唯一值(例如使用者 ID 或訂單 ID),時序列 (time series) 數量會暴增 —— 這會造成指標儲存過載和成本上升(所謂的 cardinality explosion)。把 tags 維持在粗粒度。

有些開發者會忽視 System.Diagnostics.Metrics 與現成工具,自己用日誌和計時做「自造輪子」。結果是整合性差、維護麻煩。建議使用標準工具和自動化 instrumentations。

另一個錯誤是 指標有蒐集但沒有匯出。匯出器是必要的:例如加入 .AddPrometheusExporter(),並確認 /metrics 端點對 scrape 可用。

最後一點,別把指標類型搞混:平均回應時間不應只用 Counter 去計算,而是要用 Histogram —— 否則你看不到峰值與分布。Counters 用來計數;Histograms 用來時間/大小/分布;Gauges 用來當前狀態。

2
任務
C# SELF, 等級 64, 課堂 3
上鎖
使用直方圖來測量操作執行時間
使用直方圖來測量操作執行時間
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION