This article was translated from Japanese by AI and may contain inaccuracies. For the most accurate content, please refer to the original Japanese version.
ブラキオサウルスのイラスト

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 リクエストのトレースが自動的に計装されます。

server.ts
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_DENOtrue に設定した上で --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:リクエストされた URL
  • url.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 属性をスパンに追加するようにしましょう。この場合ルート名を含めるようにスパン名も更新する必要があります。

Tip

メトリックやトレースに付与する属性の名前は多くの場合 Semantic Conventions という規約に定められています。カスタム属性を追加する際にはこの規約に対応する属性がないか確認するようにしましょう。

server.ts
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_DENOtrue に設定して --unstable-otel フラグを付けて Deno を実行する

参考

Comprehension check

Answer the following questions to deepen your understanding of the article.

OpenTelemetry の自動計装を有効にするにはどのような環境変数を設定する必要があるか?

  • OTEL_AUTO_INSTRUMENT=true

    Try again

  • OTEL_DENO=true

    Correct!

  • OTEL_DENO_AUTO_INSTRUMENT=true

    Try again

  • OTEL_ENABLE=true

    Try again