{"record":{"id":"6ed7f38863dc18fb","repo":"grpc/grpc-go","slug":"cannot-use-component-logger-as-grpclog-logger","errorCode":null,"errorMessage":"cannot use component logger as grpclog logger","messagePattern":"cannot use component logger as grpclog logger","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"grpclog/loggerv2.go","lineNumber":37,"sourceCode":"package grpclog\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"google.golang.org/grpc/grpclog/internal\"\n)\n\n// LoggerV2 does underlying logging work for grpclog.\ntype LoggerV2 internal.LoggerV2\n\n// SetLoggerV2 sets logger that is used in grpc to a V2 logger.\n// Not mutex-protected, should be called before any gRPC functions.\nfunc SetLoggerV2(l LoggerV2) {\n\tif _, ok := l.(*componentData); ok {\n\t\tpanic(\"cannot use component logger as grpclog logger\")\n\t}\n\tinternal.LoggerV2Impl = l\n\tinternal.DepthLoggerV2Impl, _ = l.(internal.DepthLoggerV2)\n}\n\n// NewLoggerV2 creates a loggerV2 with the provided writers.\n// Fatal logs will be written to errorW, warningW, infoW, followed by exit(1).\n// Error logs will be written to errorW, warningW and infoW.\n// Warning logs will be written to warningW and infoW.\n// Info logs will be written to infoW.\nfunc NewLoggerV2(infoW, warningW, errorW io.Writer) LoggerV2 {\n\treturn internal.NewLoggerV2(infoW, warningW, errorW, internal.LoggerV2Config{})\n}\n\n// NewLoggerV2WithVerbosity creates a loggerV2 with the provided writers and\n// verbosity level.\nfunc NewLoggerV2WithVerbosity(infoW, warningW, errorW io.Writer, v int) LoggerV2 {\n\treturn internal.NewLoggerV2(infoW, warningW, errorW, internal.LoggerV2Config{Verbosity: v})","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/grpclog/loggerv2.go#L19-L55","documentation":"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.","triggerScenarios":"Calling grpclog.SetLoggerV2(grpclog.Component(\"myapp\")) - i.e. passing the result of Component() instead of a real logger implementation.","commonSituations":"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.","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)."],"exampleFix":"// before\ngrpclog.SetLoggerV2(grpclog.Component(\"myapp\")) // panic: component logger\n\n// after\ngrpclog.SetLoggerV2(grpclog.NewLoggerV2(os.Stdout, os.Stdout, os.Stderr))\n// or with an adapter, e.g.:\n// grpclog.SetLoggerV2(zaplogger.New(zap.L()))","handlingStrategy":"validation","validationCode":"// Component() returns the internal *componentData (unexported), so the only safe rule is:\n// never pass a grpclog.Component(...) result to SetLoggerV2.\nfunc setGlobalLogger(l grpclog.LoggerV2) {\n    // l must come from NewLoggerV2 or an external adapter, not Component().\n    grpclog.SetLoggerV2(l)\n}","typeGuard":"// componentData is unexported, so callers cannot name it directly.\n// There is no public marker; the practical guard is: never pass a value\n// obtained from grpclog.Component(...) into SetLoggerV2.\nfunc looksLikeComponentLogger(l grpclog.LoggerV2) bool { return false }","tryCatchPattern":null,"preventionTips":["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(...))."],"tags":["grpclog","logger","registration","panic","go"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}