OpenTelemetry¶
Mite provides optional OpenTelemetry tracing for observing journey execution, transactions, and HTTP requests with minimal code changes.
Features¶
Explicit decorator - Use
@mite_http_tracedfor journeys you want to traceZero overhead for non-traced journeys -
@mite_httpjourneys have no tracing overheadDistributed tracing - Context propagation across HTTP requests
Hierarchical spans - Journey → Transaction → HTTP request traces
Multiple exporters - Console, OTLP (HTTP/gRPC), Zipkin, Jaeger
Configurable sampling - Control trace volume in production
Metrics export - HTTP request counters and duration histograms
Selective instrumentation - Mix traced and untraced journeys in the same scenario
Lazy patching - Tracing infrastructure only activated when needed
Installation¶
pip install mite[otel]
Quick start¶
First, create a simple scenario in demo_otel.py:
from mite.otel import mite_http_traced
from mite_http import mite_http
from mite import ensure_average_separation
# This journey will be traced
@mite_http_traced
async def traced_journey(ctx):
async with ensure_average_separation(1):
async with ctx.transaction("Get request"):
await ctx.http.get("https://example.com/")
# This journey will NOT be traced (zero overhead)
@mite_http
async def untraced_journey(ctx):
async with ensure_average_separation(1):
await ctx.http.get("https://example.com/health")
scenario = lambda: [
("demo_otel:traced_journey", None, lambda s, e: 1),
("demo_otel:untraced_journey", None, lambda s, e: 10),
]
1. Enable Tracing¶
export MITE_CONF_OTEL_ENABLED=true
This environment variable controls whether tracing is available. When false, even @mite_http_traced journeys will behave like regular @mite_http journeys with no tracing.
2. Run Your Test¶
mite scenario test demo_otel:scenario --hide-constant-logs
Important: Only @mite_http_traced journeys and their transactions/HTTP requests will be traced. Regular @mite_http journeys have zero tracing overhead.
3. View Traces¶
Traces are printed to console by default. For visualization, use Jaeger or Zipkin (see below).
Configuration¶
All settings are controlled via environment variables:
Variable |
Default |
Description |
|---|---|---|
|
|
Enable/disable tracing |
|
|
Service name in traces |
|
|
Sampling rate (0.0-1.0) |
|
|
|
|
|
OTLP collector/backend URL |
|
|
|
|
|
Comma-separated |
|
|
Max spans per batch |
|
|
Export timeout (seconds) |
Usage Examples¶
Console Output¶
MITE_CONF_OTEL_ENABLED=true \
MITE_CONF_OTEL_SPAN_PROCESSOR=console \
mite scenario test demo_otel:scenario --hide-constant-logs
Jaeger¶
# Start: docker run -d -p 16686:16686 -p 4318:4318 jaegertracing/all-in-one
MITE_CONF_OTEL_ENABLED=true \
MITE_CONF_OTEL_SPAN_PROCESSOR=otlp \
MITE_CONF_OTEL_OTLP_ENDPOINT=http://localhost:4318/v1/traces \
mite scenario test demo_otel:scenario --hide-constant-logs
# View: http://localhost:16686
Zipkin¶
# Start: docker run -d -p 9411:9411 openzipkin/zipkin
MITE_CONF_OTEL_ENABLED=true \
MITE_CONF_OTEL_SPAN_PROCESSOR=zipkin \
MITE_CONF_OTEL_OTLP_ENDPOINT=http://localhost:9411/api/v2/spans \
mite scenario test demo_otel:scenario --hide-constant-logs
# View: http://localhost:9411
1% sampling, authenticated¶
MITE_CONF_OTEL_ENABLED=true \
MITE_CONF_OTEL_SAMPLER_RATIO=0.01 \
MITE_CONF_OTEL_SPAN_PROCESSOR=otlp \
MITE_CONF_OTEL_OTLP_ENDPOINT=https://collector.company.com:4318/v1/traces \
MITE_CONF_OTEL_OTLP_HEADERS="Authorization=Bearer ${API_KEY}" \
mite scenario test production:scenario --hide-constant-logs
Trace Structure¶
Mite automatically creates hierarchical traces:
journey.user_login (520ms)
└── transaction.Login (450ms)
├── HTTP POST /auth/login (280ms)
│ ├── http.method: POST
│ ├── http.url: https://api.example.com/auth/login
│ ├── http.status_code: 200
│ └── http.response.time.total: 0.28
└── transaction.Fetch_Profile (170ms)
└── HTTP GET /users/me (169ms)
├── http.method: GET
├── http.status_code: 200
└── http.response.body.size: 2048
Span Types¶
Journey spans - Created for each
@mite_http_traceddecorated functionTransaction spans - Created for each
ctx.transaction()call within traced journeys onlyHTTP spans - Created for each
ctx.http.*()request within traced journeys only
Note: Regular @mite_http journeys do not create any spans, even when MITE_CONF_OTEL_ENABLED=true. This ensures zero overhead for non-traced journeys.
Distributed Tracing¶
HTTP requests automatically include W3C traceparent headers for distributed tracing:
from mite.otel import mite_http_traced
@mite_http_traced
async def call_downstream_service(ctx):
async with ctx.transaction("Call Service B"):
# Automatically includes traceparent header
response = await ctx.http.post(
"https://service-b.com/api",
json={"data": "test"}
)
The downstream service can extract the trace context to continue the distributed trace.
Manual Instrumentation¶
For custom spans or advanced use cases:
from mite.otel import trace_journey, trace_transaction
# Custom journey decorator
@trace_journey("custom_journey_name")
async def my_journey(ctx):
# Custom transaction with attributes
async with trace_transaction("database_query", table="users") as span:
if span:
span.set_attribute("query.rows", 150)
# Your logic here
Selective Instrumentation¶
You can mix traced and untraced journeys in the same scenario to optimize performance:
from mite.otel import mite_http_traced
from mite_http import mite_http
# This journey will be fully traced (journey, transactions, HTTP requests)
@mite_http_traced
async def critical_journey(ctx):
async with ctx.transaction("Critical Operation"):
await ctx.http.post("https://api.example.com/critical")
# This journey will NOT be traced at all (zero overhead)
@mite_http
async def background_journey(ctx):
async with ctx.transaction("Health Check"): # No transaction span created
await ctx.http.get("https://api.example.com/health") # No HTTP span created
scenario = lambda: [
("my_scenario:critical_journey", None, lambda s, e: 1),
("my_scenario:background_journey", None, lambda s, e: 10),
]
This allows you to:
Reduce trace volume by only instrumenting critical journeys
Achieve zero tracing overhead for high-volume background operations
Control costs when using paid tracing services
How It Works¶
When MITE_CONF_OTEL_ENABLED=true:
@mite_http_tracedjourneys: Full tracing (journey, transactions, HTTP requests)@mite_httpjourneys: No tracing at all, zero overhead
When MITE_CONF_OTEL_ENABLED=false:
Both decorators behave identically (no tracing)
The tracing infrastructure uses lazy patching - it only activates when the first @mite_http_traced journey is encountered, ensuring minimal impact on startup performance.
Performance Tuning¶
Selective Tracing¶
The most effective way to reduce overhead is to only trace critical journeys:
from mite.otel import mite_http_traced
from mite_http import mite_http
# Trace only 1 in 10 journeys
@mite_http_traced
async def important_journey(ctx):
# Full tracing
pass
# No tracing overhead for these 9
@mite_http
async def regular_journey(ctx):
# Zero overhead
pass
Sampling¶
Control trace volume in production:
# Only trace 1% of @mite_http_traced journeys
export MITE_CONF_OTEL_SAMPLER_RATIO=0.01
Disabling Tracing¶
To completely disable tracing (even for @mite_http_traced journeys):
export MITE_CONF_OTEL_ENABLED=false
This makes @mite_http_traced behave identically to @mite_http.
Batch Size¶
Adjust for memory vs latency tradeoff:
# Smaller batches = less memory, more frequent exports
export MITE_CONF_OTEL_MAX_EXPORT_BATCH_SIZE=100
# Larger batches = more memory, fewer exports
export MITE_CONF_OTEL_MAX_EXPORT_BATCH_SIZE=2048
Processor Selection¶
console- Immediate output, good for debuggingbatch- Batched exports, better for productionotlp- OTLP protocol with batchingzipkin- Zipkin-specific format with batching