当前位置:网站首页>开源一夏 | GO语言框架中如何快速集成日志模块
开源一夏 | GO语言框架中如何快速集成日志模块
2022-08-02 09:38:00 【InfoQ】
前言
zap包的集成
简介
zap最基础的使用
package main
import (
"go.uber.org/zap"
"time"
)
func main() {
logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Info(" log info msg",
zap.String("name", "掘金"),
zap.Int("num", 3),
zap.Duration("timer", time.Minute),
)
}
{"level":"info","ts":1657600159.826612,"caller":"log/main.go:11","msg":" log info msg","name":"掘金","num":3,"timer":60}
{"level":"warn","ts":1657600159.8266969,"caller":"log/main.go:16","msg":" this is err msg","msg":"code err"}
定制化
// NewProduction builds a sensible production Logger that writes InfoLevel and
// above logs to standard error as JSON.
//
// It's a shortcut for NewProductionConfig().Build(...Option).
func NewProduction(options ...Option) (*Logger, error) {
return NewProductionConfig().Build(options...)
}
configbuildpackage main
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"time"
)
func main() {
conf := zap.NewProductionConfig()
// 可以把输出方式改为控制台编码, 更容易阅读
conf.Encoding = "console"
// 时间格式自定义
conf.EncoderConfig.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString("[" + t.Format("2006-01-02 15:04:05") + "]")
}
// 打印路径自定义
conf.EncoderConfig.EncodeCaller = func(caller zapcore.EntryCaller, encoder zapcore.PrimitiveArrayEncoder) {
encoder.AppendString("[" + caller.TrimmedPath() + "]")
}
// 级别显示自定义
conf.EncoderConfig.EncodeLevel = func(level zapcore.Level, encoder zapcore.PrimitiveArrayEncoder) {
encoder.AppendString("[" + level.String() + "]")
}
logger, _ := conf.Build()
logger.Info("service start")
logger.Info("info msg",
zap.String("name", "掘金"),
zap.Int("num", 3),
zap.Duration("timer", time.Minute),
)
}
[2022-07-12 14:57:18] [info] [log/main.go:28] service start
[2022-07-12 14:57:18] [info] [log/main.go:30] info msg {"name": "掘金", "num": 3, "timer": 60}
jsonjsonjsonzap// An EncoderConfig allows users to configure the concrete encoders supplied by
// zapcore.
type EncoderConfig struct {
// Set the keys used for each log entry. If any key is empty, that portion
// of the entry is omitted.
MessageKey string `json:"messageKey" yaml:"messageKey"`
LevelKey string `json:"levelKey" yaml:"levelKey"`
TimeKey string `json:"timeKey" yaml:"timeKey"`
NameKey string `json:"nameKey" yaml:"nameKey"`
CallerKey string `json:"callerKey" yaml:"callerKey"`
FunctionKey string `json:"functionKey" yaml:"functionKey"`
StacktraceKey string `json:"stacktraceKey" yaml:"stacktraceKey"`
LineEnding string `json:"lineEnding" yaml:"lineEnding"`
// Configure the primitive representations of common complex types. For
// example, some users may want all time.Times serialized as floating-point
// seconds since epoch, while others may prefer ISO8601 strings.
EncodeLevel LevelEncoder `json:"levelEncoder" yaml:"levelEncoder"`
EncodeTime TimeEncoder `json:"timeEncoder" yaml:"timeEncoder"`
EncodeDuration DurationEncoder `json:"durationEncoder" yaml:"durationEncoder"`
EncodeCaller CallerEncoder `json:"callerEncoder" yaml:"callerEncoder"`
// Unlike the other primitive type encoders, EncodeName is optional. The
// zero value falls back to FullNameEncoder.
EncodeName NameEncoder `json:"nameEncoder" yaml:"nameEncoder"`
// Configures the field separator used by the console encoder. Defaults
// to tab.
ConsoleSeparator string `json:"consoleSeparator" yaml:"consoleSeparator"`
}
进阶封装
jsonconsolezaplogindex.gopackage logging
import (
"go.uber.org/zap"
"go.uber.org/zap/buffer"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
"os"
"path"
"strings"
)
type (
Conf struct {
Path string // 日志路径
Encoder string // 编码器选择
}
logItem struct {
FileName string
Level zap.LevelEnablerFunc
}
Encoder interface {
Config() zapcore.Encoder
WithKey(key string) Encoder
WithField(key, val string) Encoder
Debug(msg string)
Debugf(format string, v ...interface{})
Info(msg string)
Infof(format string, v ...interface{})
Warn(msg string)
Warnf(format string, v ...interface{})
Error(msg string)
Errorf(format string, v ...interface{})
Fatal(msg string)
Fatalf(format string, v ...interface{})
}
)
var (
maxSize = 200 // 每个日志文件最大尺寸200M
maxBackups = 20 // 日志文件最多保存20个备份
maxAge = 30 // 保留最大天数
_logger *zap.Logger
_pool = buffer.NewPool()
c Conf
ConsoleEncoder = "console" // 控制台输出
JsonEncoder = "json" // json输出
)
// Init 初始化日志.
func Init(conf Conf) {
c = conf
prefix, suffix := getFileSuffixPrefix(c.Path)
infoPath := path.Join(prefix + ".info" + suffix)
errPath := path.Join(prefix + ".err" + suffix)
items := []logItem{
{
FileName: infoPath,
Level: func(level zapcore.Level) bool {
return level <= zap.InfoLevel
},
},
{
FileName: errPath,
Level: func(level zapcore.Level) bool {
return level > zap.InfoLevel
},
},
}
NewLogger(items)
}
// NewLogger 日志.
func NewLogger(items []logItem) {
var (
cfg zapcore.Encoder
cores []zapcore.Core
)
switch c.Encoder {
case JsonEncoder:
cfg = NewJsonLog().Config()
case ConsoleEncoder:
cfg = NewConsoleLog().Config()
default:
cfg = NewConsoleLog().Config()
}
for _, v := range items {
hook := lumberjack.Logger{
Filename: v.FileName,
MaxSize: maxSize, // 每个日志文件保存的最大尺寸 单位:M
MaxBackups: maxBackups, // 日志文件最多保存多少个备份
MaxAge: maxAge, // 文件最多保存多少天
Compress: true, // 是否压缩
LocalTime: true, // 备份文件名本地/UTC时间
}
core := zapcore.NewCore(
cfg, // 编码器配置;
zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(&hook)), // 打印到控制台和文件
v.Level, // 日志级别
)
cores = append(cores, core)
}
// 开启开发模式,堆栈跟踪
caller := zap.AddCaller()
// 开发模式
development := zap.Development()
// 二次封装
skip := zap.AddCallerSkip(1)
// 构造日志
_logger = zap.New(zapcore.NewTee(cores...), caller, development, skip)
return
}
// GetEncoder 获取自定义编码器.
func GetEncoder() Encoder {
switch c.Encoder {
case JsonEncoder:
return NewJsonLog()
case ConsoleEncoder:
return NewConsoleLog()
default:
return NewConsoleLog()
}
}
// GetLogger 获取日志记录器.
func GetLogger() *zap.Logger {
return _logger
}
// getFileSuffixPrefix 文件路径切割
func getFileSuffixPrefix(fileName string) (prefix, suffix string) {
paths, _ := path.Split(fileName)
base := path.Base(fileName)
suffix = path.Ext(fileName)
prefix = strings.TrimSuffix(base, suffix)
prefix = path.Join(paths, prefix)
return
}
// getFilePath 自定义获取文件路径.
func getFilePath(ec zapcore.EntryCaller) string {
if !ec.Defined {
return "undefined"
}
buf := _pool.Get()
buf.AppendString(ec.Function)
buf.AppendByte(':')
buf.AppendInt(int64(ec.Line))
caller := buf.String()
buf.Free()
return caller
}
console.gopackage logging
import (
"fmt"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"time"
)
type ConsoleLog struct {
val string
}
func NewConsoleLog() Encoder {
return new(ConsoleLog)
}
// Config 自定义配置.
func (slf *ConsoleLog) Config() zapcore.Encoder {
var (
cfg = zap.NewProductionEncoderConfig()
)
// 时间格式自定义
cfg.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString("[" + t.Format("2006-01-02 15:04:05") + "]")
}
// 打印路径自定义
cfg.EncodeCaller = func(caller zapcore.EntryCaller, encoder zapcore.PrimitiveArrayEncoder) {
encoder.AppendString("[" + getFilePath(caller) + "]")
}
// 级别显示自定义
cfg.EncodeLevel = func(level zapcore.Level, encoder zapcore.PrimitiveArrayEncoder) {
encoder.AppendString("[" + level.String() + "]")
}
return zapcore.NewConsoleEncoder(cfg)
}
// WithKey 添加单个键.
func (slf *ConsoleLog) WithKey(key string) Encoder {
slf.val = slf.val + "[" + key + "] "
return slf
}
// WithField 添加字段.
func (slf *ConsoleLog) WithField(key, val string) Encoder {
slf.val = slf.val + fmt.Sprintf("[%s:%s] ", key, val)
return slf
}
func (slf *ConsoleLog) Debug(msg string) {
_logger.Debug(slf.val + msg)
}
func (slf *ConsoleLog) Debugf(format string, v ...interface{}) {
_logger.Debug(fmt.Sprintf(slf.val+format, v...))
}
func (slf *ConsoleLog) Info(msg string) {
_logger.Info(slf.val + msg)
}
func (slf *ConsoleLog) Infof(format string, v ...interface{}) {
_logger.Info(fmt.Sprintf(slf.val+format, v...))
}
func (slf *ConsoleLog) Warn(msg string) {
_logger.Warn(slf.val + msg)
}
func (slf *ConsoleLog) Warnf(format string, v ...interface{}) {
_logger.Warn(fmt.Sprintf(slf.val+format, v...))
}
func (slf *ConsoleLog) Error(msg string) {
_logger.Error(slf.val + msg)
}
func (slf *ConsoleLog) Errorf(format string, v ...interface{}) {
_logger.Error(fmt.Sprintf(slf.val+format, v...))
}
func (slf *ConsoleLog) Fatal(msg string) {
_logger.Fatal(slf.val + msg)
}
func (slf *ConsoleLog) Fatalf(format string, v ...interface{}) {
_logger.Fatal(fmt.Sprintf(slf.val+format, v...))
}
json.gojsonpackage logging
import (
"fmt"
"go.uber.org/zap/zapcore"
"time"
"go.uber.org/zap"
)
type JsonLog struct {
fields []zap.Field
val string
}
// NewJsonLog 自定义添加log field.
func NewJsonLog() Encoder {
return &JsonLog{fields: make([]zap.Field, 0)}
}
// Config 自定义配置.
func (slf *JsonLog) Config() zapcore.Encoder {
var (
cfg = zap.NewProductionEncoderConfig()
)
// 时间格式自定义
cfg.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(t.Format("2006-01-02 15:04:05"))
}
// 打印路径自定义
cfg.EncodeCaller = func(caller zapcore.EntryCaller, encoder zapcore.PrimitiveArrayEncoder) {
encoder.AppendString(getFilePath(caller))
}
// 级别显示自定义
cfg.EncodeLevel = func(level zapcore.Level, encoder zapcore.PrimitiveArrayEncoder) {
encoder.AppendString(level.String())
}
return zapcore.NewJSONEncoder(cfg)
}
// WithKey 添加单个键.
func (slf *JsonLog) WithKey(key string) Encoder {
slf.val = slf.val + key + " "
return slf
}
// WithField 添加字段.
func (slf *JsonLog) WithField(key, val string) Encoder {
slf.fields = append(slf.fields, zap.String(key, val))
return slf
}
func (slf *JsonLog) Debug(msg string) {
_logger.Debug(slf.val+msg, slf.fields...)
}
func (slf *JsonLog) Debugf(format string, v ...interface{}) {
_logger.Debug(fmt.Sprintf(slf.val+format, v...), slf.fields...)
}
func (slf *JsonLog) Info(msg string) {
_logger.Info(slf.val+msg, slf.fields...)
}
func (slf *JsonLog) Infof(format string, v ...interface{}) {
_logger.Info(fmt.Sprintf(slf.val+format, v...), slf.fields...)
}
func (slf *JsonLog) Warn(msg string) {
_logger.Warn(slf.val+msg, slf.fields...)
}
func (slf *JsonLog) Warnf(format string, v ...interface{}) {
_logger.Warn(fmt.Sprintf(slf.val+format, v...), slf.fields...)
}
func (slf *JsonLog) Error(msg string) {
_logger.Error(slf.val+msg, slf.fields...)
}
func (slf *JsonLog) Errorf(format string, v ...interface{}) {
_logger.Error(fmt.Sprintf(slf.val+format, v...), slf.fields...)
}
func (slf *JsonLog) Fatal(msg string) {
_logger.Fatal(slf.val+msg, slf.fields...)
}
func (slf *JsonLog) Fatalf(format string, v ...interface{}) {
_logger.Fatal(fmt.Sprintf(slf.val+format, v...), slf.fields...)
}
service.gopackage logging
import (
"fmt"
)
func Sync() {
_ = _logger.Sync()
}
func Debug(msg string) {
_logger.Debug(msg)
}
func Debugf(format string, v ...interface{}) {
_logger.Debug(fmt.Sprintf(format, v...))
}
func Info(msg string) {
_logger.Info(msg)
}
func Infof(format string, v ...interface{}) {
_logger.Info(fmt.Sprintf(format, v...))
}
func Warn(msg string) {
_logger.Warn(msg)
}
func Warnf(format string, v ...interface{}) {
_logger.Warn(fmt.Sprintf(format, v...))
}
func Error(msg string) {
_logger.Error(msg)
}
func Errorf(format string, v ...interface{}) {
_logger.Error(fmt.Sprintf(format, v...))
}
func Fatal(msg string) {
_logger.Fatal(msg)
}
func Fatalf(format string, v ...interface{}) {
_logger.Fatal(fmt.Sprintf(format, v...))
}
Go边栏推荐
猜你喜欢

