deno.com
本頁面內容

OpenTelemetry

注意

Deno 的 OpenTelemetry 整合仍在開發中,可能會有所變動。若要使用它,您必須將 --unstable-otel 旗標傳遞給 Deno。

Deno 內建支援 OpenTelemetry

OpenTelemetry 是一系列 API、SDK 和工具的集合。使用它可以檢測、產生、收集和匯出遙測資料(指標、記錄和追蹤),以協助您分析軟體的效能和行為。

- https://opentelemetry.io/

此整合讓您可以使用 OpenTelemetry 可觀測性工具(如記錄、指標和追蹤)來監控您的 Deno 應用程式。

Deno 提供以下功能

快速開始 跳到標題

若要啟用 OpenTelemetry 整合,請使用 --unstable-otel 旗標執行您的 Deno 腳本,並設定環境變數 OTEL_DENO=true

OTEL_DENO=true deno run --unstable-otel my_script.ts

這將自動收集執行階段可觀測性資料,並使用 Protobuf over HTTP (http/protobuf) 將其匯出到 localhost:4318 的 OpenTelemetry 端點。

提示

如果您尚未設定 OpenTelemetry 收集器,您可以透過執行以下命令,開始使用 Docker 中的本機 LGTM 堆疊 (Loki (記錄)、Grafana (儀表板)、Tempo (追蹤) 和 Mimir (指標))

docker run --name lgtm -p 3000:3000 -p 4317:4317 -p 4318:4318 --rm -ti \
	-v "$PWD"/lgtm/grafana:/data/grafana \
	-v "$PWD"/lgtm/prometheus:/data/prometheus \
	-v "$PWD"/lgtm/loki:/data/loki \
	-e GF_PATHS_DATA=/data/grafana \
	docker.io/grafana/otel-lgtm:0.8.1

然後,您可以使用使用者名稱 admin 和密碼 adminhttps://127.0.0.1:3000 存取 Grafana 儀表板。

這將自動收集並匯出執行階段可觀測性資料,例如 console.log、HTTP 請求的追蹤和 Deno 執行階段的指標。深入瞭解自動檢測

您也可以使用 npm:@opentelemetry/api 套件建立自己的指標、追蹤和記錄。深入瞭解使用者定義的指標

自動檢測 跳到標題

Deno 會自動收集並匯出一些可觀測性資料到 OTLP 端點。

此資料會在 Deno 執行階段的內建檢測範圍中匯出。此範圍的名稱為 deno。Deno 執行階段的版本是 deno 檢測範圍的版本。(例如 deno:2.1.4)。

追蹤 跳到標題

Deno 會自動為各種操作建立 span,例如

  • 使用 Deno.serve 提供的傳入 HTTP 請求。
  • 使用 fetch 發出的傳出 HTTP 請求。

Deno.serve 跳到標題

當您使用 Deno.serve 建立 HTTP 伺服器時,會為每個傳入請求建立一個 span。span 會在傳送回應標頭時自動結束(而不是在回應 body 傳送完成時)。

建立的 span 名稱為 ${method}。span 種類為 server

以下屬性會在建立時自動新增至 span

  • http.request.method:請求的 HTTP 方法。
  • url.full:請求的完整 URL(如同 req.url 報告的那樣)。
  • url.scheme:請求 URL 的協定(例如 httphttps)。
  • url.path:請求 URL 的路徑。
  • url.query:請求 URL 的查詢字串。

處理請求後,會新增以下屬性

  • http.status_code:回應的狀態碼。

Deno 不會自動將 http.route 屬性新增至 span,因為路由不是由執行階段所知,而是由使用者處理常式函數中的路由邏輯決定。如果您想將 http.route 屬性新增至 span,您可以在您的處理常式函數中使用 npm:@opentelemetry/api 來執行。在這種情況下,您也應該更新 span 名稱以包含路由。

import { trace } from "npm:@opentelemetry/api@1";

const INDEX_ROUTE = new URLPattern({ pathname: "/" });
const BOOK_ROUTE = new URLPattern({ pathname: "/book/:id" });

