Trying the OpenTelemetry integration implemented in Deno 2.2

| 8 min read
Author: masahiro-kondo masahiro-kondoの画像
Information

To reach a broader audience, this article has been translated from Japanese.
You can find the original version here.

Introduction

#

It has been almost two months since the release of Deno 2.2, when the integration of OpenTelemetry was announced as one of its highlights.

Deno 2.2: OpenTelemetry, Lint Plugins, node:sqlite

Collecting telemetry data—such as metrics, traces, and logs—is indispensable for achieving application observability. With telemetry data exchange being standardized by the OpenTelemetry specification, support in various languages and runtimes has been advancing, and Deno has also incorporated it as a runtime feature.

The documentation for Deno’s OpenTelemetry can be found here.

Information

There are several detailed articles on metrics collection and distributed tracing with OpenTelemetry on this website, so please refer to them.




Launching the OTLP Endpoint / Grafana

#

When testing locally, as mentioned in the official blog, you can use Grafana Labs’ docker-otel-lgtm to launch the OpenTelemetry (hereafter Otel) Collector endpoint and Grafana, making them observable on the dashboard.

The data collected by the Otel Collector is accumulated in the Prometheus Metric, Tempo Trace, and Loki Logs databases, making it possible to monitor via the Grafana dashboard.

flowchart LR
  OC[Otel Collector]
  PM[Prometheus Metric<br>Database]
  TT[Tempo<br>Trace Database]
  LL[Loki<br>Logs Database]
  G[Grafana]
  OC --> PM
  OC --> TT
  OC --> LL
  G -.-> PM
  G -.-> TT
  G -.-> LL

It can be easily launched with docker run.

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

The following output shows that the gRPC and HTTP endpoints, as well as Grafana, have been launched.

The OpenTelemetry collector and the Grafana LGTM stack are up and running. (created /tmp/ready)
Open ports:
 - 4317: OpenTelemetry GRPC endpoint
 - 4318: OpenTelemetry HTTP endpoint
 - 3000: Grafana. User: admin, password: admin

Testing Traces with Multiple Service Integration: Service Implementation

#

As shown below, we'll test tracing in a setup where a REST API service (A) calls another REST API service (B).

flowchart LR
  client --> SA[Service A<br>port:3001] --> SB[Service B<br>port:9000]

Service A code. It starts on port 3001 and returns the result of calling Service B to the client.

service_a.ts
Deno.serve({ port: 3001 }, async (req) => {
  console.log("Service A Received request for", req.url);
  const res = await fetch("http://localhost:9000");
  const text = await res.text();
  console.log("Service A Received response from Service B", text);
  return new Response(`Service A got: ${text}`);
});

Service B code. It starts on port 9000 and returns a simple message to the client (in this case, Service A).

service_b.ts
Deno.serve({ port: 9000 }, (req) => {
  console.log("Service B Received request for", req.url);
  return new Response("Hello world");
});

Starting Service A. By setting the environment variable OTEL_DENO to true, Otel is enabled. Assigning a service name using OTEL_SERVICE_NAME makes the trace results easier to view. Here, we set the service name as svc-a.

OTEL_DENO=true OTEL_SERVICE_NAME=svc-a deno run --unstable-otel --allow-net service_a.ts
Information

Deno's OpenTelemetry integration API is still unstable and may change in the future, so it is necessary to specify the --unstable-otel flag.

Similarly, start Service B, setting the service name as svc-b.

OTEL_DENO=true OTEL_SERVICE_NAME=svc-b deno run --unstable-otel --allow-net service_b.ts

Testing Traces with Multiple Service Integration: Visualizing in Grafana

#

Now that the services have been connected, let's invoke Service A and observe the tracing in Grafana.

Accessing localhost:3000 displays the Grafana interface.

Grafana Home

Select Explore from the menu. Since the default data source is Prometheus, choose Tempo.

Explore

The Tempo query panel is displayed.

Tempo Query panel

Invoke Service A using curl.

curl - http://localhost:3001

Clicking the refresh button displays the Trace ID, timestamp, service name, HTTP method, and response time of the call in the Tempo query panel.

TraceQL

It appears that the Trace ID is assigned by Deno's Otel Exporter. Clicking this Trace ID shows detailed trace information, displaying in the order of svc-a → svc-b.

Trace info

Expanding the Node graph makes the call relationships visible.

Node graph

The Service Graph in the query panel also visualizes the relationships between services.

Service graph

It appears that the Trace ID is embedded in the request headers. Code to display the headers has been added in Service B.

service_b.ts
Deno.serve({ port: 9000}, (req) => {
  // Display headers
  for (const [key, value] of req.headers.entries()) {
    console.log(key, value);
  }
  console.log("Service B Received request for", req.url);
  return new Response("Hello world");
});

The Trace ID is set under the key traceparent.

accept */*
accept-encoding gzip,br
accept-language *
host localhost:9000
traceparent 00-34ea2fccfa0ce81bc8855043682b2810-895a8a6b4e02df52-01
user-agent Deno/2.2.8

It seems that simply enabling Otel, without any modifications to the application code, is sufficient to enable distributed tracing.

Displaying Metrics

#

Metrics can be viewed from the Metrics dashboard, with Prometheus as the data source.

Metrics

When filtered by the otel_ prefix, it appears to reflect the status of resources used by Otel.

Metrics otelcol

When filtering by the v8js_ prefix, the metrics of the V8 JavaScript engine powering the Deno runtime are displayed.

Metrics v8js

Additional Otel Features

Additional Otel-related features have been continuously added since version 2.2.0.

In v2.2.4, support for Context propagation was added.

In v2.2.5, support for Span Events was added.

In v2.2.7, support for V8 JS Engine Runtime metrics was added. This appears to enable the collection of metrics with the v8js_ prefix in Grafana.

The specification for V8 JS engine runtime metrics can be found below.

Viewing Logs

#

Logs can be viewed from the Logs dashboard. Logs for each service are displayed in chronological order.

Logs

Conclusion

#

That concludes our brief exploration of the OpenTelemetry integration implemented in Deno 2.2 using simple code. Even in Kubernetes environments, it is convenient as there is no need to launch sidecar containers—just specify the environment variables. This will likely enable distributed tracing in environments with a mix of languages and runtimes beyond Deno.

Although the focus ended up being mostly an overview of the Grafana interface, there appears to be demand for creating dashboards that leverage alerts and queries for practical operations.

2025.04.10 Update

An official Otel blog post has also been released.

Zero-config Debugging with Deno and OpenTelemetry

豆蔵では共に高め合う仲間を募集しています!

recruit

具体的な採用情報はこちらからご覧いただけます。