Go | zap

news/2025/2/23 3:46:28/

Go | zap

1. 简介

那些介绍、性能比较直接看参考中zap链接,这里只介绍该日志库用法,方便快速上手。

package mainimport ("go.uber.org/zap"
)func main() {logger, _ := zap.NewProduction()defer logger.Sync()logger.Info("hello world",zap.String("name", "yimt"),)// 可以将结构化日志转为fmt包形式sugar := logger.Sugar()sugar.Infof("name %s", "yimt")
}

输出

{"level":"info","ts":1684460836.884597,"caller":"hello-go/main.go:11","msg":"hello world","name":"yimt"}
{"level":"info","ts":1684460836.88468,"caller":"hello-go/main.go:17","msg":"name yimt"}

2. NewDevelopment & NewProduction区别

接下来通过查看NewDevelopmentNewProduction实现,简单了解下zap的配置,为后续学习zapcore.Core打下基础。

2.1. 实现

NewDevelopment

func NewDevelopment(options ...Option) (*Logger, error) {return NewDevelopmentConfig().Build(options...)
}
func NewDevelopmentConfig() Config {return Config{Level:            NewAtomicLevelAt(DebugLevel),Development:      true,Encoding:         "console",EncoderConfig:    NewDevelopmentEncoderConfig(),OutputPaths:      []string{"stderr"},ErrorOutputPaths: []string{"stderr"},}
}
func NewDevelopmentEncoderConfig() zapcore.EncoderConfig {return zapcore.EncoderConfig{// Keys can be anything except the empty string.TimeKey:        "T",LevelKey:       "L",NameKey:        "N",CallerKey:      "C",FunctionKey:    zapcore.OmitKey,MessageKey:     "M",StacktraceKey:  "S",LineEnding:     zapcore.DefaultLineEnding,EncodeLevel:    zapcore.CapitalLevelEncoder,EncodeTime:     zapcore.ISO8601TimeEncoder,EncodeDuration: zapcore.StringDurationEncoder,EncodeCaller:   zapcore.ShortCallerEncoder,}
}

NewProduction

func NewProduction(options ...Option) (*Logger, error) {return NewProductionConfig().Build(options...)
}
func NewProductionConfig() Config {return Config{Level:       NewAtomicLevelAt(InfoLevel),Development: false,Sampling: &SamplingConfig{Initial:    100,Thereafter: 100,},Encoding:         "json",EncoderConfig:    NewProductionEncoderConfig(),OutputPaths:      []string{"stderr"},ErrorOutputPaths: []string{"stderr"},}
}
func NewProductionEncoderConfig() zapcore.EncoderConfig {return zapcore.EncoderConfig{TimeKey:        "ts",LevelKey:       "level",NameKey:        "logger",CallerKey:      "caller",FunctionKey:    zapcore.OmitKey,MessageKey:     "msg",StacktraceKey:  "stacktrace",LineEnding:     zapcore.DefaultLineEnding,EncodeLevel:    zapcore.LowercaseLevelEncoder,EncodeTime:     zapcore.EpochTimeEncoder,EncodeDuration: zapcore.SecondsDurationEncoder,EncodeCaller:   zapcore.ShortCallerEncoder,}
}

2.2. 执行结果

NewDevelopment

func main() {logger, _ := zap.NewDevelopment()defer logger.Sync()logger.Info("hello world",zap.String("name", "yimt"),)
}

输出

2023-05-17T14:07:09.722+0800    INFO    hello-go/main.go:11     hello world     {"name": "yimt"}

NewProduction

func main() {logger, _ := zap.NewProduction()defer logger.Sync()logger.Info("hello world",zap.String("name", "yimt"),)
}

输出

{"level":"info","ts":1684303547.31098,"caller":"hello-go/main.go:11","msg":"hello world","name":"yimt"}

3. zapcore.Core接口

  • zapcore.NewCore(enc Encoder, ws WriteSyncer, enab LevelEnabler)
  • zapcore.NewNopCore()
  • zapcore.NewIncreaseLevelCore(core Core, level LevelEnabler) (Core, error)
  • zapcore.NewTee()

3.1. zapcore.NewCore(enc Encoder, ws WriteSyncer, enab LevelEnabler)

3.1.1. Encoder

  • zapcore.NewJSONEncoder(cfg EncoderConfig):以JSON形式编码。
  • zapcore.NewConsoleEncoder(cfg EncoderConfig):以console形式编码。

zapcore.EncoderConfig

主要用于配置日志编码,包括时间格式,日志键名,每条日志是否换行等。

  • zap.NewDevelopmentEncoderConfig()
  • zap.NewProductionEncoderConfig()
  • zapcore.EncoderConfig{}:可以自定义配置。

控制日志编码格式,正常控制台显示console形式编码,日志文件记录JSON形式编码。

3.1.2. zapcore.WriteSyncer

  • zapcore.AddSync(f):指定日志输出位置。

控制日志输出位置,主要有控制台和日志文件。

3.1.3. zapcore.LevelEnabler

  • zap.NewAtomicLevel():可以运行过程中线程安全的调节日志等级。
  • zap.LevelEnablerFunc:以回调形式控制日志等级。

用于控制日志等级。

3.1.4. 演示

package mainimport ("go.uber.org/zap""go.uber.org/zap/zapcore""os"
)func main() {core := zapcore.NewCore(zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()), zapcore.AddSync(os.Stdout), zap.NewAtomicLevel())logger := zap.New(core)defer logger.Sync()logger.Info("hello world")
}

