motel: Getting Started¶
2026-02-11T16:00:00Z
motel generates synthetic OTLP traces from a YAML topology definition. This demo walks through the basics: checking the tool, validating a config, generating traces, and inspecting the output.
Version check¶
Confirm motel is built and available.
motel version | grep -c 'motel'
1
The topology file¶
A topology defines services, their operations, call relationships, and traffic settings. Here is a minimal two-service example.
cat docs/examples/traffic-patterns.yaml
# Minimal topology for demonstrating traffic patterns
# Two services, one call — keeps output easy to read
version: 1
services:
api:
operations:
request:
duration: 10ms +/- 3ms
error_rate: 1%
calls:
- database.query
database:
operations:
query:
duration: 5ms +/- 2ms
error_rate: 0.1%
traffic:
rate: 50/s
pattern: uniform
Validation¶
The validate command checks structural correctness: service and operation references, duration formats, error rates, and traffic configuration.
motel validate docs/examples/traffic-patterns.yaml
Configuration valid: 2 services, 1 root operation
The validator catches common mistakes. For example, a broken call reference:
cat > /tmp/bad-topology.yaml << 'EOF'
version: 1
services:
api:
operations:
request:
duration: 10ms
calls:
- nonexistent.op
traffic:
rate: 10/s
EOF
motel validate /tmp/bad-topology.yaml 2>&1 | head -1
Error: service "api" operation "request": call "nonexistent.op" references unknown operation
Generating traces¶
Run with --stdout to emit spans as JSON, one per line. The --duration flag controls how long the generator runs.
motel run --stdout --duration 200ms docs/examples/traffic-patterns.yaml 2>/dev/null | jq -rs '
"spans generated: \(length > 0)",
"services: \([.[].Attributes[] | select(.Key == "synth.service") | .Value.Value] | unique)"'
spans generated: true
services: ["api","database"]
Span structure¶
Each span carries standard OTel fields: trace and span IDs, timestamps, attributes, and status. Root operations are SERVER spans (SpanKind 2); downstream calls are CLIENT spans (SpanKind 3).
motel run --stdout --duration 200ms docs/examples/traffic-patterns.yaml 2>/dev/null | jq -rs '
group_by(.SpanContext.TraceID) | .[0] |
"spans per trace: \(length)",
"span kinds (2=SERVER, 3=CLIENT): \([.[].SpanKind] | unique | sort)",
"has root span: \(map(select(.Parent.TraceID == "00000000000000000000000000000000")) | length == 1)",
"root operation: \(map(select(.Parent.TraceID == "00000000000000000000000000000000")) | .[0].Name)"'
spans per trace: 2
span kinds (2=SERVER, 3=CLIENT): [2,3]
has root span: true
root operation: request
Run statistics¶
At the end of each run, motel emits a JSON summary to stderr with trace and span counts, timing, and error rates.
motel run --stdout --duration 500ms docs/examples/traffic-patterns.yaml 2>&1 >/dev/null | tail -1 | jq -r '
"fields: \(keys)",
"traces > 0: \(.traces > 0)",
"spans per trace: \(.spans / (if .traces == 0 then 1 else .traces end) | floor)"'
fields: ["elapsed_ms","error_rate","errors","spans","spans_per_second","traces","traces_per_second"]
traces > 0: true
spans per trace: 2
Each trace in this topology produces exactly 2 spans (api.request calls database.query), so the spans-per-trace ratio is always 2.