Deno.serve(async (req) => {
  const span = trace.getActiveSpan();
  if (INDEX_ROUTE.test(req.url)) {
    span.setAttribute("http.route", "/");
    span.updateName(`${req.method} /`);

    // handle index route
  } else if (BOOK_ROUTE.test(req.url)) {
    span.setAttribute("http.route", "/book/:id");
    span.updateName(`${req.method} /book/:id`);

    // handle book route
  } else {
    return new Response("Not found", { status: 404 });
  }
});

fetch 跳到標題

當您使用 fetch 發出 HTTP 請求時,會為該請求建立一個 span。span 會在收到回應標頭時自動結束。

建立的 span 名稱為 ${method}。span 種類為 client

以下屬性會在建立時自動新增至 span

  • http.request.method:請求的 HTTP 方法。
  • url.full:請求的完整 URL。
  • url.scheme:請求 URL 的協定。
  • url.path:請求 URL 的路徑。
  • url.query:請求 URL 的查詢字串。

收到回應後,會新增以下屬性

  • http.status_code:回應的狀態碼。

指標 跳到標題

以下指標會自動收集和匯出

Deno.serve / Deno.serveHttp 跳到標題

http.server.request.duration 跳到標題

使用 Deno.serveDeno.serveHttp 提供的傳入 HTTP 請求持續時間的直方圖。測量的時間是從收到請求到傳送回應標頭的時間。這不包含傳送回應 body 的時間。此指標的單位是秒。直方圖 bucket 為 [0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1.0, 2.5, 5.0, 7.5, 10.0]

此指標會記錄以下屬性

  • http.request.method:請求的 HTTP 方法。
  • url.scheme:請求 URL 的協定。
  • network.protocol.version:用於請求的 HTTP 協定版本(例如 1.12)。
  • server.address:伺服器正在監聽的位址。
  • server.port:伺服器正在監聽的埠。
  • http.response.status_code:回應的狀態碼(如果請求已處理且沒有發生嚴重錯誤)。
  • error.type:發生的錯誤類型(如果請求處理遇到錯誤)。
http.server.active_requests 跳到標題

在任何給定時間,由 Deno.serveDeno.serveHttp 處理的活動請求數量的量表。這是已收到但尚未回應的請求數量(回應標頭尚未傳送)。此指標會記錄以下屬性

  • http.request.method:請求的 HTTP 方法。
  • url.scheme:請求 URL 的協定。
  • server.address:伺服器正在監聽的位址。
  • server.port:伺服器正在監聽的埠。
http.server.request.body.size 跳到標題

使用 Deno.serveDeno.serveHttp 提供的傳入 HTTP 請求 body 大小的直方圖。此指標的單位是位元組。直方圖 bucket 為 [0, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000]

此指標會記錄以下屬性

  • http.request.method:請求的 HTTP 方法。
  • url.scheme:請求 URL 的協定。
  • network.protocol.version:用於請求的 HTTP 協定版本(例如 1.12)。
  • server.address:伺服器正在監聽的位址。
  • server.port:伺服器正在監聽的埠。
  • http.response.status_code:回應的狀態碼(如果請求已處理且沒有發生嚴重錯誤)。
  • error.type:發生的錯誤類型(如果請求處理遇到錯誤)。
http.server.response.body.size 跳到標題

使用 Deno.serveDeno.serveHttp 提供的傳入 HTTP 請求回應 body 大小的直方圖。此指標的單位是位元組。直方圖 bucket 為 [0, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000]

此指標會記錄以下屬性

  • http.request.method:請求的 HTTP 方法。
  • url.scheme:請求 URL 的協定。
  • network.protocol.version:用於請求的 HTTP 協定版本(例如 1.12)。
  • server.address:伺服器正在監聽的位址。
  • server.port:伺服器正在監聽的埠。
  • http.response.status_code:回應的狀態碼(如果請求已處理且沒有發生嚴重錯誤)。
  • error.type:發生的錯誤類型(如果請求處理遇到錯誤)。

