Skip to content

Instantly share code, notes, and snippets.

@jinleileiking
Created June 28, 2017 14:03
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jinleileiking/95b2efd12563e4abc8d0b8a888b58161 to your computer and use it in GitHub Desktop.
Save jinleileiking/95b2efd12563e4abc8d0b8a888b58161 to your computer and use it in GitHub Desktop.
gpc + middleware + zap + log rotate
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