0.6.0 — Resilience observability
httpware 0.6.0 — Resilience observability
0.6.0 is additive. No breaking changes. Code written against 0.5.0 continues to work unchanged.
This release adds operational-event emission to Retry and Bulkhead via two channels — stdlib logging records (always on) and OpenTelemetry span events (opt-in via the otel extra). Re-introduces the otel extra (PR #24 removed it as YAGNI; this release brings it back paired with the code that uses it).
New features
- Structured logging on resilience operations. Acquire
logging.getLogger("httpware.retry")andlogging.getLogger("httpware.bulkhead")to see four operational events:retry.giving_up(WARNING) — max_attempts exhausted; attributes includeattempts,method,url,last_status,last_exception_typeretry.budget_refused(WARNING) —RetryBudgetrefused to permit a retryretry.streaming_refused(WARNING) — streaming-body marker prevented an otherwise-retryable retrybulkhead.rejected(WARNING) —acquire_timeoutelapsed without acquisition; attributes includemax_concurrent,acquire_timeout,method,url
- Optional OpenTelemetry attribute enrichment. Install
httpware[otel](which pullsopentelemetry-api>=1.20, just the API — you supply the SDK). When installed, the same four events are added to the active span viatrace.get_current_span().add_event(name, attributes=...). We never create our own spans — for HTTP-level tracing installopentelemetry-instrumentation-httpxseparately.
Backwards compatibility
Purely additive:
- All previously-shipping methods behave identically.
- Successful retries and successful bulkhead acquisitions emit nothing — the four events fire only on operational concern.
- Per
engineering.md §2, httpware never configures handlers, levels, or callslogging.basicConfig(). Consumers own their logging configuration. - The
otelextra is opt-in —pip install httpwarecontinues to work withoutopentelemetry-api.
Usage
import logging
from httpware import AsyncClient, Bulkhead, Retry
# Enable visibility into retry / bulkhead operational events
logging.getLogger("httpware.retry").setLevel(logging.WARNING)
logging.getLogger("httpware.bulkhead").setLevel(logging.WARNING)
# Your normal application logging config picks up the records
logging.basicConfig(level=logging.WARNING, format="%(asctime)s %(name)s %(message)s")
async with AsyncClient(
base_url="https://api.example.com",
middleware=[Bulkhead(max_concurrent=10), Retry()],
) as client:
await client.get("/users/1")
# On a 503 + retry exhaustion you'll see:
# 2026-06-05 12:00:00 httpware.retry retry gave up after 3 attemptsFor OTel span events:
pip install httpware[otel]
# Plus your SDK + opentelemetry-instrumentation-httpx for HTTP-level spansWhat's still ahead
Epic 5's original 5-1 (hook protocol) and 5-4 (standalone OTel middleware) stories are retired, not deferred. Rationale in the spec: opentelemetry-instrumentation-httpx already covers transport-level tracing, and a hook system without a built-in consumer is infrastructure for code that doesn't exist. The structured-emission contract we're shipping is already extensible — users plug into standard logging handlers without needing httpware-specific hooks.
This effectively closes Epic 5. Remaining roadmap is Epic 6 (ship v1.0): docs site (mkdocs), benchmarks, Trusted Publishers + Sigstore release flow.