Table of contents
Part of nurago, a collection of independent Go packages for backend services.
import "github.com/tecnickcom/nurago/pkg/httpclient"
An outbound HTTP client that carries the trace ID onward and logs each call in a structured, redacted form.
c := httpclient.New(httpclient.WithTimeout(10 * time.Second))
resp, err := c.Do(req)
Defaults: a 1-minute timeout, the standard transport, and the traceid default header.
Trace propagation, including when there is nothing to propagate
Do reads the trace ID from the context and sets it on the outbound request. When the context has none, it generates a UUIDv7 and attaches it to both the context and the request headers.
Generating rather than skipping is the useful behaviour. A background job, a startup task, or a periodic refresh has no inbound request and therefore no trace ID, and those are exactly the calls that are hardest to find in a log when something goes wrong. Every outbound call gets a correlatable identifier whether or not it began with one.
UUIDv7 is time-ordered, so the IDs sort chronologically. See random.
What gets redacted, and what does not
The package is specific about this, and the gaps matter more than the coverage.
Query strings are redacted at every log level, not only at debug. An api_key or token in a query parameter never reaches the log. That includes the error field of a failed request, whose embedded URL has its query and userinfo redacted.
Debug-level dumps are redacted by the configured function. The default masks Authorization-style headers, cookies, other sensitive key/value pairs, and card-like numbers.
Three things the defaults do not cover:
- The request path. A secret in the path, such as
/reset/{token}, is logged. Keep secrets out of paths. - Arbitrary secret headers. A custom
X-Api-Keyis not matched by the default redactor. Supply a stronger one throughWithRedactFn; redact is the package for building it. - Errors from a custom round-tripper that do not wrap a
*url.Error. Redact those inside the round-tripper.
Log volume is level-dependent
At debug, full request and response dumps are logged after redaction. At other levels, summary metadata only: timing, status, and the trace context.
The gap between those two is large. Debug dumps include response bodies, so enabling debug on a service handling substantial payloads can produce more log volume than the traffic itself. It is a diagnostic setting, not a default.
Errors are logged with the same trace context and timing fields, so a failure and the successful calls around it correlate.
Composition
WithTransport and WithDialContext are where the other pieces attach. dnscache’s DialContext goes here to get cached resolution; a metrics client’s InstrumentRoundTripper goes here for request metrics.
WithComponent tags log fields with the name of the dependency being called, so outbound calls are separable per upstream in a log query. WithLogFieldPrefix avoids collisions with the service’s own fields.
Retries are deliberately not here. httpretrier wraps a configured client rather than replacing it, so an instrumented and authenticated client keeps its behaviour and gains retries.
Dependencies
This package reaches no external module: it uses only the Go standard library, plus traceid, logutil and redact.
Links
- Reference documentation
- Source code
- In depth: Observing Outbound HTTP in Go Without Disturbing It
- Retries: httpretrier
- Cached resolution: dnscache
- All nurago packages