grpc/grpc-go · critical
cannot use component logger as grpclog logger
Error message
cannot use component logger as grpclog logger
What it means
grpclog.SetLoggerV2 (grpclog/loggerv2.go:35) installs the process-wide gRPC logger. componentData (grpclog/component.go:26) is the internal per-component logger returned by grpclog.Component(name): it does NOT log on its own, it delegates to the global logger and prefixes a component name. Setting a componentData as the global logger would create a recursive delegation (global -> component -> global) and break the contract, so SetLoggerV2 explicitly type-asserts against *componentData and panics at line 36-38.
Solutions
- Pass a logger built with grpclog.NewLoggerV2 / NewLoggerV2WithVerbosity, or an adapter for logrus/zap/zaplogr that implements grpclog.LoggerV2.
- If you want component-prefixed output, configure the underlying global logger with prefixes; do not feed a Component() back into SetLoggerV2.
- Check the concrete type is not the internal component logger before calling (it is unexported, so the practical check is: never pass a value returned by grpclog.Component).
Example fix
// before
grpclog.SetLoggerV2(grpclog.Component("myapp")) // panic: component logger
// after
grpclog.SetLoggerV2(grpclog.NewLoggerV2(os.Stdout, os.Stdout, os.Stderr))
// or with an adapter, e.g.:
// grpclog.SetLoggerV2(zaplogger.New(zap.L())) Defensive patterns
Strategy: validation
Validate before calling
// Component() returns the internal *componentData (unexported), so the only safe rule is:
// never pass a grpclog.Component(...) result to SetLoggerV2.
func setGlobalLogger(l grpclog.LoggerV2) {
// l must come from NewLoggerV2 or an external adapter, not Component().
grpclog.SetLoggerV2(l)
} Type guard
// componentData is unexported, so callers cannot name it directly.
// There is no public marker; the practical guard is: never pass a value
// obtained from grpclog.Component(...) into SetLoggerV2.
func looksLikeComponentLogger(l grpclog.LoggerV2) bool { return false } Prevention
- Always build the global logger with grpclog.NewLoggerV2 / NewLoggerV2WithVerbosity or a known adapter (zap, logrus).
- Reserve grpclog.Component(name) for obtaining child loggers inside gRPC subsystems, never as the global.
- In review, flag any SetLoggerV2(grpclog.Component(...)).
When it happens
Trigger: Calling grpclog.SetLoggerV2(grpclog.Component("myapp")) - i.e. passing the result of Component() instead of a real logger implementation.
Common situations: Wanting named/prefixed logging and reaching for Component() in the wrong place; copy-pasting a logging setup snippet that assigned the component logger variable to SetLoggerV2; confusing the global-logger setter with the component-logger factory.
Related errors
- cannot register a nil Codec
- cannot register a nil CodecV2
- cannot register Codec with empty string result for Name()
- cannot register CodecV2 with empty string result for Name()
- Cannot free freed buffer
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/6ed7f38863dc18fb.
Report an issue: GitHub.
Appendix: source
Thrown at grpclog/loggerv2.go:37
package grpclog
import (
"io"
"os"
"strconv"
"strings"
"google.golang.org/grpc/grpclog/internal"
)
// LoggerV2 does underlying logging work for grpclog.
type LoggerV2 internal.LoggerV2
// SetLoggerV2 sets logger that is used in grpc to a V2 logger.
// Not mutex-protected, should be called before any gRPC functions.
func SetLoggerV2(l LoggerV2) {
if _, ok := l.(*componentData); ok {
panic("cannot use component logger as grpclog logger")
}
internal.LoggerV2Impl = l
internal.DepthLoggerV2Impl, _ = l.(internal.DepthLoggerV2)
}
// NewLoggerV2 creates a loggerV2 with the provided writers.
// Fatal logs will be written to errorW, warningW, infoW, followed by exit(1).
// Error logs will be written to errorW, warningW and infoW.
// Warning logs will be written to warningW and infoW.
// Info logs will be written to infoW.
func NewLoggerV2(infoW, warningW, errorW io.Writer) LoggerV2 {
return internal.NewLoggerV2(infoW, warningW, errorW, internal.LoggerV2Config{})
}
// NewLoggerV2WithVerbosity creates a loggerV2 with the provided writers and
// verbosity level.
func NewLoggerV2WithVerbosity(infoW, warningW, errorW io.Writer, v int) LoggerV2 {
return internal.NewLoggerV2(infoW, warningW, errorW, internal.LoggerV2Config{Verbosity: v})View on GitHub (pinned to 0c51461d27)