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

  1. Pass a real logger built with grpclog.NewLoggerV2(...) (or your own LoggerV2 impl), not a value from grpclog.Component(...).
  2. If you intended to set a component-specific logger, that is not what SetLoggerV2 does; set the global logger and let components inherit it.
  3. 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

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


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/6ed7f38863dc18fb. Report an issue: GitHub.