控制台输出

{"level":"info","ts":1684459049.47701,"msg":"hello world"}

3.2. zapcore.NewNopCore()

package mainimport ("go.uber.org/zap""go.uber.org/zap/zapcore"
)func main() {logger := zap.New(zapcore.NewNopCore())defer logger.Sync()logger.Info("hello world")
}

执行完什么都不会发生,可以直接看NewNopCore()内实现,上面Core所有功能都不处理。

type nopCore struct{}// NewNopCore returns a no-op Core.
func NewNopCore() Core                                        { return nopCore{} }
func (nopCore) Enabled(Level) bool                            { return false }
func (n nopCore) With([]Field) Core                           { return n }
func (nopCore) Check(_ Entry, ce *CheckedEntry) *CheckedEntry { return ce }
func (nopCore) Write(Entry, []Field) error                    { return nil }
func (nopCore) Sync() error                                   { return nil }

3.3. zapcore.NewIncreaseLevelCore(core Core, level LevelEnabler) (Core, error)

实现

func NewIncreaseLevelCore(core Core, level LevelEnabler) (Core, error) {for l := _maxLevel; l >= _minLevel; l-- {if !core.Enabled(l) && level.Enabled(l) {return nil, fmt.Errorf("invalid increase level, as level %q is allowed by increased level, but not by existing core", l)}}return &levelFilterCore{core, level}, nil
}

直接在已有一个Core下降1个日志等级,以显示更多日志。

3.4. zapcore.NewTee()

上面示例都有一个问题,日志只能输出到一个Core。在有的时候我们需要日志以console形式输出到控制台,以json形式输出到日志文件时就用到了NewTee,可以将日志一次输入到多个Core处理。

package mainimport ("go.uber.org/zap""go.uber.org/zap/zapcore""os"
)func main() {f, err := os.Create("log.txt")if err != nil {panic(err)}// 日志已json形式输出文件outputFileCore := zapcore.NewCore(zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()), zapcore.AddSync(f), zap.NewAtomicLevel())// 日志已console形式输出到控制台outputConsoleCore := zapcore.NewCore(zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()), zapcore.AddSync(os.Stdout), zap.NewAtomicLevel())tee := zapcore.NewTee(outputFileCore, outputConsoleCore)logger := zap.New(tee)defer logger.Sync()logger.Info("hello world")
}

log.txt

{"level":"info","ts":1684459649.253505,"msg":"hello world"}

控制台

2023-05-19T09:27:29.253+0800    INFO    hello world

4. 输出日志文件问题

上面示例虽然可以输出日志文件,但是还存在以下问题。

  1. 单个日志文件大小无法限制,单个日志文件过大最直观的麻烦是我们用文件编辑器打开经常未响应。
  2. 日志文件不会自己删除,在生产服务器可能会存在大量日志文件,占用硬盘空间。应该有不少人在MySQL的binlog日志文件上踩过坑吧。
  3. 旧日志压缩存储,节约硬盘空间。

下面就介绍lumberjack库,帮助我们自己管理日志文件,直接看下面示例就可以了。

