Phase 7c: SDK-free observability + protocol-agnostic model example - #18
Merged
Merged
Conversation
Completes the observability story short of a telemetry-SDK adapter (recorded in the PLAN as an optional post-0.1.0 //runtime:otel target; opentelemetry-cpp's protobuf/gRPC tree stays out of the dep-light core): - HttpResponse gains a non-wire `operation` annotation; the router stamps it from a new Router::Add operation-name argument (both protocol generators pass the Smithy operation name), so middleware outside the generated router can label by operation. - RequestObservation now carries `operation` and `trace_parent` (the incoming W3C traceparent header, verbatim) alongside method/target/status/duration — log correlation and per-operation metrics without touching generated code. Dispatch failures (404/405) report an empty operation. - smithy/http/trace_context.h: W3C Trace Context helpers — ParseTraceparent (strict hex, zero-id and version-ff rejection, future-version tolerance), FormatTraceparent, GenerateTraceContext, GenerateSpanId. - smithy/client/observability.h: ObserveAttempts (one callback per HTTP attempt: method/target/attempt/status or transport error — the client-side metrics hook) and PropagateTraceContext (sets traceparent on attempts that lack one; optional current-context source joins the application's active trace, otherwise fresh sampled roots). - Weather e2e: PropagateTraceContext + ObserveAttempts on the generated client against Observe on the generated server — the observation reports operation "GetCity" and a parseable sampled traceparent; an unrouted request reports 404 with an empty operation. - docs: production-guide Observability section (including the explicit OpenTelemetry stance), runtime overview, PLAN. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WjaNFwBZxoHdqagvq8ycQf
Demonstrates the upstream Smithy vision in the quick start: the todo model no longer declares a protocol — @http traits describe HTTP semantics abstractly, and small overlay files bind the service to a concrete protocol with `apply`. The consumer now generates the same acme.todo#Todo service twice, side by side: restJson1 under acme::todo and rpcv2Cbor under acme::todo::cbor, with an integration test proving identical handler semantics over the second wire protocol. Fixes the runner bug this exposed: --model was documented as repeatable but only the last flag won, so a base model plus overlay could never assemble ("Trait applied to unknown shape"). The runner now imports every --model file, matching the Bazel rule, which already passed one flag per file in srcs. docs/quickstart.md teaches the overlay flow (and notes the inline-trait shortcut still works). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WjaNFwBZxoHdqagvq8ycQf
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WjaNFwBZxoHdqagvq8ycQf
The greeter model the rule tests generate against is now protocol-agnostic, with a greeter_restjson1.smithy `apply` overlay — so every smithy_cpp_*_library rule test exercises multi-file model assembly on every CI run. Adds an analysis-test assertion that each srcs entry becomes its own --model flag on the generator action, and a functional greeter_roundtrip_test proving the overlay-bound service's generated client and server work together in-process (this is the in-tree counterpart of the consumer example's overlay flow, and runs in the main bazel job on all four OS configs). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WjaNFwBZxoHdqagvq8ycQf
Bug-shaking coverage for the painful spots, plus the missing REST-path showcase: - Weather gains GetReport, the first greedy-label (@http uri "/reports/{reportPath+}") example operation. E2e proves greedy labels keep embedded slashes and decode percent-escapes segment by segment (spaces, %, ?, #, &, UTF-8). - Hostile plain-label round-trips: CityId values inside its @pattern charset echo back exactly through encode -> route -> decode (the handler error message is the oracle, including consecutive/leading/ trailing spaces); URI-delimiter and UTF-8 values outside the pattern are rejected by generated validation with a clean 400 — never a crash, mis-route, or mangled echo. - Paginator drives a token full of URI-hostile characters ("a b&c=d?e+f/g%h#i") through the output-body -> input-query round trip and the server verifies it arrives byte-identical. - Full-stack layering: FlakyTransport under retry x bearer_token x PropagateTraceContext x ObserveAttempts against Chain(RequireBearerAuth, Observe) — client hooks see all three attempts (-1, -1, 200), the server sees exactly one authorized, traced request labeled GetCity. - @httpApiKeyAuth(in: "query") wire test pins both target-append branches ('?' with no query string, '&' after one) and that the key itself is percent-encoded ("k&e y" -> "k%26e%20y"). - Cafe's OrderCoffee is now @requestCompression(["gzip"]): wire tests pin no-compression under the threshold, compression at threshold 0 and past the 10 KiB default, and that the payload gunzips back to the exact CBOR (clientToken preserved byte-for-byte). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WjaNFwBZxoHdqagvq8ycQf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The observability slice discussed in review: enrich the existing hooks so real backends (including OpenTelemetry) plug in cleanly, add W3C Trace Context propagation — and keep the core SDK-free. The optional
//runtime:oteladapter is recorded in the PLAN as post-0.1.0 (opentelemetry-cpp's protobuf/gRPC dependency tree stays out of the dep-light core; hook shapes stabilize in production first).Addendum (second commit): the consumer example now demonstrates the upstream protocol-agnostic vision, and fixes a runner bug it exposed — see the last section.
Operation names in observations
HttpResponsegains a non-wireoperationannotation;Router::Addtakes the operation name and stamps it after dispatch (both protocol generators pass it, goldens regenerated).RequestObservationnow reportsoperation— per-operation metrics and logs without touching generated code. Dispatch failures (404/405) report it empty.Trace correlation
RequestObservation.trace_parentcarries the incomingtraceparentheader verbatim for log correlation.smithy/http/trace_context.h:ParseTraceparent(strict lowercase hex, zero-id and version-ffrejection, future-version tolerance),FormatTraceparent,GenerateTraceContext,GenerateSpanId.smithy::PropagateTraceContext()client interceptor: setstraceparenton every attempt that lacks one; pass a current-context source to join the application's active trace, omit for fresh sampled roots.Client attempt observation
smithy::ObserveAttempts(callback)— the client-side counterpart of the server'sObserve: one callback per HTTP attempt with method/target/attempt/status (−1 + message on transport errors), retries visible viaattempt > 1.Tests
PropagateTraceContext+ObserveAttemptson the generated client againstObserveon the generated server — the served observation reports operationGetCityand a parseable sampled traceparent; an unrouted request reports 404 with an empty operation.Protocol-agnostic model example (upstream Smithy vision)
examples/bazel-consumer/model/todo.smithyno longer declares a protocol:@httptraits describe HTTP semantics abstractly, and overlay files (model/bindings/restjson1.smithy,model/bindings/rpcv2cbor.smithy) bind the service withapply.acme.todo#Todoservice twice, side by side — restJson1 underacme::todo, rpcv2Cbor underacme::todo::cbor— with an integration test proving identical handler semantics over the second wire protocol.--modelwas documented repeatable but only the last flag won, so base-model-plus-overlay could never assemble ("Trait applied to unknown shape"). The runner now imports every--modelfile, matching what the Bazel rule already passed.docs/quickstart.mdteaches the overlay flow (the inline-trait shortcut still works).Docs
Production guide gains an Observability section with the explicit OpenTelemetry stance; runtime overview, quickstart, and PLAN updated.
Validation
bazel test //...— 45/45 green locally, plus ASan+UBSan (Beast targets CI-verified as usual)🤖 Generated with Claude Code
https://claude.ai/code/session_01WjaNFwBZxoHdqagvq8ycQf