Node.js Diagnostics Channel:解耦你的業務邏輯與監控程式碼

在開發大型生產環境應用時,我們通常需要監控 API 的響應時間、資料庫查詢的頻率或是快取的命中率。傳統做法是在業務邏輯中夾雜大量的日誌或是監控上報程式碼:

// 傳統侵入式寫法
async function getUser(id) {
  const start = Date.now();
  const user = await db.query(id);
  metrics.report('db_time', Date.now() - start); // 業務邏輯中混入了監控
  return user;
}

這會讓程式碼變得難以維護且難以測試。node:diagnostics_channel 模組提供了一個「發佈/訂閱」機制,讓你能完全解耦業務邏輯與診斷數據。

核心原理:發佈與訂閱

診斷頻道的核心概念是建立一個命名的管道。生產者 (Producers) 負責發送數據,而消費者 (Consumers) 負責接收並處理(如轉發到 Prometheus 或 Datadog)。

實戰開發:實作一個解耦的資料庫監控

1. 定義頻道與發送數據 (生產者)

在你的資料庫模組或基礎元件中,只需專注於發送事件。

const dc = require('node:diagnostics_channel');

// 建立或獲取一個名為 'db.query' 的頻道
const channel = dc.channel('db.query');

async function performQuery(sql) {
  const startTime = process.hrtime();

  // 執行業務邏輯...
  const result = await someDbDriver.execute(sql);

  // 如果頻道有人訂閱,則發送診斷數據
  if (channel.hasSubscribers) {
    channel.publish({
      sql,
      duration: process.hrtime(startTime),
      status: 'success',
    });
  }

  return result;
}

2. 接收數據並進行上報 (消費者)

在程式的入口點 (Entry point) 或專門的監控模組中訂閱數據。

const dc = require('node:diagnostics_channel');

// 訂閱特定頻道
dc.subscribe('db.query', (message, name) => {
  console.log(`[監控] 收到 SQL 執行紀錄: ${message.sql}`);
  console.log(`[監控] 耗時: ${message.duration[1] / 1000000} ms`);
});

內建頻道的強大力量

Node.js 核心模組(如 httpnet)已經內建了許多診斷頻道。你不需要修改 Node.js 的原始碼,就能監聽到內部事件。

例如,監控所有對外的 HTTP 請求:

const dc = require('node:diagnostics_channel');

dc.subscribe('undici:request:create', ({ request }) => {
  console.log(`偵測到對外連線:${request.method} ${request.origin}`);
});

為什麼選擇 Diagnostics Channel?

  1. 極致效能:如果沒有人訂閱頻道,publish 幾乎不會產生任何運算開銷。
  2. 完全解耦:監控邏輯與業務邏輯分開存放,測試業務邏輯時不需要 Mock 複雜的監控 API。
  3. OpenTelemetry 友好:它是現代 APM 工具(如 OpenTelemetry Node SDK)底層獲取核心數據的主要手段。

總結

  1. diagnostics_channel 是企業級應用實現無侵入監控的關鍵。
  2. 透過 channel.publishdc.subscribe 實現邏輯分離。
  3. 善用 Node.js 內建的頻道,能讓你對系統運行狀況有更深度的洞察。