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 (loggerv2.go:35) sets the global gRPC logger and panics if you pass it a *componentData (the type returned by grpclog.Component(...)). A component logger is scoped to a sub-component and delegates back to the global logger, so making it the global logger would cause infinite recursion; gRPC refuses this to prevent a stack overflow at first log.
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 03255a9237)
Solutions
- Pass a real logger built with grpclog.NewLoggerV2(...) (or your own LoggerV2 impl), not a value from grpclog.Component(...).
- If you intended to set a component-specific logger, that is not what SetLoggerV2 does; set the global logger and let components inherit it.
- Rename variables so a component logger is never accidentally passed as the global one.
Example fix
// before
compLog := grpclog.Component("myapp")
grpclog.SetLoggerV2(compLog) // panics
// after
grpclog.SetLoggerV2(grpclog.NewLoggerV2(os.Stdout, os.Stderr, os.Stderr)) Defensive patterns
Strategy: validation
Validate before calling
// Never pass a component logger to SetLoggerV2
func setGlobalLogger(l grpclog.LoggerV2) {
if l == nil { log.Fatal("logger nil") }
// component loggers are *componentData (unexported); the safe rule:
// only pass loggers from NewLoggerV2 or your own LoggerV2 impl.
grpclog.SetLoggerV2(l)
} Prevention
- Keep a clear type distinction: component loggers (from Component()) are NEVER global loggers.
- Pass only grpclog.NewLoggerV2(...) or a custom LoggerV2 implementation to SetLoggerV2.
- Call SetLoggerV2 once at startup before any gRPC activity.
When it happens
Trigger: Calling grpclog.SetLoggerV2(grpclog.Component(...)) directly, or assigning a component logger (obtained via Component(packageName)) into the variable you later pass to SetLoggerV2.
Common situations: Copy-pasting logger-setup code; confusing the global logger with a per-component logger; refactor that swapped in a component logger where a real logger was expected.
Related errors
- The unary server interceptor was already set and may not be
- The stream server interceptor was already set and may not be
- The tap handle was already set and may not be reset.
- grpc: no transport security set (use grpc.WithTransportCrede
- grpc: credentials.Bundle may not be used with individual Tra
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/6ed7f38863dc18fb.
Report an issue: GitHub.