Skip to main content
Changed in v0.11.0: Config.Logger is a *slog.Logger. The nexus.Logger interface and NoopLogger are gone.
A nil Logger discards everything.

Printing a client or a config

Both are safe to print and to log. Config and *Client implement fmt.Stringer, fmt.GoStringer and slog.LogValuer, so every formatting channel renders the same redacted form:
All three interfaces are needed, and this is why: fmt’s own rules send %#v to GoStringer rather than to Stringer, and a slog handler resolves LogValuer before it formats a value at all. APIKey:set is the whole answer about the credential - not a prefix, not a length. The counts and the base URL are there deliberately: a redacted form nobody can read gets replaced by a raw one. Two things this cannot cover. fmt reaches a value in an unexported field of another struct by reflection and cannot call a method on it, so %+v on your own type holding a nexus.Config in an unexported field renders that config field by field - keep the field exported, or give your type its own String(). And the value GetSecret returns is a plain string: what it goes into afterwards is yours.

Why *slog.Logger

log/slog is the standard library’s structured logger and the ecosystem has settled on its Handler interface, so accepting a *slog.Logger reaches every backend without the SDK depending on any of them. zap, zerolog and logrus all provide a handler, and so does anything else worth using. It also makes the output aggregatable. Values arrive as attributes rather than baked into the message text, so every occurrence of an event shares one message template: “how often did the stream drop” becomes a countable query rather than a substring search over lines that are each slightly different.

What the output looks like

Every record carries a component attribute naming the subsystem: So the three can be routed or filtered independently without parsing message text.

Levels

Nothing above Error is ever emitted: the SDK does not decide that an application should stop.

Scoping the SDK’s output

Pass a logger that already carries attributes of your own and everything the SDK emits inherits them:
To send SDK output somewhere separate from the rest of the application, hand it a different handler:
To suppress the SDK’s debug records while keeping your own, set the level on the handler you pass in - the SDK has no level setting of its own.

Secrets never appear

Secret values are never logged, as a message or as an attribute, and neither are secret key names. The same holds for the API key, the workload OIDC token and the session JWT. A test drives a sync carrying both a text and a file-type secret through a capturing handler at debug level and fails if any of them shows up in the output, so this is a checked property rather than a convention.

Observer faults

A StreamObserver callback that panics is recovered, and the fault is reported here at Error with the hook name and a stack trace:
The stream stays up.

Next