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.

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