
Deno v2.2 で追加されたビルドイン OpenTelemetry サポートを試してみる
Deno v2.2 でビルドインの OpenTelemetry サポートが追加されました。アプリケーションのコードに変更を加えることなく、Deno のビルトイン API から自動的にテレメトリーデータを計装できるようになります。
Deno v2.2 でビルドインの OpenTelemetry サポートが追加されました。OpenTelemetry は分散トレーシングのためのオープンソースの規格です。OpenTelemetry の規格に従うことで、トレース・メトリクス・ログなどのテレメトリーデータをベンダーやツールにとらわれずに収集・エクスポートできるようになります。
一般的に OpenTelemetry を使用してテレメトリーデータを計装するにはプログラミング言語ごとに用意されている SDK を使用します。Deno のビルドイン OpenTelemetry サポートは、追加の SDK のインストールや設定を行わずに Deno アプリケーションから OpenTelemetry を利用できるようにするものです。console.log
, Deno.serve
, fetch
などの Deno のビルトイン API から自動的にテレメトリーデータを計装します。
テレメトリーデータを計装する
OpenTelemetry のテレメトリーデータを計装するにあたってソースコードに変更を加える必要はありません。多くのテレメトリーデータは Deno によって自動で計装されます。独自のメトリックやトレースを計装したい場合には npm:@opentelemetry/api
パッケージを使用することもできます。
以下のコードは Deno で HTTP サーバーを立ち上げ、リクエストを受け取るサンプルコードです。このコードを実行すると、HTTP リクエストのトレースが自動的に計装されます。
Deno.serve(async (req) => {
console.log("Incoming request", req.url);
const res = await fetch("https://jsonplaceholder.typicode.com/todos");
const todos = await res.json();
if (!res.ok) {
console.error("Failed to fetch todos:", res.statusText);
return new Response("Error fetching todos", { status: 500 });
}
console.log("fetch todos", todos);
return new Response(JSON.stringify(todos), {
headers: { "content-type": "application/json" },
});
});
自動計装されたテレメトリーデータはデフォルトで http/protobuf
プロトコルを使用して localhost:4318
にエクスポートされます。Deno アプリケーションからエクスポートされたテレメトリーデータは OpenTelemetry Collector で受け取り、データを加工してから外部の監視バックエンドサービスに送信するのが一般的です。
ここでは Docker で local LGTM スタック(Prometheus, Grafana, Loki, Tempo)を使用して OpenTelemetry Collector と監視バックエンドサービスを立ち上げます。
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
OpenTelemetry の自動計装を有効にして Deno アプリケーションを実行するには、環境変数 OTEL_DENO
を true
に設定した上で
--unstable-otel
フラグを付けて Deno を実行します。
OTEL_DENO=true deno run --unstable-otel --allow-net server.ts
curl
コマンドを使用して HTTP リクエストを何度か送信してみましょう。
curl http://localhost:8000
http://localhost:3000 にアクセスして Grafana にログインして Explore 画面を開きます。以下のクエリを入力して Tempo で受け取ったトレースを表示します。Deno.serve()
を起点としたトレースが表示されるはずです。
一番長い親スパンがリクエスト全体が処理される時間であり、その下にある子スパンは fetch()
で外部にリクエストを送信する時間を示しています。
また、console.log()
で出力したログも Loki に送信され、Grafana でログを確認することができます。
メトリックやトレースをカスタマイズする
自動計装されたテレメトリーデータのみでは不十分な場合があります。例えば Deno.serve()
で計装されるトレースには以下の属性が含まれます。
http.request.method
:HTTP リクエストのメソッドurl.full
:リクエストされた URLurl.scheme
:リクエストされた URL のスキームurl.path
:リクエストされた URL のパスurl.query
:リクエストされた URL のクエリパラメータhttp.status_code
:HTTP レスポンスのステータスコード
上記の属性に加えて HTTP リクエストのトレースを計装する際には http.route
属性が含まれているべきです。この属性は users/:id
のようなサーバーフレームワークで使われているテンプレート書式を表します。この属性は正規化された状態でエンドポイントのパフォーマンスを分析するのに役立ちます。
しかし、Deno の自動計装ではこの属性は含まれません。deno.serve()
メソッドはルーティング機能を提供していないためです。このような場合には npm:@opentelemetry/api
パッケージを使用してスパンに新しい属性を追加します。以下のコマンドでパッケージを追加します。
deno add npm:@opentelemetry/api
ルーティングのロジックを追加して http.route
属性をスパンに追加するようにしましょう。この場合ルート名を含めるようにスパン名も更新する必要があります。
メトリックやトレースに付与する属性の名前は多くの場合 Semantic Conventions という規約に定められています。カスタム属性を追加する際にはこの規約に対応する属性がないか確認するようにしましょう。
import { trace } from "npm:@opentelemetry/api@1";
const TODO_LIST_ROUTE = new URLPattern({ pathname: "/" });
const TODO_ROUTE = new URLPattern({ pathname: "/todos/:id" });
Deno.serve(async (req) => {
// 現在のスパンを取得
const span = trace.getActiveSpan();
if (TODO_LIST_ROUTE.test(req.url)) {
// スパン名が {method} {target} になるように更新
// これは OpenTelemetry の Semantic Conventions に従った命名である
// https://opentelemetry.io/docs/specs/semconv/http/http-spans/#name
span?.updateName(`${req.method} /`);
span?.setAttribute("http.route", "GET /todos");
// TODO リストを取得する処理
} else if (TODO_ROUTE.test(req.url)) {
span?.updateName(`GET /todos/:id`);
span?.setAttribute("http.route", `GET /todos/:id`);
// id に応じて TODO を取得する処理
} else {
return new Response("Not Found", { status: 404 });
}
});
まとめ
- Deno v2.2 でビルドインの OpenTelemetry サポートが追加された
Deno.serve()
,fetch()
,console.*()
などのビルトイン API から自動的にテレメトリーデータを計装できる- デフォルトで
http/protobuf
プロトコルを使用してlocalhost:4318
にエクスポートされるので、OpenTelemetry Collector で受け取り、データを加工してから外部の監視バックエンドサービスに送信するのが一般的 - OpenTelemetry の自動計装を有効にするには、環境変数
OTEL_DENO
をtrue
に設定して--unstable-otel
フラグを付けて Deno を実行する