package mainimport ("go.uber.org/zap""go.uber.org/zap/zapcore""gopkg.in/natefinch/lumberjack.v2"
)func main() {writeSyncer := zapcore.AddSync(&lumberjack.Logger{Filename:   "log.txt",MaxSize:    500, // megabytesMaxBackups: 3,MaxAge:     28,   //daysCompress:   true, // disabled by default})core := zapcore.NewCore(zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()), writeSyncer, zap.NewAtomicLevel())logger := zap.New(core)defer logger.Sync()logger.Info("hello world")
}

5. 最终示例

功能

  1. 同时输出控制台和日志文件
  2. 日志文件达到限制大小自动创建新日志文件。
  3. 自己删除过期日志。
  4. 旧日志文件自动压缩。
package mainimport ("go.uber.org/zap""go.uber.org/zap/zapcore""gopkg.in/natefinch/lumberjack.v2""os"
)func main() {writeSyncer := zapcore.AddSync(&lumberjack.Logger{Filename:   "log.txt",MaxSize:    500, // megabytesMaxBackups: 3,MaxAge:     28,   //daysCompress:   true, // disabled by default})// 日志已json形式输出文件outputFileCore := zapcore.NewCore(zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()), writeSyncer, zap.NewAtomicLevel())// 日志已console形式输出到控制台outputConsoleCore := zapcore.NewCore(zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig()), zapcore.AddSync(os.Stdout), zap.NewAtomicLevel())tee := zapcore.NewTee(outputFileCore, outputConsoleCore)logger := zap.New(tee)defer logger.Sync()logger.Info("hello world")
}

6. 参考

  • zap
  • lumberjack

http://www.ppmy.cn/news/89099.html

相关文章

兼容性测试点和注意项,建议收藏

一:兼容性测试的概念:就是验证开发出来的程序在特定的运行环境中与特定的软件、硬件或数据相组合是否能正常运行、有无异常的测试过程。 二:兼容性测试的分类: (1)浏览器兼容性测试 指的是在浏览器上检查…

python爬虫之ajax网页抓取

在进行python爬虫时,我们经常会面对一些采用Ajax异步加载数据的网页,这种情况下,我们无法通过直接获取网页源代码来获取需要的数据。本文将介绍如何使用python爬虫抓取Ajax网页。 一、Ajax简介 Ajax全称为Asynchronous JavaScript and XML&…

基于RetinaNet和TensorFlow Object Detection API实现目标检测(附源码)

文章目录 一、RetinaNet原理二、RetinaNet实现1. tf.train.CheckPoint简介2. RetinaNet的TensorFlow源码 一、RetinaNet原理 待补充 二、RetinaNet实现 1. tf.train.CheckPoint简介 待补充 2. RetinaNet的TensorFlow源码 Step 1:安装Tensorflow 2 Object Detect…

JavaEE(系列10) -- 多线程案例3(定时器)

目录 1. 定时器 2. 标准库中的定时器 3. 实现定时器 3.1 创建带优先级的阻塞队列 3.2 创建MyTask类 3.3 构建schedule方法 3.4 构建timer类中的线程 3.5 思考 1. 定时器 定时器也是软件开发中的一个重要组件. 类似于一个 "闹钟". 达到一个设定的时间之后, 就执行某…

【GPT科技系列】国内开发者调用openAI-API科技方法

1. 前言 openAI上线7个月了,但是随着openAI的约束越来越多,国内开发者想要使用openai的接口实现开发简直就是难上加难。那真的就没有办法了吗?no no no,CF解决一切不开心~ 2.准备工作 我们需要一个国际域名 注册cloudflare账号 …

在Windows上安装Docker与k8s,完美亲测!

一、软件准备 1、去Docker官网下载Docker Desktop,并一键安装 2、下载k8s-for-docker-desktop包 git clone https://github.com/AliyunContainerService/k8s-for-docker-desktop.git二、镜像源配置 配置docker的国内镜像,国外的网络下载可能比较慢 doc…

PaLM 2重磅来袭,深挖谷歌92页技术报告亮点总结

谷歌CEO桑达尔・皮查伊(Sundar Pichai)亲切地将2023年称为是一个AI busy year,当地时间5月10日,谷歌IO大会上,谷歌大语言模型PaLM 2虽迟但到。作为一个“AI-first”公司,谷歌在Bard聊天机器人爆出事实性错误…

365天深度学习打卡 第P9周:YOLOv5的backbone实现

🍨 本文为🔗365天深度学习训练营中的学习记录博客🍖 原作者:K同学啊|接辅导、项目定制 文章目录 一、Backbone模块代码1.1 Conv模块1.2 C3模块Bottleneck模块SPPF模块 二、数据集和相关参数设置2.1 数据集操作2.2 相关参数设置2.3…