記錄 跳到標題

以下記錄會自動收集和匯出

  • 使用 console.* 方法(例如 console.logconsole.error)建立的任何記錄。
  • 由 Deno 執行階段建立的任何記錄,例如除錯記錄、Downloading 記錄和類似記錄。
  • 導致 Deno 執行階段結束的任何錯誤(來自使用者程式碼和執行階段本身)。

從 JavaScript 程式碼引發的記錄將會與相關的 span 上下文一起匯出(如果記錄發生在活動 span 內)。

console 自動檢測可以使用 OTEL_DENO_CONSOLE 環境變數進行設定

  • capture:記錄會發送到 stdout/stderr,也會與 OpenTelemetry 一起匯出。(預設)
  • replace:記錄僅與 OpenTelemetry 一起匯出,且不會發送到 stdout/stderr。
  • ignore:記錄僅發送到 stdout/stderr,且不會與 OpenTelemetry 一起匯出。

使用者指標 跳到標題

除了自動收集的遙測資料外,您也可以使用 npm:@opentelemetry/api 套件建立自己的指標和追蹤。

您不需要設定 npm:@opentelemetry/api 套件即可將其與 Deno 一起使用。當傳遞 --unstable-otel 旗標時,Deno 會自動設定 npm:@opentelemetry/api 套件。無需呼叫 metrics.setGlobalMeterProvider()trace.setGlobalTracerProvider()context.setGlobalContextManager()。資源、匯出器設定等的所有設定都透過環境變數完成。

Deno 可與 npm:@opentelemetry/api 套件的 1.x 版本搭配使用。您可以直接從 npm:@opentelemetry/api@1 匯入,也可以使用 deno add 在本機安裝套件,然後從 @opentelemetry/api 匯入。

deno add npm:@opentelemetry/api@1

對於追蹤和指標,您都需要分別定義追蹤器和計量器的名稱。如果您要檢測函式庫,您應該以函式庫命名追蹤器或計量器(例如 my-awesome-lib)。如果您要檢測應用程式,您應該以應用程式命名追蹤器或計量器(例如 my-app)。追蹤器或計量器的版本應設定為函式庫或應用程式的版本。

追蹤 跳到標題

若要建立新的 span,請先從 npm:@opentelemetry/api 匯入 trace 物件,並建立新的追蹤器

import { trace } from "npm:@opentelemetry/api@1";

const tracer = trace.getTracer("my-app", "1.0.0");

然後,使用 tracer.startActiveSpan 方法建立新的 span,並將回呼函數傳遞給它。您必須透過呼叫 startActiveSpan 傳回的 span 物件上的 end 方法來手動結束 span。

function myFunction() {
  return tracer.startActiveSpan("myFunction", (span) => {
    try {
      // do myFunction's work
    } catch (error) {
      span.recordException(error);
      span.setStatus({
        code: trace.SpanStatusCode.ERROR,
        message: (error as Error).message,
      });
      throw error;
    } finally {
      span.end();
    }
  });
}

span.end() 應在 finally 區塊中呼叫,以確保即使發生錯誤 span 也會結束。span.recordExceptionspan.setStatus 也應在 catch 區塊中呼叫,以記錄發生的任何錯誤。

在回呼函數內部,建立的 span 是「活動 span」。您可以使用 trace.getActiveSpan() 取得活動 span。「活動 span」將用作在回呼函數(或從回呼函數呼叫的任何函數)內部建立的任何 span(手動或由執行階段自動建立)的父 span。深入瞭解上下文傳播

startActiveSpan 方法會傳回回呼函數的傳回值。

Span 可以在其生命週期內新增屬性。屬性是鍵值對,表示關於 span 的結構化中繼資料。可以使用 span 物件上的 setAttributesetAttributes 方法新增屬性。

span.setAttribute("key", "value");
span.setAttributes({ success: true, "bar.count": 42n, "foo.duration": 123.45 });