Spend 2 hours a day to make up for Tencent T8, play 688 pages of SSM framework and Redis, and successfully land on Meituan

Daily practice of dynamic programming (3)

干货|如何在海量文件系统中选择合适自己的文件系统

从零开始入门单片机(一):必会背景知识总结

瑞萨RZ/G2L处理器详细测评

HCIA静态路由综合练习

软件测试与质量 之白盒测试

Use the scrapy to climb to save data to mysql to prevent repetition

HCIA动态主机配置协议实验(dhcp)

In the whole development of chi V853 board tried to compile QT test
随机推荐
YugaByte adds Voyager migration service in its 2.15 database update
ABAP 和json转换的方法
第15章 泛型
百战RHCE(第四十七战:运维工程师必会技-Ansible学习2-Ansible安装配置练习环境)
The love-hate relationship between C language volatile keyword, inline assembly volatile and compiler
AutoJs学习-AES加解密
第十七章 Excel操作
function call to print lua internal structure
sql concat(),如何才能拼接表的名字
In the whole development of chi V853 board tried to compile QT test
初学者怎么快速学会SQL
SAP 云平台上一种 Low Code Development(低代码开发)解决方案
理解JS的三座大山
带你认识40G单纤双向光模块-QSFP+ BiDi光模块
享年94岁,图灵奖得主、计算复杂性理论先驱Juris Hartmanis逝世
打印lua内部结构的函数调用
用汇编实现爱心特效【七夕来袭】
【技术分享】OSPFv3基本原理
练习-17
被报表需求逼疯的银行数据人,是时候放弃用Excel做报表了