auto-instrument the std network clients when observability is on#451
Merged
Conversation
when tracing is active (after obs.init), the http and grpc clients now create a client span, inject a W3C traceparent into the outbound request, and record RED metrics — with no span code in the app. a small pair of server-side helpers (begin_server_span / end_server_span) lets a handler extract the inbound traceparent and record a server span in two lines. every hook gates on trace.is_active() first, so an uninstrumented program (the default) pays a single bool check and behaves byte-identically. - grpc Conn.unary: CLIENT span named after the full method, rpc.* attrs, traceparent injected into the request headers, span status from the grpc-status, and rpc_client_requests_total / rpc_client_duration_ms. streaming opens propagate the current trace context too. - http ClientRequest.send / send_tls_with_config: CLIENT span "HTTP <method>", http.* attrs, traceparent header, status from the response code, and http_client_requests_total / http_client_duration_ms. - http begin_server_span / end_server_span: SERVER span joined to the inbound traceparent, plus http_server_requests_total / http_server_duration_ms. tested: pith test on std/net/grpc.pith (24), std/net/http.pith (23), and std/net/http2/connection.pith (36); make run-examples (86) and run-regressions to prove off-by-default is transparent; and an end-to-end program that runs a traced client call against a traced server and confirms both spans share one trace id with the server span parented under the client span, and that the RED counters incremented.
the auto-instrumentation makes std.net.grpc and std.net.http import std.trace unconditionally. trace only borrowed log's id generators, but importing log dragged its whole public surface — including a generic `struct Field` — into every grpc/http consumer's compile graph. that collided with the protogen tool's own `struct Field` (E219). trace now generates ids straight from std.uuid, the same source log uses, so the format is unchanged and correlation still lines up. log->trace is the right direction anyway: a log record reads the current trace id, the tracer never needs the logger.
kacy
force-pushed
the
feat/o11y-autoinstrument
branch
from
July 17, 2026 20:03
12f2fbd to
809771a
Compare
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.
summary
phase 4 of the observability sprint: when tracing is active (after
obs.init), the std http and grpc clients now create a client span, inject a W3Ctraceparentinto the outbound request, and record RED metrics — with no span code in the app. a small pair of server-side helpers lets a handler record a server span in two lines.every hook gates on
trace.is_active()first, so the default (uninstrumented) program pays a single bool check and behaves byte-identically. no compiler change, no bootstrap-seed refresh — std only.hooks
Conn.unary— CLIENT span named after the full method,rpc.system/rpc.methodattrs,traceparentinjected into the request headers, span status from the grpc-status, andrpc_client_requests_total{rpc_method,grpc_status}+rpc_client_duration_ms{rpc_method}. the streaming opens propagate the current trace context viatraceparenttoo.ClientRequest.send/send_tls_with_config— CLIENT span"HTTP <method>",http.request.method/http.urlattrs,traceparentheader, status from the response code (>= 400 or a transport error is an error), andhttp_client_requests_total{method,status}+http_client_duration_ms{method}.begin_server_span(req)/end_server_span(span, status)— new public helpers.beginextracts the inboundtraceparentso the SERVER span joins the caller's trace;endsets status, ends, and recordshttp_server_requests_total{method,route,status}+http_server_duration_ms{method,route}. usage is documented in the doc comment (two lines around a handler, since there is no server framework).what was tested
pith testonstd/net/grpc.pith(24),std/net/http.pith(23),std/net/http2/connection.pith(36) — all pass.make run-examples(86 passed) andmake run-regressions(218 passed) — proves off-by-default is transparent.trace_idand the server span'sparent_idequals the client span'sspan_id(traceparent propagated across the wire), andmetrics.snapshot_text()showed both the client and server RED counters.design notes
send/send_tls_with_config/unaryreturn exactly what they did.