屬性的值可以是字串、數字(浮點數)、bigint(鉗制為 u64)、布林值或這些類型的陣列。如果屬性值不是這些類型之一,則會被忽略。

可以使用 span 物件上的 updateName 方法更新 span 的名稱。

span.updateName("new name");

可以使用 span 物件上的 setStatus 方法設定 span 的狀態。recordException 方法可用於記錄 span 生命週期中發生的例外狀況。recordException 會建立一個事件,其中包含例外狀況堆疊追蹤和名稱,並將其附加到 span。recordException 不會將 span 狀態設定為 ERROR,您必須手動執行此操作。

import { SpanStatusCode } from "npm:@opentelemetry/api@1";

span.setStatus({
  code: SpanStatusCode.ERROR,
  message: "An error occurred",
});
span.recordException(new Error("An error occurred"));

// or

span.setStatus({
  code: SpanStatusCode.OK,
});

Span 也可以新增 事件連結。事件是與 span 相關聯的時間點。連結是對其他 span 的參考。

Span 也可以使用 tracer.startSpan 手動建立,它會傳回 span 物件。此方法不會將建立的 span 設定為活動 span,因此它不會自動用作稍後建立的任何 span 或任何 console.log 呼叫的父 span。可以使用 上下文傳播 API 手動將 span 設定為回呼的活動 span。

tracer.startActiveSpantracer.startSpan 都可以採用可選的選項包,其中包含以下任何屬性

  • kind:span 的種類。可以是 SpanKind.CLIENTSpanKind.SERVERSpanKind.PRODUCERSpanKind.CONSUMERSpanKind.INTERNAL。預設為 SpanKind.INTERNAL
  • startTime 代表 span 開始時間的 Date 物件,或代表自 Unix epoch 以來經過的毫秒數的數字。如果未提供,將使用目前時間。
  • attributes:包含要新增至 span 的屬性的物件。
  • links:要新增至 span 的連結陣列。
  • root:一個布林值,指示 span 是否應為根 span。如果為 true,則 span 將沒有父 span(即使存在活動 span)。

在選項包之後,tracer.startActiveSpantracer.startSpan 也可以採用來自 上下文傳播 APIcontext 物件。

OpenTelemetry JS API 文件中深入瞭解完整的追蹤 API。

指標 跳到標題

若要建立指標,請先從 npm:@opentelemetry/api 匯入 metrics 物件,並建立新的計量器

import { metrics } from "npm:@opentelemetry/api@1";

const meter = metrics.getMeter("my-app", "1.0.0");

然後,可以從計量器建立儀器,並用於記錄值

const counter = meter.createCounter("my_counter", {
  description: "A simple counter",
  unit: "1",
});

counter.add(1);
counter.add(2);

每個記錄也可以有相關聯的屬性

counter.add(1, { color: "red" });
counter.add(2, { color: "blue" });

提示

在 OpenTelemetry 中,指標屬性通常應該具有低基數。這表示不應有太多屬性值組合。例如,擁有使用者所在的洲的屬性可能還可以,但是擁有使用者的確切經緯度的屬性基數會太高。高基數屬性可能會導致指標儲存和匯出問題,應盡量避免。針對高基數資料使用 span 和記錄。

可以使用計量器建立幾種類型的儀器

  • 計數器:計數器是單調遞增的值。計數器只能是正數。它們可以用於始終遞增的值,例如處理的請求數。

  • UpDownCounter:UpDownCounter 是一個可以增加和減少的值。UpDownCounter 可以用於可以增加和減少的值,例如活動連線數或進行中的請求數。

  • 量表:量表是可以設定為任何值的值。它們用於不會隨著時間「累積」的值,而是在任何給定時間具有特定值的值,例如目前溫度。

  • 直方圖:直方圖是記錄為值分佈的值。直方圖可以用於不僅僅是單個數字,而是數字分佈的值,例如請求的回應時間(以毫秒為單位)。直方圖可以用於計算百分位數、平均值和其他統計資訊。它們具有預先定義的邊界,用於定義放置值的 bucket。預設情況下,邊界為 [0.0, 5.0, 10.0, 25.0, 50.0, 75.0, 100.0, 250.0, 500.0, 750.0, 1000.0, 2500.0, 5000.0, 7500.0, 10000.0]

