Author integration tests that query Jaeger for cross-service trace verification - Jaeger all-in-one Docker for CI (OTLP gRPC :4317 + HTTP :4318 ingest, query API on :16686), `/api/traces?service=X&operation=Y` query patterns, span set + parent-child + duration assertions. Use when verifying that a request produces the expected spans across service boundaries in a running Jaeger backend.
79
99%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
Deep reference for the jaeger-trace-tests SKILL.md. Consult for the full
Jaeger query API surface, parent-child + duration assertion patterns, the
GitHub Actions service block, and per-test isolation / retention on a shared
CI backend.
Jaeger exposes trace data over an HTTP query API on :16686:
| Endpoint | Returns |
|---|---|
GET /api/services | List of service names |
GET /api/services/{service}/operations | Operations for a service |
GET /api/traces?service=X&operation=Y&lookback=5m&limit=10 | Trace JSON |
GET /api/traces/{traceId} | Single trace by ID |
Trace JSON response shape (selected fields):
{
"data": [{
"traceID": "abc...",
"spans": [
{
"spanID": "def...",
"operationName": "order.create",
"duration": 12345,
"tags": [{"key": "order.item_count", "type": "int64", "value": 1}],
"references": [{"refType": "CHILD_OF", "spanID": "parent..."}]
}
]
}]
}duration is in microseconds; tags is a flat list of typed key/value
pairs; parent links live in references, not on the child span directly.
referencesJaeger encodes parent links as references with refType: "CHILD_OF".
def parent_id(span):
refs = span.get("references", [])
child_of = [r for r in refs if r["refType"] == "CHILD_OF"]
return child_of[0]["spanID"] if child_of else None
assert parent_id(db_span) == order_span["spanID"]Assert a duration ceiling straight off the microsecond duration field:
assert order_span["duration"] < 500_000 # under 500msRun the all-in-one image as a job service so every step can reach OTLP ingest and the query API:
services:
jaeger:
image: cr.jaegertracing.io/jaegertracing/jaeger:2.17.0
ports:
- 16686:16686
- 4317:4317
- 4318:4318Full port map: 16686 query API + UI, 4317 OTLP/gRPC ingest, 4318
OTLP/HTTP ingest, 5778 sampling config, 9411 Zipkin (B3) compatibility.
CI runs many tests against one Jaeger. Scope each test with a unique
service.name (or a unique trace tag) so a query never sees another
test's spans:
service_name = f"orders-test-{uuid4()}"
# configure the SDK with this service name, then query filtered by itIn-memory storage is bounded by Jaeger's eviction; long test runs should restart the container or accept eviction.
All-in-one uses transient in-memory storage per the Jaeger getting-started docs. For longer runs mount a config or restart the container between workflows:
docker run ... \
-v /path/to/config.yaml:/jaeger/config.yaml \
cr.jaegertracing.io/jaegertracing/jaeger:2.17.0 \
--config /jaeger/config.yamlProduction storage backends (Cassandra, Elasticsearch, OpenSearch, Badger) matter for real deployments; the in-memory all-in-one is sufficient for CI.