Distributed tracing is active across the full stack: Bun monolith → Go backend → PostgreSQL. Every request creates a single trace in Jaeger or Tempo spanning all three layers.
1. What is Distributed Tracing?
When a user makes a request, it touches multiple components: HTTP handlers, business logic, database queries, and potentially multiple services. Distributed Tracing tracks the entire journey as a single timeline. Without tracing, diagnosing “Why is this API slow?” requires manually correlating logs. With tracing, you get a visual breakdown of exactly where time was spent.2. Architecture
3. E2E Trace Example
A request to list org permissions produces a single trace:team_memberships query is immediately visible in Jaeger without any manual instrumentation.
4. Feature Flag & Runtime Toggle
Tracing has two control layers:AuthMiddleware and only registered when TELEMETRY_ENABLED=true at startup.
Admin Endpoint
5. Sampling Strategy
ParentBased ensures DB spans (created by otelgorm) inherit the HTTP span’s sampling decision. No per-query configuration needed.
6. Go Backend
Key files
Dependencies
Environment variables
7. Bun Backend
Key files
Dependencies
traceparent propagation
Due to Bun’s runtime differences with Node.js, we avoidNodeSDK and Node-specific instrumentations like UndiciInstrumentation. Instead, HTTPClient manually injects the W3C traceparent context into outgoing request headers using propagation.inject(). This ensures traces continue unbroken into the Go backend.
Environment variables
8. Local Development
The infrastructure services (MongoDB, Redis, NATS, and Telemetry) have been consolidated into the rootinfra/ directory.
Tracing Backends
You can choose your preferred tracing backend viaTELEMETRY_BACKEND in your .env.
Option A: Jaeger (Default)
- Exposes OTLP HTTP on
:4318 - Start it:
docker compose -f infra/docker-compose.yml up -d jaeger - UI:
http://localhost:16686
- The
otel-collectorreceives traces on:4319and forwards them to Tempo. - Tempo stores the traces, and Grafana visualizes them.
- Start them:
docker compose -f infra/docker-compose.yml up -d otel-collector tempo grafana - UI:
http://localhost:3001(Navigate to Explore → Data source: Tempo)
Verify tracing
- Start your preferred backend (see above)
- Start Go/Bun backends with
TELEMETRY_ENABLED=true - Make a request:
curl http://localhost:8080/api/organizations - Open your backend’s UI and search for traces from
go-coreorbun-monolith.
Disable tracing at runtime
9. Core Concepts
Span
A single unit of work:span_id, parent_span_id, name, start/end timestamps, attributes, status. Spans form a tree matching the call hierarchy.
Context Propagation
The W3Ctraceparent header carries the trace_id and span_id across service boundaries:
What to look for in Jaeger or Tempo
10. Production Considerations
- Sampling: Default is 100% locally. Set
TELEMETRY_COLLECTOR_URLto your collector and reduce rate viaPOST /admin/telemetry {"rate": 0.1}or restart with a lower default. - TLS: The OTLP exporter uses
WithInsecure()for local dev. In production, configure TLS via the collector. - Span attributes: Avoid logging PII (emails, tokens) as span attributes.
- Performance: Typical overhead is 1–3% CPU with batching. Setting
TELEMETRY_ENABLED=falsegives zero overhead.