還有幾種類型的可觀察儀器。這些儀器沒有同步記錄方法,而是傳回一個回呼,可以呼叫該回呼來記錄值。當 OpenTelemetry SDK 準備好記錄值時(例如在匯出之前),將會呼叫回呼。

const counter = meter.createObservableCounter("my_counter", {
  description: "A simple counter",
  unit: "1",
});
counter.addCallback((res) => {
  res.observe(1);
  // or
  res.observe(1, { color: "red" });
});

有三種類型的可觀察儀器

  • ObservableCounter:可觀察計數器是可以非同步觀察的計數器。它可以用於始終遞增的值,例如處理的請求數。
  • ObservableUpDownCounter:可觀察 UpDownCounter 是一個可以增加和減少的值,並且可以非同步觀察。UpDownCounter 可以用於可以增加和減少的值,例如活動連線數或進行中的請求數。
  • ObservableGauge:可觀察量表是可以設定為任何值的值,並且可以非同步觀察。它們用於不會隨著時間「累積」的值,而是在任何給定時間具有特定值的值,例如目前溫度。

OpenTelemetry JS API 文件中深入瞭解完整的指標 API。

上下文傳播 跳到標題

在 OpenTelemetry 中,上下文傳播是將某些上下文資訊(例如目前的 span)從應用程式的一部分傳遞到另一部分的過程,而無需將其顯式傳遞為每個函數的引數。

在 Deno 中,上下文傳播是使用 AsyncContext 的規則完成的,AsyncContext 是異步上下文傳播的 TC39 提案。AsyncContext API 尚未在 Deno 中向使用者公開,但它在內部用於跨異步邊界傳播活動 span 和其他上下文資訊。

AsyncContext 傳播如何運作的快速概述

  • 當新的異步任務開始時(例如 promise 或計時器),會儲存目前的上下文。
  • 然後,其他程式碼可以與異步任務同時執行,在不同的上下文中。
  • 當異步任務完成時,會還原儲存的上下文。

這表示異步上下文傳播基本上就像一個全域變數,其範圍限定為目前的異步任務,並自動複製到從目前任務啟動的任何新異步任務。

來自 npm:@opentelemetry/api@1context API 向使用者公開了此功能。它的運作方式如下

import { context } from "npm:@opentelemetry/api@1";

// Get the currently active context
const currentContext = context.active();

// You can add create a new context with a value added to it
const newContext = currentContext.setValue("id", 1);

// The current context is not changed by calling setValue
console.log(currentContext.getValue("id")); // undefined

// You can run a function inside a new context
context.with(newContext, () => {
  // Any code in this block will run with the new context
  console.log(context.active().getValue("id")); // 1

  // The context is also available in any functions called from this block
  function myFunction() {
    return context.active().getValue("id");
  }
  console.log(myFunction()); // 1

  // And it is also available in any asynchronous callbacks scheduled from here
  setTimeout(() => {
    console.log(context.active().getValue("id")); // 1
  }, 10);
});

// Outside, the context is still the same
console.log(context.active().getValue("id")); // undefined

上下文 API 也與 span 整合。例如,若要在特定 span 的上下文中執行函數,可以將 span 新增至上下文,然後可以在該上下文中執行函數

import { context, trace } from "npm:@opentelemetry/api@1";

const tracer = trace.getTracer("my-app", "1.0.0");

const span = tracer.startSpan("myFunction");
const contextWithSpan = trace.setSpan(context.active(), span);

context.with(contextWithSpan, () => {
  const activeSpan = trace.getActiveSpan();
  console.log(activeSpan === span); // true
});

// Don't forget to end the span!
span.end();

OpenTelemetry JS API 文件中深入瞭解完整的上下文 API。

