kratos微服务框架引入zap日志库

介绍

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"),
)
上一篇:接口安全测试基础


下一篇:在Go语言项目中使用Zap日志库