138 lines
3.4 KiB
Go
138 lines
3.4 KiB
Go
package logging
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
|
|
"github.com/rs/zerolog"
|
|
"github.com/rs/zerolog/log"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/metadata"
|
|
"google.golang.org/grpc/status"
|
|
)
|
|
|
|
func InterceptorLogger() logging.Logger {
|
|
return logging.LoggerFunc(func(ctx context.Context, lvl logging.Level, msg string, fields ...any) {
|
|
l := zerolog.Ctx(ctx).With().Fields(fields).Logger()
|
|
|
|
switch lvl {
|
|
case logging.LevelDebug:
|
|
l.Debug().Msg(msg)
|
|
case logging.LevelInfo:
|
|
l.Info().Msg(msg)
|
|
case logging.LevelWarn:
|
|
l.Warn().Msg(msg)
|
|
case logging.LevelError:
|
|
l.Error().Msg(msg)
|
|
default:
|
|
l.Info().Msg(msg)
|
|
}
|
|
})
|
|
}
|
|
|
|
func LoggingOpts() []logging.Option {
|
|
return []logging.Option{
|
|
logging.WithLogOnEvents(logging.StartCall, logging.FinishCall),
|
|
logging.WithLevels(levelFunc),
|
|
logging.WithDurationField(func(d time.Duration) logging.Fields {
|
|
return logging.Fields{"grpc.duration_ms", d.Milliseconds()}
|
|
}),
|
|
}
|
|
}
|
|
|
|
func levelFunc(code codes.Code) logging.Level {
|
|
switch code {
|
|
case codes.OK, codes.NotFound, codes.Canceled:
|
|
return logging.LevelInfo
|
|
case codes.InvalidArgument, codes.AlreadyExists, codes.Unauthenticated:
|
|
return logging.LevelWarn
|
|
default:
|
|
return logging.LevelError
|
|
}
|
|
}
|
|
|
|
func RequestIDInterceptor() grpc.UnaryServerInterceptor {
|
|
return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
|
|
requestID := uuid.New().String()
|
|
|
|
logger := log.Logger.With().
|
|
Str("request_id", requestID).
|
|
Str("grpc.method", info.FullMethod).
|
|
Logger()
|
|
|
|
ctx = logger.WithContext(ctx)
|
|
|
|
resp, err := handler(ctx, req)
|
|
if err != nil {
|
|
st, _ := status.FromError(err)
|
|
zerolog.Ctx(ctx).Debug().
|
|
Str("grpc.code", st.Code().String()).
|
|
Str("grpc.error", st.Message()).
|
|
Msg("request failed")
|
|
}
|
|
|
|
return resp, err
|
|
}
|
|
}
|
|
|
|
func PeerInfoInterceptor() grpc.UnaryServerInterceptor {
|
|
return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
|
|
md, ok := metadata.FromIncomingContext(ctx)
|
|
if ok {
|
|
evt := zerolog.Ctx(ctx).With()
|
|
if ua := md.Get("user-agent"); len(ua) > 0 {
|
|
evt = evt.Str("peer.user_agent", ua[0])
|
|
}
|
|
if auth := md.Get("authorization"); len(auth) > 0 {
|
|
evt = evt.Bool("peer.authenticated", true)
|
|
}
|
|
logger := evt.Logger()
|
|
ctx = logger.WithContext(ctx)
|
|
}
|
|
|
|
return handler(ctx, req)
|
|
}
|
|
}
|
|
|
|
func StreamRequestIDInterceptor() grpc.StreamServerInterceptor {
|
|
return func(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
|
requestID := uuid.New().String()
|
|
|
|
logger := log.Logger.With().
|
|
Str("request_id", requestID).
|
|
Str("grpc.method", info.FullMethod).
|
|
Logger()
|
|
|
|
ctx := logger.WithContext(ss.Context())
|
|
wrapped := &wrappedStream{ServerStream: ss, ctx: ctx}
|
|
|
|
return handler(srv, wrapped)
|
|
}
|
|
}
|
|
|
|
type wrappedStream struct {
|
|
grpc.ServerStream
|
|
ctx context.Context
|
|
}
|
|
|
|
func (w *wrappedStream) Context() context.Context {
|
|
return w.ctx
|
|
}
|
|
|
|
func (w *wrappedStream) SendMsg(m any) error {
|
|
zerolog.Ctx(w.ctx).Trace().Str("direction", "send").Msg(fmt.Sprintf("stream message: %T", m))
|
|
return w.ServerStream.SendMsg(m)
|
|
}
|
|
|
|
func (w *wrappedStream) RecvMsg(m any) error {
|
|
err := w.ServerStream.RecvMsg(m)
|
|
if err == nil {
|
|
zerolog.Ctx(w.ctx).Trace().Str("direction", "recv").Msg(fmt.Sprintf("stream message: %T", m))
|
|
}
|
|
return err
|
|
}
|