...

Source file src/github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/options.go

Documentation: github.com/grpc-ecosystem/go-grpc-middleware/logging/zap

     1  package grpc_zap
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	grpc_logging "github.com/grpc-ecosystem/go-grpc-middleware/logging"
     8  	"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap"
     9  	"go.uber.org/zap"
    10  	"go.uber.org/zap/zapcore"
    11  	"google.golang.org/grpc/codes"
    12  )
    13  
    14  var (
    15  	defaultOptions = &options{
    16  		levelFunc:       DefaultCodeToLevel,
    17  		shouldLog:       grpc_logging.DefaultDeciderMethod,
    18  		codeFunc:        grpc_logging.DefaultErrorToCode,
    19  		durationFunc:    DefaultDurationToField,
    20  		messageFunc:     DefaultMessageProducer,
    21  		timestampFormat: time.RFC3339,
    22  	}
    23  )
    24  
    25  type options struct {
    26  	levelFunc       CodeToLevel
    27  	shouldLog       grpc_logging.Decider
    28  	codeFunc        grpc_logging.ErrorToCode
    29  	durationFunc    DurationToField
    30  	messageFunc     MessageProducer
    31  	timestampFormat string
    32  }
    33  
    34  func evaluateServerOpt(opts []Option) *options {
    35  	optCopy := &options{}
    36  	*optCopy = *defaultOptions
    37  	optCopy.levelFunc = DefaultCodeToLevel
    38  	for _, o := range opts {
    39  		o(optCopy)
    40  	}
    41  	return optCopy
    42  }
    43  
    44  func evaluateClientOpt(opts []Option) *options {
    45  	optCopy := &options{}
    46  	*optCopy = *defaultOptions
    47  	optCopy.levelFunc = DefaultClientCodeToLevel
    48  	for _, o := range opts {
    49  		o(optCopy)
    50  	}
    51  	return optCopy
    52  }
    53  
    54  type Option func(*options)
    55  
    56  // CodeToLevel function defines the mapping between gRPC return codes and interceptor log level.
    57  type CodeToLevel func(code codes.Code) zapcore.Level
    58  
    59  // DurationToField function defines how to produce duration fields for logging
    60  type DurationToField func(duration time.Duration) zapcore.Field
    61  
    62  // WithDecider customizes the function for deciding if the gRPC interceptor logs should log.
    63  func WithDecider(f grpc_logging.Decider) Option {
    64  	return func(o *options) {
    65  		o.shouldLog = f
    66  	}
    67  }
    68  
    69  // WithLevels customizes the function for mapping gRPC return codes and interceptor log level statements.
    70  func WithLevels(f CodeToLevel) Option {
    71  	return func(o *options) {
    72  		o.levelFunc = f
    73  	}
    74  }
    75  
    76  // WithCodes customizes the function for mapping errors to error codes.
    77  func WithCodes(f grpc_logging.ErrorToCode) Option {
    78  	return func(o *options) {
    79  		o.codeFunc = f
    80  	}
    81  }
    82  
    83  // WithDurationField customizes the function for mapping request durations to Zap fields.
    84  func WithDurationField(f DurationToField) Option {
    85  	return func(o *options) {
    86  		o.durationFunc = f
    87  	}
    88  }
    89  
    90  // WithMessageProducer customizes the function for message formation.
    91  func WithMessageProducer(f MessageProducer) Option {
    92  	return func(o *options) {
    93  		o.messageFunc = f
    94  	}
    95  }
    96  
    97  // WithTimestampFormat customizes the timestamps emitted in the log fields.
    98  func WithTimestampFormat(format string) Option {
    99  	return func(o *options) {
   100  		o.timestampFormat = format
   101  	}
   102  }
   103  
   104  // DefaultCodeToLevel is the default implementation of gRPC return codes and interceptor log level for server side.
   105  func DefaultCodeToLevel(code codes.Code) zapcore.Level {
   106  	switch code {
   107  	case codes.OK:
   108  		return zap.InfoLevel
   109  	case codes.Canceled:
   110  		return zap.InfoLevel
   111  	case codes.Unknown:
   112  		return zap.ErrorLevel
   113  	case codes.InvalidArgument:
   114  		return zap.InfoLevel
   115  	case codes.DeadlineExceeded:
   116  		return zap.WarnLevel
   117  	case codes.NotFound:
   118  		return zap.InfoLevel
   119  	case codes.AlreadyExists:
   120  		return zap.InfoLevel
   121  	case codes.PermissionDenied:
   122  		return zap.WarnLevel
   123  	case codes.Unauthenticated:
   124  		return zap.InfoLevel // unauthenticated requests can happen
   125  	case codes.ResourceExhausted:
   126  		return zap.WarnLevel
   127  	case codes.FailedPrecondition:
   128  		return zap.WarnLevel
   129  	case codes.Aborted:
   130  		return zap.WarnLevel
   131  	case codes.OutOfRange:
   132  		return zap.WarnLevel
   133  	case codes.Unimplemented:
   134  		return zap.ErrorLevel
   135  	case codes.Internal:
   136  		return zap.ErrorLevel
   137  	case codes.Unavailable:
   138  		return zap.WarnLevel
   139  	case codes.DataLoss:
   140  		return zap.ErrorLevel
   141  	default:
   142  		return zap.ErrorLevel
   143  	}
   144  }
   145  
   146  // DefaultClientCodeToLevel is the default implementation of gRPC return codes to log levels for client side.
   147  func DefaultClientCodeToLevel(code codes.Code) zapcore.Level {
   148  	switch code {
   149  	case codes.OK:
   150  		return zap.DebugLevel
   151  	case codes.Canceled:
   152  		return zap.DebugLevel
   153  	case codes.Unknown:
   154  		return zap.InfoLevel
   155  	case codes.InvalidArgument:
   156  		return zap.DebugLevel
   157  	case codes.DeadlineExceeded:
   158  		return zap.InfoLevel
   159  	case codes.NotFound:
   160  		return zap.DebugLevel
   161  	case codes.AlreadyExists:
   162  		return zap.DebugLevel
   163  	case codes.PermissionDenied:
   164  		return zap.InfoLevel
   165  	case codes.Unauthenticated:
   166  		return zap.InfoLevel // unauthenticated requests can happen
   167  	case codes.ResourceExhausted:
   168  		return zap.DebugLevel
   169  	case codes.FailedPrecondition:
   170  		return zap.DebugLevel
   171  	case codes.Aborted:
   172  		return zap.DebugLevel
   173  	case codes.OutOfRange:
   174  		return zap.DebugLevel
   175  	case codes.Unimplemented:
   176  		return zap.WarnLevel
   177  	case codes.Internal:
   178  		return zap.WarnLevel
   179  	case codes.Unavailable:
   180  		return zap.WarnLevel
   181  	case codes.DataLoss:
   182  		return zap.WarnLevel
   183  	default:
   184  		return zap.InfoLevel
   185  	}
   186  }
   187  
   188  // DefaultDurationToField is the default implementation of converting request duration to a Zap field.
   189  var DefaultDurationToField = DurationToTimeMillisField
   190  
   191  // DurationToTimeMillisField converts the duration to milliseconds and uses the key `grpc.time_ms`.
   192  func DurationToTimeMillisField(duration time.Duration) zapcore.Field {
   193  	return zap.Float32("grpc.time_ms", durationToMilliseconds(duration))
   194  }
   195  
   196  // DurationToDurationField uses a Duration field to log the request duration
   197  // and leaves it up to Zap's encoder settings to determine how that is output.
   198  func DurationToDurationField(duration time.Duration) zapcore.Field {
   199  	return zap.Duration("grpc.duration", duration)
   200  }
   201  
   202  func durationToMilliseconds(duration time.Duration) float32 {
   203  	return float32(duration.Nanoseconds()/1000) / 1000
   204  }
   205  
   206  // MessageProducer produces a user defined log message
   207  type MessageProducer func(ctx context.Context, msg string, level zapcore.Level, code codes.Code, err error, duration zapcore.Field)
   208  
   209  // DefaultMessageProducer writes the default message
   210  func DefaultMessageProducer(ctx context.Context, msg string, level zapcore.Level, code codes.Code, err error, duration zapcore.Field) {
   211  	// re-extract logger from newCtx, as it may have extra fields that changed in the holder.
   212  	ctxzap.Extract(ctx).Check(level, msg).Write(
   213  		zap.Error(err),
   214  		zap.String("grpc.code", code.String()),
   215  		duration,
   216  	)
   217  }
   218  

View as plain text