介绍
zap日志库是一款高性能的开源日志库,提供了结构化日志记录和printf风格的日志记录
安装
go get -u go.uber.org/zap
如何在kratos框架中使用
参考官方文档中描述,为了方便业务自适配不同的 log 接入使用,Logger 只包含了最简单的 Log 接口。当业务需要在 Kratos 框架内部使用自定义的 log 的时候,只需要简单实现 Log 方法即可。
日志库较为公用建议放在kit基础库中方便其他微服务引用,可参考Go工程化最佳实践
实现log接口并配置zap日志库编码
package pkg
import (
"fmt"
"github.com/go-kratos/kratos/v2/log"
"github.com/natefinch/lumberjack"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
)
var _ log.Logger = (*ZapLogger)(nil)
type ZapLogger struct {
log *zap.Logger
Sync func() error
}
// Logger 配置zap日志,将zap日志库引入
func Logger() log.Logger {
//配置zap日志库的编码器
encoder := zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
MessageKey: "msg",
StacktraceKey: "stack",
EncodeTime: zapcore.ISO8601TimeEncoder,
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.CapitalLevelEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.FullCallerEncoder,
}
return NewZapLogger(
encoder,
zap.NewAtomicLevelAt(zapcore.DebugLevel),
zap.AddStacktrace(
zap.NewAtomicLevelAt(zapcore.ErrorLevel)),
zap.AddCaller(),
zap.AddCallerSkip(2),
zap.Development(),
)
}
// 日志自动切割,采用 lumberjack 实现的
func getLogWriter() zapcore.WriteSyncer {
lumberJackLogger := &lumberjack.Logger{
Filename: "../../zap.log",
MaxSize: 10, //日志的最大大小(M)
MaxBackups: 5, //日志的最大保存数量
MaxAge: 30, //日志文件存储最大天数
Compress: false, //是否执行压缩
}
return zapcore.AddSync(lumberJackLogger)
}
// NewZapLogger return a zap logger.
func NewZapLogger(encoder zapcore.EncoderConfig, level zap.AtomicLevel, opts ...zap.Option) *ZapLogger {
//日志切割
writeSyncer := getLogWriter()
//设置日志级别
level.SetLevel(zap.InfoLevel)
var core zapcore.Core
//开发模式下打印到标准输出
// --根据配置文件判断输出到控制台还是日志文件--
if conf.GetConfig().GetString("project.mode") == "dev" {
core = zapcore.NewCore(
zapcore.NewConsoleEncoder(encoder), // 编码器配置
zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout)), // 打印到控制台
level, // 日志级别
)
} else {
core = zapcore.NewCore(
zapcore.NewJSONEncoder(encoder), // 编码器配置
zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(writeSyncer)), // 打印到控制台和文件
level, // 日志级别
)
}
zapLogger := zap.New(core, opts...)
return &ZapLogger{log: zapLogger, Sync: zapLogger.Sync}
}
// Log 实现log接口
func (l *ZapLogger) Log(level log.Level, keyvals ...interface{}) error {
if len(keyvals) == 0 || len(keyvals)%2 != 0 {
l.log.Warn(fmt.Sprint("Keyvalues must appear in pairs: ", keyvals))
return nil
}
var data []zap.Field
for i := 0; i < len(keyvals); i += 2 {
data = append(data, zap.Any(fmt.Sprint(keyvals[i]), keyvals[i+1]))
}
switch level {
case log.LevelDebug:
l.log.Debug("", data...)
case log.LevelInfo:
l.log.Info("", data...)
case log.LevelWarn:
l.log.Warn("", data...)
case log.LevelError:
l.log.Error("", data...)
case log.LevelFatal:
l.log.Fatal("", data...)
}
return nil
}
替换为zap日志库
在main函数中将元日志替换为zap日志
app, cleanup, err := initApp(bc.Server, bc.Data, zaoLog.Logger())
添加日志中间件
在 grpc.ServerOption和http.ServerOption 中引入 logging.Server(), 则会在每次收到 gRPC 请求的时候打印详细请求信息。
var opts = []grpc.ServerOption{
grpc.Middleware(
recovery.Recovery(),
logging.Server(logger),//日志中间件
),
}
//=======================================
var opts = []http.ServerOption{
http.Middleware(
recovery.Recovery(),
logging.Server(logger),//日志中间件
),
}
在 grpc.WithMiddleware和http.WithMiddleware 中引入 logging.Client(), 则会在每次发起 grpc 请求的时候打印详细请求信息。
logger := log.DefaultLogger
conn, err := transgrpc.DialInsecure(
context.Background(),
grpc.WithEndpoint("127.0.0.1:9000"),
grpc.WithMiddleware(
logging.Client(logger),
),
)
//=======================================
logger := log.DefaultLogger
conn, err := http.NewClient(
context.Background(),
http.WithMiddleware(
logging.Client(logger),
),
http.WithEndpoint("127.0.0.1:8000"),
)