Package grpc_zap
`grpc_zap` is a gRPC logging middleware backed by ZAP loggers
It accepts a user-configured `zap.Logger` that will be used for logging completed gRPC calls. The same `zap.Logger` will
be used for logging completed gRPC calls, and be populated into the `context.Context` passed into gRPC handler code.
On calling `StreamServerInterceptor` or `UnaryServerInterceptor` this logging middleware will add gRPC call information
to the ctx so that it will be present on subsequent use of the `ctx_zap` logger.
If a deadline is present on the gRPC request the grpc.request.deadline tag is populated when the request begins. grpc.request.deadline
is a string representing the time (RFC3339) when the current call will expire.
This package also implements request and response *payload* logging, both for server-side and client-side. These will be
logged as structured `jsonpb` fields for every message received/sent (both unary and streaming). For that please use
`Payload*Interceptor` functions for that. Please note that the user-provided function that determines whether to log
the full request/response payload needs to be written with care, this can significantly slow down gRPC.
ZAP can also be made as a backend for gRPC library internals. For that use `ReplaceGrpcLoggerV2`.
*Server Interceptor*
Below is a JSON formatted example of a log that would be logged by the server interceptor:
{
"level": "info", // string zap log levels
"msg": "finished unary call", // string log message
"grpc.code": "OK", // string grpc status code
"grpc.method": "Ping", // string method name
"grpc.service": "mwitkow.testproto.TestService", // string full name of the called service
"grpc.start_time": "2006-01-02T15:04:05Z07:00", // string RFC3339 representation of the start time
"grpc.request.deadline": "2006-01-02T15:04:05Z07:00", // string RFC3339 deadline of the current request if supplied
"grpc.request.value": "something", // string value on the request
"grpc.time_ms": 1.345, // float32 run time of the call in ms
"peer.address": {
"IP": "127.0.0.1", // string IP address of calling party
"Port": 60216, // int port call is coming in on
"Zone": "" // string peer zone for caller
},
"span.kind": "server", // string client | server
"system": "grpc" // string
"custom_field": "custom_value", // string user defined field
"custom_tags.int": 1337, // int user defined tag on the ctx
"custom_tags.string": "something", // string user defined tag on the ctx
}
*Payload Interceptor*
Below is a JSON formatted example of a log that would be logged by the payload interceptor:
{
"level": "info", // string zap log levels
"msg": "client request payload logged as grpc.request.content", // string log message
"grpc.request.content": { // object content of RPC request
"msg" : { // object ZAP specific inner object
"value": "something", // string defined by caller
"sleepTimeMs": 9999 // int defined by caller
}
},
"grpc.method": "Ping", // string method being called
"grpc.service": "mwitkow.testproto.TestService", // string service being called
"span.kind": "client", // string client | server
"system": "grpc" // string
}
Note - due to implementation ZAP differs from Logrus in the "grpc.request.content" object by having an inner "msg" object.
Please see examples and tests for examples of use.
Please see settable_test.go for canonical integration through "zaptest" with golang testing infrastructure.
▹ Example (Initialization)
▾ Example (Initialization)
Initialization shows a relatively complex initialization sequence.
Code:
opts := []grpc_zap.Option{
grpc_zap.WithLevels(customFunc),
}
grpc_zap.ReplaceGrpcLoggerV2(zapLogger)
_ = grpc.NewServer(
grpc_middleware.WithUnaryServerChain(
grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
grpc_zap.UnaryServerInterceptor(zapLogger, opts...),
),
grpc_middleware.WithStreamServerChain(
grpc_ctxtags.StreamServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
grpc_zap.StreamServerInterceptor(zapLogger, opts...),
),
)
▹ Example (InitializationWithDecider)
▾ Example (InitializationWithDecider)
Code:
opts := []grpc_zap.Option{
grpc_zap.WithDecider(func(fullMethodName string, err error) bool {
if err == nil && fullMethodName == "foo.bar.healthcheck" {
return false
}
return true
}),
}
_ = []grpc.ServerOption{
grpc_middleware.WithStreamServerChain(
grpc_ctxtags.StreamServerInterceptor(),
grpc_zap.StreamServerInterceptor(zap.NewNop(), opts...)),
grpc_middleware.WithUnaryServerChain(
grpc_ctxtags.UnaryServerInterceptor(),
grpc_zap.UnaryServerInterceptor(zap.NewNop(), opts...)),
}
▹ Example (InitializationWithDurationFieldOverride)
▾ Example (InitializationWithDurationFieldOverride)
Initialization shows an initialization sequence with the duration field generation overridden.
Code:
opts := []grpc_zap.Option{
grpc_zap.WithDurationField(func(duration time.Duration) zapcore.Field {
return zap.Int64("grpc.time_ns", duration.Nanoseconds())
}),
}
_ = grpc.NewServer(
grpc_middleware.WithUnaryServerChain(
grpc_ctxtags.UnaryServerInterceptor(),
grpc_zap.UnaryServerInterceptor(zapLogger, opts...),
),
grpc_middleware.WithStreamServerChain(
grpc_ctxtags.StreamServerInterceptor(),
grpc_zap.StreamServerInterceptor(zapLogger, opts...),
),
)
- Variables
- func AddFields(ctx context.Context, fields ...zapcore.Field)
- func DefaultClientCodeToLevel(code codes.Code) zapcore.Level
- func DefaultCodeToLevel(code codes.Code) zapcore.Level
- func DefaultMessageProducer(ctx context.Context, msg string, level zapcore.Level, code codes.Code, err error, duration zapcore.Field)
- func DurationToDurationField(duration time.Duration) zapcore.Field
- func DurationToTimeMillisField(duration time.Duration) zapcore.Field
- func Extract(ctx context.Context) *zap.Logger
- func PayloadStreamClientInterceptor(logger *zap.Logger, decider grpc_logging.ClientPayloadLoggingDecider) grpc.StreamClientInterceptor
- func PayloadStreamServerInterceptor(logger *zap.Logger, decider grpc_logging.ServerPayloadLoggingDecider) grpc.StreamServerInterceptor
- func PayloadUnaryClientInterceptor(logger *zap.Logger, decider grpc_logging.ClientPayloadLoggingDecider) grpc.UnaryClientInterceptor
- func PayloadUnaryServerInterceptor(logger *zap.Logger, decider grpc_logging.ServerPayloadLoggingDecider) grpc.UnaryServerInterceptor
- func ReplaceGrpcLogger(logger *zap.Logger)
- func ReplaceGrpcLoggerV2(logger *zap.Logger)
- func ReplaceGrpcLoggerV2WithVerbosity(logger *zap.Logger, verbosity int)
- func SetGrpcLoggerV2(settable grpc_logsettable.SettableLoggerV2, logger *zap.Logger)
- func SetGrpcLoggerV2WithVerbosity(settable grpc_logsettable.SettableLoggerV2, logger *zap.Logger, verbosity int)
- func StreamClientInterceptor(logger *zap.Logger, opts ...Option) grpc.StreamClientInterceptor
- func StreamServerInterceptor(logger *zap.Logger, opts ...Option) grpc.StreamServerInterceptor
- func UnaryClientInterceptor(logger *zap.Logger, opts ...Option) grpc.UnaryClientInterceptor
- func UnaryServerInterceptor(logger *zap.Logger, opts ...Option) grpc.UnaryServerInterceptor
- type CodeToLevel
- type DurationToField
- type MessageProducer
- type Option
- func WithCodes(f grpc_logging.ErrorToCode) Option
- func WithDecider(f grpc_logging.Decider) Option
- func WithDurationField(f DurationToField) Option
- func WithLevels(f CodeToLevel) Option
- func WithMessageProducer(f MessageProducer) Option
- func WithTimestampFormat(format string) Option
Package files
client_interceptors.go
context.go
doc.go
grpclogger.go
options.go
payload_interceptors.go
server_interceptors.go
Variables
var (
SystemField = zap.String("system", "grpc")
ServerField = zap.String("span.kind", "server")
)
var (
ClientField = zap.String("span.kind", "client")
)
DefaultDurationToField is the default implementation of converting request duration to a Zap field.
var DefaultDurationToField = DurationToTimeMillisField
var (
JsonPbMarshaller grpc_logging.JsonPbMarshaler = &jsonpb.Marshaler{}
)
func AddFields(ctx context.Context, fields ...zapcore.Field)
AddFields adds zap fields to the logger.
Deprecated: should use the ctxzap.AddFields instead
func DefaultClientCodeToLevel(code codes.Code) zapcore.Level
DefaultClientCodeToLevel is the default implementation of gRPC return codes to log levels for client side.
func DefaultCodeToLevel(code codes.Code) zapcore.Level
DefaultCodeToLevel is the default implementation of gRPC return codes and interceptor log level for server side.
func DefaultMessageProducer(ctx context.Context, msg string, level zapcore.Level, code codes.Code, err error, duration zapcore.Field)
DefaultMessageProducer writes the default message
func DurationToDurationField(duration time.Duration) zapcore.Field
DurationToDurationField uses a Duration field to log the request duration
and leaves it up to Zap's encoder settings to determine how that is output.
func DurationToTimeMillisField(duration time.Duration) zapcore.Field
DurationToTimeMillisField converts the duration to milliseconds and uses the key `grpc.time_ms`.
func Extract(ctx context.Context) *zap.Logger
Extract takes the call-scoped Logger from grpc_zap middleware.
Deprecated: should use the ctxzap.Extract instead
func PayloadStreamClientInterceptor(logger *zap.Logger, decider grpc_logging.ClientPayloadLoggingDecider) grpc.StreamClientInterceptor
PayloadStreamClientInterceptor returns a new streaming client interceptor that logs the payloads of requests and responses.
func PayloadStreamServerInterceptor(logger *zap.Logger, decider grpc_logging.ServerPayloadLoggingDecider) grpc.StreamServerInterceptor
PayloadStreamServerInterceptor returns a new server server interceptors that logs the payloads of requests.
This *only* works when placed *after* the `grpc_zap.StreamServerInterceptor`. However, the logging can be done to a
separate instance of the logger.
func PayloadUnaryClientInterceptor(logger *zap.Logger, decider grpc_logging.ClientPayloadLoggingDecider) grpc.UnaryClientInterceptor
PayloadUnaryClientInterceptor returns a new unary client interceptor that logs the payloads of requests and responses.
func PayloadUnaryServerInterceptor(logger *zap.Logger, decider grpc_logging.ServerPayloadLoggingDecider) grpc.UnaryServerInterceptor
PayloadUnaryServerInterceptor returns a new unary server interceptors that logs the payloads of requests.
This *only* works when placed *after* the `grpc_zap.UnaryServerInterceptor`. However, the logging can be done to a
separate instance of the logger.
func ReplaceGrpcLogger(logger *zap.Logger)
ReplaceGrpcLogger sets the given zap.Logger as a gRPC-level logger.
This should be called *before* any other initialization, preferably from init() functions.
Deprecated: use ReplaceGrpcLoggerV2.
func ReplaceGrpcLoggerV2(logger *zap.Logger)
ReplaceGrpcLoggerV2 replaces the grpclog.LoggerV2 with the provided logger.
It should be called before any gRPC functions. Logging verbosity defaults to info level.
To adjust gRPC logging verbosity, see ReplaceGrpcLoggerV2WithVerbosity.
func ReplaceGrpcLoggerV2WithVerbosity(logger *zap.Logger, verbosity int)
ReplaceGrpcLoggerV2WithVerbosity replaces the grpclog.Logger with the provided logger and verbosity.
It should be called before any gRPC functions.
verbosity correlates to grpclogs verbosity levels. A higher verbosity value results in less logging.
func SetGrpcLoggerV2(settable grpc_logsettable.SettableLoggerV2, logger *zap.Logger)
SetGrpcLoggerV2 replaces the grpc_log.Logger with the provided logger.
It can be used even when grpc infrastructure was initialized.
func SetGrpcLoggerV2WithVerbosity(settable grpc_logsettable.SettableLoggerV2, logger *zap.Logger, verbosity int)
SetGrpcLoggerV2WithVerbosity replaces the grpc_.LoggerV2 with the provided logger and verbosity.
It can be used even when grpc infrastructure was initialized.
func StreamClientInterceptor(logger *zap.Logger, opts ...Option) grpc.StreamClientInterceptor
StreamClientInterceptor returns a new streaming client interceptor that optionally logs the execution of external gRPC calls.
func StreamServerInterceptor(logger *zap.Logger, opts ...Option) grpc.StreamServerInterceptor
StreamServerInterceptor returns a new streaming server interceptor that adds zap.Logger to the context.
func UnaryClientInterceptor(logger *zap.Logger, opts ...Option) grpc.UnaryClientInterceptor
UnaryClientInterceptor returns a new unary client interceptor that optionally logs the execution of external gRPC calls.
func UnaryServerInterceptor(logger *zap.Logger, opts ...Option) grpc.UnaryServerInterceptor
UnaryServerInterceptor returns a new unary server interceptors that adds zap.Logger to the context.
CodeToLevel function defines the mapping between gRPC return codes and interceptor log level.
type CodeToLevel func(code codes.Code) zapcore.Level
DurationToField function defines how to produce duration fields for logging
type DurationToField func(duration time.Duration) zapcore.Field
MessageProducer produces a user defined log message
type MessageProducer func(ctx context.Context, msg string, level zapcore.Level, code codes.Code, err error, duration zapcore.Field)
type Option func(*options)
func WithCodes(f grpc_logging.ErrorToCode) Option
WithCodes customizes the function for mapping errors to error codes.
func WithDecider(f grpc_logging.Decider) Option
WithDecider customizes the function for deciding if the gRPC interceptor logs should log.
func WithDurationField(f DurationToField) Option
WithDurationField customizes the function for mapping request durations to Zap fields.
func WithLevels(f CodeToLevel) Option
WithLevels customizes the function for mapping gRPC return codes and interceptor log level statements.
func WithMessageProducer(f MessageProducer) Option
WithMessageProducer customizes the function for message formation.
func WithTimestampFormat(format string) Option
WithTimestampFormat customizes the timestamps emitted in the log fields.
Subdirectories
Name |
Synopsis |
.. |
ctxzap
|
`ctxzap` is a ctxlogger that is backed by Zap
|