Documentation
¶
Index ¶
- func DecActiveRequests(ctx context.Context, peer, method, addr string)
- func EndHTTPClientSpan(span trace.Span, statusCode int, errType string, responseBytes int, err error)
- func IncActiveRequests(ctx context.Context, peer, method, addr string)
- func IncRetry(ctx context.Context, peer, method, reason string)
- func InitHTTPMeter()
- func InitHTTPTracer()
- func RecordHTTPClientMetrics(ctx context.Context, m *HTTPClientMeasurement)
- func ResetMeterForTesting()
- func ResetTracerForTesting()
- func StartHTTPClientSpan(ctx context.Context, info *HTTPSpanInfo) (context.Context, trace.Span)
- type HTTPClientMeasurement
- type HTTPSpanInfo
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DecActiveRequests ¶
DecActiveRequests decrements the http.client.active_requests counter. Call this after the request completes (in a defer alongside IncActiveRequests). addr is the server hostname (server.address, omitted when empty).
func EndHTTPClientSpan ¶
func EndHTTPClientSpan(span trace.Span, statusCode int, errType string, responseBytes int, err error)
EndHTTPClientSpan applies the final response/error attributes and the status mapping, then ends the span. statusCode == 0 signals a transport error (no response received).
Status mapping (OTel HTTP client semantic conventions):
- 100-499 (incl. 4xx) without err → status unset (default OK).
- 500-599 → codes.Error, message "HTTP {code}".
- transport error (statusCode == 0) → codes.Error, message = error.type.
- any err != nil regardless of statusCode → RecordError(err) + error.type attribute. Without this, callers that pass (status, errType, err) for non-transport failures (e.g. response interceptor errors on an HTTP 200, or HTTPError wrappers on a final 5xx) would lose the error attribution that the parallel metric path records — see RecordHTTPClientMetrics.
The 4xx-as-OK rule is the OTel *client* convention — a 4xx without a transport/interceptor error is a normal flow-control response from the server, not a client-side fault. A 4xx with err (e.g. a build-failure classified as interceptor_failed) does take the error path so the failure surfaces in the span.
On nil-or-no-op span, the call is a zero-cost no-op.
func IncActiveRequests ¶
IncActiveRequests increments the http.client.active_requests counter. Call this immediately before sending a request. peer is the peer.service value (omitted when empty); method is the HTTP method; addr is the server hostname (server.address, omitted when empty).
func IncRetry ¶
IncRetry increments the http.client.retries.total counter. peer is the peer.service value (omitted when empty); method is the HTTP method (normalized via canonicalMethod); reason should be one of: "timeout", "5xx", "network", "build_response".
func InitHTTPMeter ¶
func InitHTTPMeter()
InitHTTPMeter initializes the HTTP client meter idempotently. Subsequent calls after the first are no-ops. Holds meterInitMu so that concurrent calls and ResetMeterForTesting cannot race on meterOnce.
func InitHTTPTracer ¶
func InitHTTPTracer()
InitHTTPTracer initializes the HTTP client tracer idempotently. Subsequent calls after the first are no-ops. Holds tracerInitMu so concurrent calls and ResetTracerForTesting cannot race on tracerOnce.
func RecordHTTPClientMetrics ¶
func RecordHTTPClientMetrics(ctx context.Context, m *HTTPClientMeasurement)
RecordHTTPClientMetrics records all per-request OTel metrics after a request completes. It emits:
- http.client.request.duration (seconds)
- http.client.request.body.size (bytes, when > 0)
- http.client.response.body.size (bytes, when > 0)
func ResetMeterForTesting ¶
func ResetMeterForTesting()
ResetMeterForTesting resets the package-level meter state so each test starts with a fresh meter. This is intended for use by httpclient package tests that cannot access the unexported meter state directly. Safe to call concurrently with InitHTTPMeter — the mutex serializes both paths.
func ResetTracerForTesting ¶
func ResetTracerForTesting()
ResetTracerForTesting resets the package-level tracer state so each test starts with a fresh tracer. Mirrors ResetMeterForTesting. Safe to call concurrently with InitHTTPTracer — the mutex serialises both paths.
func StartHTTPClientSpan ¶
StartHTTPClientSpan opens a CLIENT-kind span as a child of any active span on ctx. Returns the new context (with the span attached) and the span itself.
When the global tracer provider is a no-op (observability.enabled: false), `otel.Tracer(...)` returns a no-op tracer and the returned span is non-recording — every span method becomes a no-op and the per-request cost reduces to one cached lookup.
The same function serves both the parent "Do" span (info.ResendCount = 0) and per-attempt spans (info.ResendCount = attempt index). Caller decides by passing the appropriate context and info.
Note: we deliberately do NOT fall back to `trace.SpanFromContext(ctx)` even if httpTracer were nil — that would return whatever upstream span lives on ctx (e.g. the surrounding server-request span), and the caller's subsequent `EndHTTPClientSpan` would erroneously call `.End()` on that parent, corrupting its lifecycle. `otel.Tracer()` is documented to never return nil, so init is always sufficient; the only nil-safety we keep is on the returned span via `EndHTTPClientSpan`'s `span == nil` guard, which covers callers passing a literal nil (not relevant here but cheap to keep).
Types ¶
type HTTPClientMeasurement ¶
type HTTPClientMeasurement struct {
// PeerName is the logical peer service name (peer.service). Omitted when empty.
PeerName string
// Method is the HTTP method (uppercase canonical; unknown methods → "_OTHER").
Method string
// URL is the request URL, used to extract server.address, server.port, and url.scheme.
URL *url.URL
// StatusCode is the HTTP response status code. 0 means a transport error (no response).
StatusCode int
// ErrorType is the OTel error.type enum value. Empty string on success.
ErrorType string
// ResendCount is the number of prior attempts (0 = first attempt).
ResendCount int
// Elapsed is the total request duration, recorded as seconds in the histogram.
Elapsed time.Duration
// RequestBytes is the request body size in bytes. Skipped when 0.
RequestBytes int
// ResponseBytes is the response body size in bytes. Skipped when 0.
ResponseBytes int
}
HTTPClientMeasurement carries all per-request data needed to emit OTel metrics.
type HTTPSpanInfo ¶
type HTTPSpanInfo struct {
// PeerName is the logical peer service name (peer.service). Omitted when empty.
PeerName string
// Method is the HTTP method (canonicalised to uppercase; "_OTHER" for non-standard).
Method string
// URL provides server.address, server.port, url.scheme, and url.path.
URL *url.URL
// ResendCount is the number of prior attempts (0 = first). Omitted when 0,
// and always omitted on the parent "Do" span (the logical rollup).
ResendCount int
}
HTTPSpanInfo carries the per-span data needed to construct a CLIENT-kind span for an outbound HTTP call. It is the span-side twin of HTTPClientMeasurement; every span-eligible field on the measurement also lives here.