本頁面內容
OpenTelemetry
Deno 的 OpenTelemetry 整合仍在開發中,可能會有所變動。若要使用它,您必須將 --unstable-otel
旗標傳遞給 Deno。
Deno 內建支援 OpenTelemetry。
OpenTelemetry 是一系列 API、SDK 和工具的集合。使用它可以檢測、產生、收集和匯出遙測資料(指標、記錄和追蹤),以協助您分析軟體的效能和行為。
此整合讓您可以使用 OpenTelemetry 可觀測性工具(如記錄、指標和追蹤)來監控您的 Deno 應用程式。
Deno 提供以下功能
- 使用 OpenTelemetry 協定將收集到的指標、追蹤和記錄匯出到伺服器。
- Deno 執行階段的自動檢測,包含 OpenTelemetry 指標、追蹤和記錄。
- 收集使用者定義的指標、追蹤和記錄,使用
npm:@opentelemetry/api
套件建立。
快速開始 跳到標題
若要啟用 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
和密碼 admin
在 https://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 的協定(例如http
或https
)。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.serve
或 Deno.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.1
或2
)。server.address
:伺服器正在監聽的位址。server.port
:伺服器正在監聽的埠。http.response.status_code
:回應的狀態碼(如果請求已處理且沒有發生嚴重錯誤)。error.type
:發生的錯誤類型(如果請求處理遇到錯誤)。
http.server.active_requests
跳到標題
在任何給定時間,由 Deno.serve
或 Deno.serveHttp
處理的活動請求數量的量表。這是已收到但尚未回應的請求數量(回應標頭尚未傳送)。此指標會記錄以下屬性
http.request.method
:請求的 HTTP 方法。url.scheme
:請求 URL 的協定。server.address
:伺服器正在監聽的位址。server.port
:伺服器正在監聽的埠。
http.server.request.body.size
跳到標題
使用 Deno.serve
或 Deno.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.1
或2
)。server.address
:伺服器正在監聽的位址。server.port
:伺服器正在監聽的埠。http.response.status_code
:回應的狀態碼(如果請求已處理且沒有發生嚴重錯誤)。error.type
:發生的錯誤類型(如果請求處理遇到錯誤)。
http.server.response.body.size
跳到標題
使用 Deno.serve
或 Deno.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.1
或2
)。server.address
:伺服器正在監聽的位址。server.port
:伺服器正在監聽的埠。http.response.status_code
:回應的狀態碼(如果請求已處理且沒有發生嚴重錯誤)。error.type
:發生的錯誤類型(如果請求處理遇到錯誤)。
記錄 跳到標題
以下記錄會自動收集和匯出
- 使用
console.*
方法(例如console.log
和console.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.recordException
和 span.setStatus
也應在 catch
區塊中呼叫,以記錄發生的任何錯誤。
在回呼函數內部,建立的 span 是「活動 span」。您可以使用 trace.getActiveSpan()
取得活動 span。「活動 span」將用作在回呼函數(或從回呼函數呼叫的任何函數)內部建立的任何 span(手動或由執行階段自動建立)的父 span。深入瞭解上下文傳播。
startActiveSpan
方法會傳回回呼函數的傳回值。
Span 可以在其生命週期內新增屬性。屬性是鍵值對,表示關於 span 的結構化中繼資料。可以使用 span 物件上的 setAttribute
和 setAttributes
方法新增屬性。
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.startActiveSpan
和 tracer.startSpan
都可以採用可選的選項包,其中包含以下任何屬性
kind
:span 的種類。可以是SpanKind.CLIENT
、SpanKind.SERVER
、SpanKind.PRODUCER
、SpanKind.CONSUMER
或SpanKind.INTERNAL
。預設為SpanKind.INTERNAL
。startTime
代表 span 開始時間的Date
物件,或代表自 Unix epoch 以來經過的毫秒數的數字。如果未提供,將使用目前時間。attributes
:包含要新增至 span 的屬性的物件。links
:要新增至 span 的連結陣列。root
:一個布林值,指示 span 是否應為根 span。如果為true
,則 span 將沒有父 span(即使存在活動 span)。
在選項包之後,tracer.startActiveSpan
和 tracer.startSpan
也可以採用來自 上下文傳播 API 的 context
物件。
在 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@1
的 context
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_ENDPOINT
和 OTEL_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_NAME
和 OTEL_RESOURCE_ATTRIBUTES
環境變數進行設定。除了透過 OTEL_RESOURCE_ATTRIBUTES
環境變數設定的屬性外,還會自動設定以下屬性
service.name
:如果未設定OTEL_SERVICE_NAME
,則值會設定為<unknown_service>
。process.runtime.name
:deno
process.runtime.version
:Deno 執行階段的版本。telemetry.sdk.name
:deno-opentelemetry
telemetry.sdk.language
:deno-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.serve
和fetch
中自動傳播追蹤上下文。 - 不支援指標範例。
- 不支援自訂記錄串流(例如
console.log
和console.error
以外的記錄)。 - 唯一支援的匯出器是 OTLP - 不支援其他匯出器。
- OTLP 僅支援
http/protobuf
和http/json
協定。不支援其他協定,例如grpc
。 - 來自可觀察(異步)計量器的指標不會在程序結束/崩潰時收集,因此指標的最後一個值可能不會匯出。同步指標會在程序結束/崩潰時匯出。
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT
、OTEL_ATTRIBUTE_COUNT_LIMIT
、OTEL_SPAN_EVENT_COUNT_LIMIT
、OTEL_SPAN_LINK_COUNT_LIMIT
、OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT
和OTEL_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 名稱以包含路由。