This document covers how to write logs in a Substrate component. It is the logging counterpart of Metrics Best Practices and Tracing Best Practices.
For how to read actor and component logs, see Actor Observability.
Fields labeled debug_redact (see
API style guide section 12)
are masked when a process that uses the shared handler below logs a proto.
The label applies to every proto in this repository, not only the public API:
atelet, ateworker, credential provider, and object store protos are logged the
same way.
Masking is done by the shared slog handler in internal/contextlogging, which
serverboot.InitLogger and InitLoggerWithWriter install as the default
logger. A server must set up logging through one of them; a process that
builds its own handler gets no masking. These processes build their own today:
podcertcontrolleratenet sdsmintboomer-worker- plugins under
internal/plugins/, such asgsm-credential-provider. A plugin there imports onlypkg/(see its README), so it cannot callserverboot. Ifgsm-credential-providerlogged theFetchSecretResponseit serves,opaque_byteswould show in clear. The plugins undercmd/useserverbootand are masked.
The handler masks labeled fields in a proto it is handed as an attribute value
(slog.Any("resp", resp)), inside a slog.Group, as the result of a
slog.LogValuer, or bound with logger.With. This covers the request and
response bodies the gRPC interceptors log. A singular string field becomes
[REDACTED]. A field of any other kind (bytes, repeated, map, message) is
dropped, because no placeholder fits it. The caller's message is not modified.
- Log a proto as its own attribute. The handler cannot see a proto that is:
- inside a slice, map, or plain Go struct:
slog.Any("env", []*EnvVar{...}); - turned into a string first. protobuf-go's
String(),%v,prototext, andprotojsonall print labeled fields in clear; - packed in a
google.protobuf.Any.
- inside a slice, map, or plain Go struct:
- The label only affects logs written by that handler. Span attributes, metric
labels, error messages, and the OTLP copy of actor events (built by
internal/actorevent) are not masked; keep secrets out of them. - Adding or removing a label fails the tests in
internal/protoredactuntil the list of labeled fields there is updated, so the change is reviewed as a decision about what logs may show.
The exact list of what the handler covers is on ContextHandler.Handle in
internal/contextlogging.