Tracing
Trace requests across services with distributed tracing. Send spans using OpenTelemetry or simple JSON. Auto-discover services, visualize dependency maps, and correlate traces with logs and metrics.
A user clicks a button. Your API receives the request, queries a database, calls a cache, fans out to two downstream services, and returns a response. Distributed tracing captures that full journey as a tree of spans so you can see exactly where time is spent and where failures originate.
JuhJuh collects traces from your services, auto-discovers the services involved, maps their dependencies, and links traces to logs and metrics for cross-signal investigation.
Core concepts¶
Traces¶
A trace represents a single end-to-end operation, like an HTTP request or a background job execution. Every trace has a unique trace_id that ties all its parts together across services.
Spans¶
A span is one unit of work within a trace. A trace is a tree of spans. The root span is the entry point (your API receiving a request), and child spans represent downstream operations (database queries, cache lookups, HTTP calls to other services).
Each span records:
- Operation name: what happened (
GET /api/users,SELECT users,redis.get) - Service name: which service performed the work
- Duration: how long it took
- Status: whether it succeeded or failed
- Attributes: additional context (HTTP method, URL, status code, database statement)
Service map¶
JuhJuh automatically builds a dependency map from your trace data. When service A calls service B, and that relationship appears in span parent-child links, JuhJuh records the dependency and displays it as a directed graph in the service map view.
graph LR
GW[API Gateway] --> US[User Service]
GW --> PS[Payment Service]
US --> DB[(Database)]
US --> Cache[(Cache)]
PS --> DB
PS --> Ext[External Payment API]Sending traces¶
Send traces to either endpoint:
POST /api/ingest/traces/
POST /api/ingest/v1/traces/
Authorization: Bearer jjk_YOUR_API_KEY
Content-Type: application/json
Both endpoints accept the same payloads. The /v1/traces/ path follows the OTLP standard convention.
The endpoint auto-detects the format. If the top-level key is resourceSpans, it parses OTLP. If the key is spans, it parses simple JSON.
Simple JSON format¶
{
"spans": [
{
"trace_id": "abc123def456789",
"span_id": "span_001",
"parent_span_id": "",
"operation_name": "GET /api/users",
"service_name": "api-gateway",
"span_kind": "server",
"status_code": "ok",
"duration_ms": 142,
"started_at": "2026-04-09T14:30:00Z",
"http_method": "GET",
"http_url": "/api/users",
"http_status_code": 200,
"attributes": {
"user.id": "usr_12345"
},
"events": []
},
{
"trace_id": "abc123def456789",
"span_id": "span_002",
"parent_span_id": "span_001",
"operation_name": "SELECT users",
"service_name": "user-service",
"span_kind": "client",
"status_code": "ok",
"duration_ms": 23,
"started_at": "2026-04-09T14:30:00.050Z",
"attributes": {
"db.system": "postgresql",
"db.statement": "SELECT * FROM users WHERE id = $1"
}
}
]
}
| Field | Required | Default | Description |
|---|---|---|---|
trace_id |
Yes | Unique identifier linking all spans in one trace | |
span_id |
Yes | Unique identifier for this span | |
parent_span_id |
No | "" |
The span_id of the parent span. Empty for root spans |
operation_name |
No | What this span represents: GET /api/users, SELECT users, redis.get |
|
service_name |
No | unknown |
The service that produced this span |
span_kind |
No | internal |
One of: internal, server, client, producer, consumer |
status_code |
No | unset |
One of: unset, ok, error |
duration_ms |
No | Duration in milliseconds | |
started_at |
No | Server time | ISO 8601 timestamp when the span started |
http_method |
No | HTTP method if this span represents an HTTP operation | |
http_url |
No | URL or path of the HTTP request | |
http_status_code |
No | HTTP response status code | |
attributes |
No | {} |
Additional key-value context |
events |
No | [] |
List of timestamped events within the span |
OTLP format¶
OpenTelemetry SDKs send traces in OTLP format automatically:
{
"resourceSpans": [
{
"resource": {
"attributes": [
{ "key": "service.name", "value": { "stringValue": "api-gateway" } }
]
},
"scopeSpans": [
{
"spans": [
{
"traceId": "abc123def456789",
"spanId": "span_001",
"parentSpanId": "",
"name": "GET /api/users",
"kind": 2,
"startTimeUnixNano": "1712672400000000000",
"endTimeUnixNano": "1712672400142000000",
"status": { "code": 1 },
"attributes": [
{ "key": "http.method", "value": { "stringValue": "GET" } },
{ "key": "http.url", "value": { "stringValue": "/api/users" } },
{ "key": "http.status_code", "value": { "intValue": "200" } }
]
}
]
}
]
}
]
}
JuhJuh extracts standard HTTP and database attributes automatically:
| OTLP attribute | Extracted as |
|---|---|
http.method or http.request.method |
HTTP method |
http.url or url.full |
HTTP URL |
http.status_code or http.response.status_code |
HTTP status code |
db.system |
Database system (postgresql, mysql, redis) |
db.statement |
Database query text |
Span kinds¶
Span kind categorizes what role the span plays in the trace:
| Kind | Numeric | When to use |
|---|---|---|
internal |
0, 1 | Default. Internal operations within a service |
server |
2 | Your service receiving an inbound request (HTTP handler, RPC server) |
client |
3 | Your service making an outbound call (HTTP client, database query, external API) |
producer |
4 | Your service publishing a message to a queue or topic |
consumer |
5 | Your service consuming a message from a queue or topic |
Server and client spans are the most common. They form the cross-service links that build your service dependency map.
Service auto-discovery¶
JuhJuh automatically registers every unique service_name it encounters in your trace data. For each discovered service, JuhJuh tracks:
| Metric | Description |
|---|---|
| Span count (24h) | Total spans produced by this service in the last 24 hours |
| Error rate (24h) | Percentage of spans with status_code: error |
| Average latency | Mean span duration across all spans from this service |
| P99 latency | 99th percentile span duration |
| Last seen | Timestamp of the most recent span from this service |
No manual registration needed. Send traces and your services appear in the service list.
Service dependency map¶
When a span from service A is the parent of a span from service B, JuhJuh records that dependency. Over time, this builds a complete map of how your services communicate.
The dependency map shows:
- Directed edges between services (who calls whom)
- Request volume on each edge
- Error rates on each connection
- Latency percentiles per dependency
Use the map to identify:
- Single points of failure: services that many others depend on
- Error propagation: a failing downstream service causing errors upstream
- Latency bottlenecks: a slow dependency dragging down overall response times
Viewing traces¶
Trace list¶
The trace list shows recent traces with filtering options:
| Filter | What it does |
|---|---|
| Service | Show traces that include spans from a specific service |
| Operation | Filter by operation name |
| Status | Show only traces with errors |
| Duration | Filter by minimum or maximum duration |
| Time range | Narrow to a specific time window |
Trace detail¶
Click a trace to see the full span tree. The waterfall view shows:
- Each span as a horizontal bar, positioned by start time and sized by duration
- Parent-child relationships as nesting
- Service name, operation, and duration for each span
- Status indicators (green for ok, red for error)
- Click any span to see its attributes, events, and linked logs
Trace-to-log correlation¶
If your logs include a trace_id attribute (see Logging), the trace detail view shows all matching log entries alongside the span tree. This gives you both the timing view (trace) and the narrative view (logs) for the same request.
Batching¶
Send multiple spans in a single request:
{
"spans": [
{ "trace_id": "abc123", "span_id": "s1", "operation_name": "GET /", "service_name": "web" },
{ "trace_id": "abc123", "span_id": "s2", "parent_span_id": "s1", "operation_name": "SELECT", "service_name": "db" },
{ "trace_id": "def456", "span_id": "s3", "operation_name": "POST /pay", "service_name": "payments" }
]
}
Spans from different traces can be mixed in the same request. JuhJuh groups them by trace_id during processing.
| Batch size | Behavior |
|---|---|
| 1 to 50 spans | Processed synchronously, returns 200 |
| 51+ spans | Accepted with 202, processed asynchronously |
Frontend-to-backend tracing¶
Real User Monitoring connects browser-side errors to server-side traces using W3C Trace Context headers. When the RUM SDK detects a same-origin request, it injects a traceparent header. If your backend propagates that trace ID, JuhJuh links the frontend session to the backend trace.
Start from a user's session, find a slow page load, and follow it through to the exact database query that caused the delay.
Data retention¶
Traces and spans are retained for 14 days. After the retention period, trace data is permanently deleted in daily cleanup cycles.
Example: manual span creation in Python¶
Create spans manually without auto-instrumentation:
from opentelemetry import trace
tracer = trace.get_tracer("my-service")
def handle_request(request):
with tracer.start_as_current_span("handle_request", kind=trace.SpanKind.SERVER) as span:
span.set_attribute("http.method", request.method)
span.set_attribute("http.url", request.path)
with tracer.start_as_current_span("query_database", kind=trace.SpanKind.CLIENT) as db_span:
db_span.set_attribute("db.system", "postgresql")
db_span.set_attribute("db.statement", "SELECT * FROM users")
result = database.query("SELECT * FROM users")
span.set_attribute("http.status_code", 200)
return result
The OpenTelemetry SDK handles trace ID generation, parent-child linking, and export to JuhJuh automatically. See Instrumentation for full SDK setup.
Related¶
- Observability for an overview of all observability signals
- Instrumentation for SDK setup and connecting your application
- Logging for trace-to-log correlation with trace IDs
- Metrics for metric-to-trace exemplar linking
- Alerts for notifications on error rate spikes in traces
- Real User Monitoring for frontend-to-backend trace correlation