logging_logrus
Server Interceptor的json格式
{
"level": "info", // string logrus 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.234, // 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的json格式
{
"level": "info", // string logrus log levels
"msg": "client request payload logged as grpc.request.content", // string log message
"grpc.request.content": { // object content of RPC request
"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
}
e1:Initialization
// Logrus entry is used, allowing pre-definition of certain fields by the user.
logrusEntry := logrus.NewEntry(logrusLogger)
// Shared options for the logger, with a custom gRPC code to log level function.
opts := []grpc_logrus.Option{
grpc_logrus.WithLevels(customFunc),
}
// Make sure that log statements internal to gRPC library are logged using the logrus Logger as well.
grpc_logrus.ReplaceGrpcLogger(logrusEntry)
// Create a server, make sure we put the grpc_ctxtags context before everything else.
_ = grpc.NewServer(
grpc_middleware.WithUnaryServerChain(
grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
grpc_logrus.UnaryServerInterceptor(logrusEntry, opts...),
),
grpc_middleware.WithStreamServerChain(
grpc_ctxtags.StreamServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)),
grpc_logrus.StreamServerInterceptor(logrusEntry, opts...),
),
)
e2:InitializationWithDurationFieldOverride
// Logrus entry is used, allowing pre-definition of certain fields by the user.
logrusEntry := logrus.NewEntry(logrusLogger)
// Shared options for the logger, with a custom duration to log field function.
opts := []grpc_logrus.Option{
grpc_logrus.WithDurationField(func(duration time.Duration) (key string, value interface{}) {
return "grpc.time_ns", duration.Nanoseconds()
}),
}
_ = grpc.NewServer(
grpc_middleware.WithUnaryServerChain(
grpc_ctxtags.UnaryServerInterceptor(),
grpc_logrus.UnaryServerInterceptor(logrusEntry, opts...),
),
grpc_middleware.WithStreamServerChain(
grpc_ctxtags.StreamServerInterceptor(),
grpc_logrus.StreamServerInterceptor(logrusEntry, opts...),
),
)
func ReplaceGrpcLogger(logger logrus.Entry) 替换grpclog,应该在初始化就调用
func StreamClientInterceptor(entry logrus.Entry, opts …Option) grpc.StreamClientInterceptor
func StreamServerInterceptor(entry logrus.Entry, opts …Option) grpc.StreamServerInterceptor
func UnaryClientInterceptor(entry logrus.Entry, opts …Option) grpc.UnaryClientInterceptor
func UnaryServerInterceptor(entry *logrus.Entry, opts …Option) grpc.UnaryServerInterceptor
func PayloadStreamClientInterceptor(entry *logrus.Entry, decider grpc_logging.ClientPayloadLoggingDecider) grpc.StreamClientInterceptor
func PayloadStreamServerInterceptor(entry *logrus.Entry, decider grpc_logging.ServerPayloadLoggingDecider) grpc.StreamServerInterceptor
func PayloadUnaryClientInterceptor(entry *logrus.Entry, decider grpc_logging.ClientPayloadLoggingDecider) grpc.UnaryClientInterceptor
func PayloadUnaryServerInterceptor(entry *logrus.Entry, decider grpc_logging.ServerPayloadLoggingDecider) grpc.UnaryServerInterceptor
type CodeToLevel
type CodeToLevel func(code codes.Code) logrus.Level
func DefaultClientCodeToLevel(code codes.Code) logrus.Level
func DefaultCodeToLevel(code codes.Code) logrus.Level
type DurationToField
type DurationToField func(duration time.Duration) (key string, value interface{})
type MessageProducer
自定义日志
type MessageProducer func(ctx context.Context, format string, level logrus.Level, code codes.Code, err error, fields logrus.Fields)
type Option
func WithCodes(f grpc_logging.ErrorToCode) Option
func WithLevels(f CodeToLevel) Option 将grpc映射为日志等级,用于自定义
func WithDurationField(f DurationToField) Option 记录请求耗时
func WithTimestampFormat(format string) Option 自定义时间格式
func WithMessageProducer(f MessageProducer) Option
func WithDecider(f grpc_logging.Decider) Option 决策器,判断是否需要进行日志记录
opts := []grpc_logrus.Option{
grpc_logrus.WithDecider(func(methodFullName string, err error) bool {
// will not log gRPC calls if it was a call to healthcheck and no error was raised
if err == nil && methodFullName == "blah.foo.healthcheck" {
return false
}
// by default you will log all calls
return true
}),
}
_ = []grpc.ServerOption{
grpc_middleware.WithStreamServerChain(
grpc_ctxtags.StreamServerInterceptor(),
grpc_logrus.StreamServerInterceptor(logrus.NewEntry(logrus.New()), opts...)),
grpc_middleware.WithUnaryServerChain(
grpc_ctxtags.UnaryServerInterceptor(),
grpc_logrus.UnaryServerInterceptor(logrus.NewEntry(logrus.New()), opts...)),
}
例子:
package main
import (
"context"
"errors"
"fmt"
"github.com/grpc-ecosystem/go-grpc-middleware"
"github.com/grpc-ecosystem/go-grpc-middleware/ratelimit"
juju "github.com/juju/ratelimit"
logrus1 "github.com/sirupsen/logrus"
"google.golang.org/grpc"
"gopkg.in/natefinch/lumberjack.v2"
"log"
"net"
pb "study/grpc/protobuf/person"
"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
"time"
)
const (
port = ":8080"
)
type Limiter struct{
Bucket *juju.Bucket
}
func (this *Limiter) Limit() bool {
token := this.Bucket.TakeAvailable(1)
fmt.Println("桶可用数量:",this.Bucket.Available())
if token == 0 {
return true
}
return false
}
func main() {
logs :=logrus1.New()
logs.Formatter = &logrus1.TextFormatter{TimestampFormat:"2006-01-02 15:04:05.999999999"}
logs.Level = logrus1.InfoLevel
logs.SetOutput(&lumberjack.Logger{
Filename: "./log/foo.log",
MaxSize: 1 , // megabytes
MaxBackups: 3,
MaxAge: 28, //days
Compress: true, // disabled by default
})
logEntry := logrus1.NewEntry(logs)
grpc_logrus.ReplaceGrpcLogger(logEntry)
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
limiter := &Limiter{juju.NewBucket(time.Second*2,2)}
opts := grpc_middleware.WithUnaryServerChain(
grpc_logrus.UnaryServerInterceptor(
logEntry,
grpc_logrus.WithLevels(grpc_logrus.DefaultCodeToLevel),
), // 日志
ratelimit.UnaryServerInterceptor(limiter), // 限速
)
s := grpc.NewServer(opts) //起一个服务,注意这一的参数,如果是一元转换过来的,只能填写一个
pb.RegisterPersonserverServer(s,&Server{})
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
logrus/ctxlogrus
func AddFields(ctx context.Context, fields logrus.Fields)
func Extract(ctx context.Context) *logrus.Entry
ctx := context.Background()
// setting tags will be added to the logger as log fields
grpc_ctxtags.Extract(ctx).Set("custom_tags.string", "something").Set("custom_tags.int", 1337)
// Extract a single request-scoped logrus.Logger and log messages.
l := ctxlogrus.Extract(ctx)
l.Info("some ping")
l.Info("another ping")
func ToContext(ctx context.Context, entry *logrus.Entry) context.Context