設定 跳到標題

可以透過設定 OTEL_DENO=true 環境變數來啟用 OpenTelemetry 整合。

OTLP 匯出器的端點和協定可以使用 OTEL_EXPORTER_OTLP_ENDPOINTOTEL_EXPORTER_OTLP_PROTOCOL 環境變數進行設定。

如果端點需要驗證,可以使用 OTEL_EXPORTER_OTLP_HEADERS 環境變數設定標頭。

可以使用特定的環境變數為指標、追蹤和記錄單獨覆寫端點,例如

  • OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
  • OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
  • OTEL_EXPORTER_OTLP_LOGS_ENDPOINT

如需可用於設定 OTLP 匯出器的標頭的詳細資訊,請參閱 OpenTelemetry 網站

與遙測資料相關聯的資源可以使用 OTEL_SERVICE_NAMEOTEL_RESOURCE_ATTRIBUTES 環境變數進行設定。除了透過 OTEL_RESOURCE_ATTRIBUTES 環境變數設定的屬性外,還會自動設定以下屬性

  • service.name:如果未設定 OTEL_SERVICE_NAME,則值會設定為 <unknown_service>
  • process.runtime.namedeno
  • process.runtime.version:Deno 執行階段的版本。
  • telemetry.sdk.namedeno-opentelemetry
  • telemetry.sdk.languagedeno-rust
  • telemetry.sdk.version:Deno 執行階段的版本,加上 Deno 使用的 opentelemetry Rust crate 的版本,以 - 分隔。

指標收集頻率可以使用 OTEL_METRIC_EXPORT_INTERVAL 環境變數進行設定。預設值為 60000 毫秒(60 秒)。

Span 匯出器批次處理可以使用 OpenTelemetry 規格中描述的批次 span 處理器環境變數進行設定。

記錄匯出器批次處理可以使用 OpenTelemetry 規格中描述的批次記錄記錄處理器環境變數進行設定。

限制 跳到標題

雖然 Deno 的 OpenTelemetry 整合仍在開發中,但有一些限制需要注意

  • 追蹤始終被取樣(即 OTEL_TRACE_SAMPLER=parentbased_always_on)。
  • 追蹤不支援事件。
  • 追蹤僅支援沒有屬性的連結。
  • 不支援在 Deno.servefetch 中自動傳播追蹤上下文。
  • 不支援指標範例。
  • 不支援自訂記錄串流(例如 console.logconsole.error 以外的記錄)。
  • 唯一支援的匯出器是 OTLP - 不支援其他匯出器。
  • OTLP 僅支援 http/protobufhttp/json 協定。不支援其他協定,例如 grpc
  • 來自可觀察(異步)計量器的指標不會在程序結束/崩潰時收集,因此指標的最後一個值可能不會匯出。同步指標會在程序結束/崩潰時匯出。
  • OTEL_ATTRIBUTE_VALUE_LENGTH_LIMITOTEL_ATTRIBUTE_COUNT_LIMITOTEL_SPAN_EVENT_COUNT_LIMITOTEL_SPAN_LINK_COUNT_LIMITOTEL_EVENT_ATTRIBUTE_COUNT_LIMITOTEL_LINK_ATTRIBUTE_COUNT_LIMIT 環境變數中指定的限制不適用於追蹤 span。
  • OTEL_METRIC_EXPORT_TIMEOUT 環境變數不受尊重。
  • 根據 OpenTelemetry 語意慣例,未知的 HTTP 方法不會在 http.request.method span 屬性中正規化為 _OTHER
  • Deno.serve 的 HTTP 伺服器 span 沒有設定 OpenTelemetry 狀態,如果處理常式拋出錯誤(即呼叫 onError),span 將不會設定錯誤狀態,並且錯誤將不會透過事件附加到 span。
  • 沒有機制可以將 http.route 屬性新增至 fetch 的 HTTP 用戶端 span,或更新 span 名稱以包含路由。

您找到需要的資訊了嗎?

隱私權政策