Created
June 28, 2017 14:03
-
-
Save jinleileiking/95b2efd12563e4abc8d0b8a888b58161 to your computer and use it in GitHub Desktop.
gpc + middleware + zap + log rotate
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"log" | |
"net" | |
// "os" | |
// "path" | |
"os" | |
"path" | |
"go.uber.org/zap" | |
"go.uber.org/zap/zapcore" | |
pb "./pb" | |
"github.com/davecgh/go-spew/spew" | |
"github.com/grpc-ecosystem/go-grpc-middleware" | |
"github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" | |
"github.com/grpc-ecosystem/go-grpc-middleware/tags" | |
"github.com/robfig/cron" | |
"golang.org/x/net/context" | |
"google.golang.org/grpc" | |
"google.golang.org/grpc/metadata" | |
"google.golang.org/grpc/reflection" | |
) | |
import "gopkg.in/natefinch/lumberjack.v2" | |
const ( | |
port = ":9999" | |
) | |
// server is used to implement helloworld.GreeterServer. | |
type server struct{} | |
// SayHello implements helloworld.GreeterServer | |
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { | |
fmt.Println("got name:", in.Name) | |
grpc.SendHeader(ctx, metadata.New(map[string]string{ | |
"foo": "foo1", | |
"bar": "bar1", | |
})) | |
grpc.SetTrailer(ctx, metadata.New(map[string]string{ | |
"foo": "foo2", | |
"bar": "bar2", | |
})) | |
md, _ := metadata.FromIncomingContext(ctx) | |
spew.Dump(md) | |
type test struct { | |
Item1 string | |
Item2 int | |
} | |
t := test{ | |
Item1: "aaa", | |
Item2: 222, | |
} | |
grpc_ctxtags.Extract(ctx).Set("custom_tags.string", "something").Set("custom_tags.int", 1337) | |
grpc_ctxtags.Extract(ctx).Set("struct", t) | |
grpc_zap.Extract(ctx).Info("Failed to fetch URL.") // Structured context as loosely-typed key-value pairs. | |
grpc_zap.Extract(ctx).Debug("Failed to fetch URL.") // Structured context as loosely-typed key-value pairs. | |
grpc_zap.Extract(ctx).Warn("Failed to fetch URL.") // Structured context as loosely-typed key-value pairs. | |
// if header, ok := metadata.FromContext(stream.Context()); ok { | |
// spew.Dump(header) | |
// // if v, ok := header["error"]; ok { | |
// // stream.SetTrailer(metadata.New(map[string]string{ | |
// // "foo": "foo2", | |
// // "bar": "bar2", | |
// // })) | |
// // return status.Errorf(codes.InvalidArgument, "error metadata: %v", v) | |
// // } | |
// } | |
// return &pb.HelloReply{Message: "Hello " + strconv.Itoa(int(in.Name))}, nil | |
return &pb.HelloReply{Message: "Hello " + spew.Sdump(in.App)}, nil | |
} | |
func (s *server) SayHelloAgain(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { | |
// return &pb.HelloReply{Message: "Hello again" + in.Name}, nil | |
return &pb.HelloReply{Message: "Hello again"}, nil | |
} | |
func main() { | |
setupLogger() | |
zaplogger := DefaultZapLogger | |
// defer logger.Sync() // flushes buffer, if any | |
grpc_zap.ReplaceGrpcLogger(zaplogger) | |
// opts := []grpc_zap.Option{ | |
// grpc_zap.WithDurationField(func(duration time.Duration) zapcore.Field { | |
// return zap.Int64("grpc.time_ns", duration.Nanoseconds()) | |
// }), | |
// } | |
fmt.Println("running...") | |
lis, err := net.Listen("tcp", port) | |
if err != nil { | |
log.Fatalf("failed to listen: %v", err) | |
} | |
s := grpc.NewServer( | |
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( | |
grpc_ctxtags.UnaryServerInterceptor(grpc_ctxtags.WithFieldExtractor(grpc_ctxtags.CodeGenRequestFieldExtractor)), | |
// grpc_ctxtags.UnaryServerInterceptor(), | |
// grpc_opentracing.UnaryServerInterceptor(), | |
// grpc_prometheus.UnaryServerInterceptor, | |
// grpc_zap.UnaryServerInterceptor(zaplogger, opts...), | |
grpc_zap.UnaryServerInterceptor(zaplogger), | |
// grpc_auth.UnaryServerInterceptor(myAuthFunction), | |
// grpc_recovery.UnaryServerInterceptor(), | |
)), | |
) | |
pb.RegisterGreeterServer(s, &server{}) | |
// Register reflection service on gRPC server. | |
reflection.Register(s) | |
if err := s.Serve(lis); err != nil { | |
log.Fatalf("failed to serve: %v", err) | |
} | |
} | |
func setupLogger() { | |
config := Config{ | |
// EncodeLogsAsJson: false, | |
EncodeLogsAsJson: true, | |
FileLoggingEnabled: true, | |
Directory: "./logs", | |
Filename: "servant.log", | |
MaxAge: 30, | |
MaxSize: 1000, | |
} | |
Configure(config) | |
} | |
// Configuration for logging | |
type Config struct { | |
// EncodeLogsAsJson makes the log framework log JSON | |
EncodeLogsAsJson bool | |
// FileLoggingEnabled makes the framework log to a file | |
// the fields below can be skipped if this value is false! | |
FileLoggingEnabled bool | |
// Directory to log to to when filelogging is enabled | |
Directory string | |
// Filename is the name of the logfile which will be placed inside the directory | |
Filename string | |
// MaxSize the max size in MB of the logfile before it's rolled | |
MaxSize int | |
// MaxBackups the max number of rolled files to keep | |
MaxBackups int | |
// MaxAge the max age in days to keep a logfile | |
MaxAge int | |
} | |
var DefaultZapLogger = newZapLogger(false, os.Stdout) | |
func Configure(config Config) { | |
writers := []zapcore.WriteSyncer{os.Stdout} | |
if config.FileLoggingEnabled { | |
writers = append(writers, newRollingFile(config)) | |
} | |
DefaultZapLogger = newZapLogger(config.EncodeLogsAsJson, zapcore.NewMultiWriteSyncer(writers...)) | |
zap.RedirectStdLog(DefaultZapLogger) | |
Info("logging configured", | |
zap.Bool("fileLogging", config.FileLoggingEnabled), | |
zap.Bool("jsonLogOutput", config.EncodeLogsAsJson), | |
zap.String("logDirectory", config.Directory), | |
zap.String("fileName", config.Filename), | |
zap.Int("maxSizeMB", config.MaxSize), | |
zap.Int("maxBackups", config.MaxBackups), | |
zap.Int("maxAgeInDays", config.MaxAge)) | |
} | |
func newRollingFile(config Config) zapcore.WriteSyncer { | |
if err := os.MkdirAll(config.Directory, 0755); err != nil { | |
Error("failed create log directory", zap.Error(err), zap.String("path", config.Directory)) | |
return nil | |
} | |
lj_log := lumberjack.Logger{ | |
Filename: path.Join(config.Directory, config.Filename), | |
MaxSize: config.MaxSize, //megabytes | |
MaxAge: config.MaxAge, //days | |
MaxBackups: config.MaxBackups, //files | |
LocalTime: true, | |
} | |
c := cron.New() | |
// c.AddFunc("* * * * * *", func() { lj_log.Rotate() }) | |
c.AddFunc("@daily", func() { lj_log.Rotate() }) | |
c.Start() | |
return zapcore.AddSync(&lj_log) | |
} | |
func newZapLogger(encodeAsJSON bool, output zapcore.WriteSyncer) *zap.Logger { | |
encCfg := zapcore.EncoderConfig{ | |
TimeKey: "logtime", | |
LevelKey: "level", | |
NameKey: "logger", | |
CallerKey: "caller", | |
MessageKey: "msg", | |
StacktraceKey: "stacktrace", | |
EncodeLevel: zapcore.LowercaseLevelEncoder, | |
EncodeTime: zapcore.ISO8601TimeEncoder, | |
EncodeDuration: zapcore.NanosDurationEncoder, | |
EncodeCaller: zapcore.ShortCallerEncoder, | |
} | |
opts := []zap.Option{zap.AddCaller()} | |
opts = append(opts, zap.AddStacktrace(zap.WarnLevel)) | |
encoder := zapcore.NewConsoleEncoder(encCfg) | |
if encodeAsJSON { | |
encoder = zapcore.NewJSONEncoder(encCfg) | |
} | |
return zap.New(zapcore.NewCore(encoder, output, zap.NewAtomicLevelAt(zap.DebugLevel)), opts...) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment