Skip to content

Observability

Thunder has built-in OpenTelemetry (OTEL) support for exporting traces, metrics, and logs. The repository includes a Docker Compose stack with an OTEL Collector, Tempo (traces), Loki (logs), Prometheus (metrics), VictoriaMetrics, and Grafana for visualization.

Thunder Runtime
|
+-- OTLP HTTP (traces, metrics, logs)
|
v
OTEL Collector (port 4318)
|
+-- Traces --> Tempo
+-- Metrics --> Prometheus / VictoriaMetrics
+-- Logs --> Loki
|
v
Grafana (port 3000) -- unified visualization

The full stack is defined in observability/docker-compose.yml. Start it from the repository root:

Terminal window
docker compose -f observability/docker-compose.yml up -d

Verify all services are running:

Terminal window
docker compose -f observability/docker-compose.yml ps
ServiceImagePortPurpose
OTEL Collectorotel/opentelemetry-collector-contrib:0.100.04317 (gRPC), 4318 (HTTP)Receives and routes telemetry
Tempografana/tempo:2.5.03200Distributed tracing backend
Lokigrafana/loki:3.0.03100Log aggregation
Prometheusprom/prometheus:v2.52.09090Metrics storage
VictoriaMetricsvictoriametrics/victoria-metrics:v1.102.18428Alternative metrics storage
vmagentvictoriametrics/vmagent:v1.102.18429Metrics scraping agent
Grafanagrafana/grafana:11.0.03000Dashboards and exploration

Run the runtime with all telemetry signals enabled:

Terminal window
thunder \
--otel-enabled \
--otel-endpoint http://127.0.0.1:4318 \
--otel-service-name thunder-local \
--otel-enable-traces true \
--otel-enable-metrics true \
--otel-enable-logs true \
--otel-export-isolate-logs true \
start --print-isolate-logs false
FlagDefaultDescription
--otel-enabledfalseEnable OTEL export
--otel-endpoint <URL>http://127.0.0.1:4318OTLP collector base URL
--otel-service-name <NAME>thunderOTEL resource service.name
--otel-enable-tracestrueExport trace spans
--otel-enable-metricstrueExport metrics
--otel-enable-logstrueExport logs
--otel-export-isolate-logstrueExport isolate console.* logs to OTEL
--otel-export-interval-ms5000Batch export interval
--otel-export-timeout-ms10000Export timeout

All flags have corresponding environment variables prefixed with EDGE_RUNTIME_. For example, --otel-enabled maps to EDGE_RUNTIME_OTEL_ENABLED.

Thunder captures console.log(), console.warn(), console.error(), and other console.* calls from function isolates.

There are two destinations for these logs:

  1. stdout — when --print-isolate-logs true (default). Logs appear in the runtime process output.
  2. OTEL logs pipeline — when --otel-export-isolate-logs true and --print-isolate-logs false.

To route isolate logs to the OTEL pipeline, you must disable stdout printing:

Terminal window
thunder --otel-enabled start --print-isolate-logs false

If --print-isolate-logs true is set, logs go to stdout and the OTEL collector receives no isolate log events.

Open Grafana at http://localhost:3000 and log in:

  • Username: admin
  • Password: admin

The stack provisions these datasources automatically:

  • VictoriaMetrics (default)
  • Prometheus
  • Loki
  • Tempo

A dashboard called “Edge Runtime - Observability Overview” is provisioned automatically and provides a high-level view of runtime behavior.

Traces: Navigate to Explore, select the Tempo datasource, and search by service name thunder-local. Each request generates a trace with spans for HTTP routing, function dispatch, and isolate execution.

Logs: Select the Loki datasource and query:

{service_name="thunder-local"}

For isolate-specific logs:

{log_source="isolate"}

Metrics: Select the Prometheus or VictoriaMetrics datasource. Example queries:

edge_runtime_isolate_logs_exported_total

Thunder exposes a metrics endpoint on the admin listener, independent of the OTEL pipeline:

Terminal window
curl http://localhost:9000/_internal/metrics

This returns runtime and per-function metrics in JSON format. Use ?fresh=1 to force recomputation when validating recent load tests:

Terminal window
curl http://localhost:9000/_internal/metrics?fresh=1

The same data is available at the alias /metrics on the admin listener.

After starting the stack and the runtime, deploy a function and send requests:

Terminal window
# Bundle and deploy
thunder bundle --entrypoint ./examples/hello/hello.ts --output ./hello.eszip
curl -X POST http://localhost:9000/_internal/functions \
-H "x-function-name: hello" \
--data-binary @./hello.eszip
# Generate traffic
for i in $(seq 1 50); do
curl -s http://localhost:8080/hello > /dev/null
done

Then check Grafana for traces, logs, and metrics.

You can also query backends directly to verify data flow:

EndpointPurpose
http://localhost:8888/metricsOTEL Collector internal metrics
http://localhost:9464/metricsOTEL Collector Prometheus export
http://localhost:3200/readyTempo readiness
http://localhost:3100/readyLoki readiness
http://localhost:9090Prometheus UI
http://localhost:8428/vmuiVictoriaMetrics UI
http://localhost:8429/targetsvmagent scrape targets

Stop all services:

Terminal window
docker compose -f observability/docker-compose.yml down

Stop and remove all data volumes:

Terminal window
docker compose -f observability/docker-compose.yml down -v
  • Ensure the runtime started with start --print-isolate-logs false.
  • Ensure --otel-export-isolate-logs true is set.
  • Check collector logs: docker logs edge-otel-collector.
  • Confirm --otel-enabled and --otel-enable-traces true are set.
  • Verify the collector is receiving data: check docker logs edge-otel-collector.
  • Confirm the OTEL endpoint is reachable: curl http://127.0.0.1:4318/v1/traces.
  • Check targets at http://localhost:9090/targets.
  • Ensure the collector is exposing metrics on port 9464.
  • Check vmagent targets at http://localhost:8429/targets.
  • Query VictoriaMetrics directly at http://localhost:8428/vmui for otelcol_receiver_accepted_spans.