go.uber.org/zap日志库

go.uber.org/zap日志库

注:本文是以go.uber.org/zap v1.21.0进行讲解

1.GO SDK里Logger优缺点

优势:
它最大的优点是使用非常简单,我们可以设置任何io.Writer作为日志记录输出并向其发送要写入的日志

劣势:
1.仅限基本的日志级别
只有一个Print选项。不支持INFO/DEBUG等多个级别
2.对于错误日志,它有Fatal和Panic
(1)Fatal日志通过调用os.Exit(1)来结束程序
(2)Panic日志在写入日志消息之后抛出一个panic
(3)但是它缺少一个ERROR日志级别,这个级别可以在不抛出panic或退出程序的情况下记录错误
3.缺乏日志格式化的能力——例如记录调用者的函数名和行号,格式化日期和时间格式,等等
4.不提供日志切割的能力

2.介绍Uber-go zap

在许多Go语言项目中,我们需要一个好的日志记录器能够提供下面这些功能:
能够将事件记录到文件中,而不是应用程序控制台
日志切割-能够根据文件大小、时间或间隔等来切割日志文件
支持不同的日志级别。例如INFO,DEBUG,ERROR等
能够打印基本信息,如调用文件/函数名和行号,日志时间等
它同时提供了结构化日志记录和printf风格的日志记录,它非常的快
根据Uber-go Zap的文档,它的性能比类似的结构化日志包更好——也比标准库更快

3.安装zap依赖

go get -u go.uber.org/zap

image-20220301145202688

4.zap.NewProductionEncoderConfig函数

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,
	}
}

5.zap.Field类型

type Field = zapcore.Field

6.zapcore.Field结构体

type Field struct {
	Key       string
	Type      FieldType
	Integer   int64
	String    string
	Interface interface{}
}

image-20220301151919143

7.zap.String函数

func String(key string, val string) Field {
	return Field{Key: key, Type: zapcore.StringType, String: val}
}

8.zap.Error函数

func Error(err error) Field {
	return NamedError("error", err)
}

9.zap.NamedError函数

func NamedError(key string, err error) Field {
	if err == nil {
		return Skip()
	}
	return Field{Key: key, Type: zapcore.ErrorType, Interface: err}
}

10.zap.NewProduction函数

//用于创建Logger;相当于一个预置来创建logger
func NewProduction(options ...Option) (*Logger, error) {
	return NewProductionConfig().Build(options...)
}

11.zap.New函数

在许多Go语言项目中,我们需要一个好的日志记录器能够提供下面这些功能:
能够将事件记录到文件中,而不是应用程序控制台
日志切割-能够根据文件大小、时间或间隔等来切割日志文件
支持不同的日志级别。例如INFO,DEBUG,ERROR等
能够打印基本信息,如调用文件/函数名和行号,日志时间等
它同时提供了结构化日志记录和printf风格的日志记录,它非常的快
根据Uber-go Zap的文档,它的性能比类似的结构化日志包更好——也比标准库更快

12.zap.Logger结构体

12.1结构体定义

//也叫日志记录器
type Logger struct {
	core zapcore.Core

	development bool
	addCaller   bool
	onFatal     zapcore.CheckWriteAction // default is WriteThenFatal

	name        string
	errorOutput zapcore.WriteSyncer

	addStack zapcore.LevelEnabler

	callerSkip int

	clock zapcore.Clock
}

12.2方法介绍

使用Info/ Error等Logger方法记录消息
日志记录器方法的语法是这样的:
func (log *Logger) MethodXXX(msg string, fields …Field)
其中MethodXXX是一个可变参数函数,可以是Info / Error/ Debug / Panic等。每个方法都接受一个消息字符串和任意数量的zapcore.Field场参数
每个zapcore.Field其实就是一组键值对参数。

12.3Error方法

func (log *Logger) Error(msg string, fields ...Field) {}

12.4Info方法

func (log *Logger) Info(msg string, fields ...Field) {}

12.5Debug方法

func (log *Logger) Panic(msg string, fields ...Field) {}

12.6Panic方法

func (log *Logger) Panic(msg string, fields ...Field) {
	if ce := log.check(PanicLevel, msg); ce != nil {
		ce.Write(fields...)
	}
}

12.7Fatal方法

func (log *Logger) Fatal(msg string, fields ...Field) {}

12.8Sync方法

func (log *Logger) Sync() error {}

12.9Sugar方法

func (log *Logger) Sugar() *SugaredLogger {
	core := log.clone()
	core.callerSkip += 2
	return &SugaredLogger{core}
}

13.zap.SugaredLogger结构体

13.1结构体介绍
type SugaredLogger struct {
	base *Logger
}
13.2Sync方法
func (s *SugaredLogger) Sync() error {
	return s.base.Sync()
}
13.3Debugf方法
func (s *SugaredLogger) Debugf(template string, args ...interface{}) {}
14.4Panic方法
func (s *SugaredLogger) Panic(args ...interface{}) {
	s.log(PanicLevel, "", args, nil)
}
14.5Errorf方法
func (s *SugaredLogger) Errorf(template string, args ...interface{}) {
	s.log(ErrorLevel, template, args, nil)
}
14.6Infof方法
func (s *SugaredLogger) Infof(template string, args ...interface{}) {
	s.log(InfoLevel, template, args, nil)
}

14.zapcore.AddSync函数

func AddSync(w io.Writer) WriteSyncer {}

15.zapcore.NewJSONEncoder函数

func NewJSONEncoder(cfg EncoderConfig) Encoder {
	return newJSONEncoder(cfg, false)
}

16.zapcore.Encoder接口17zapcore.WriteSyncer接口

//指定日志将写到哪里去
type WriteSyncer interface {
   io.Writer
   Sync() error
}

18.zapcore.NewCore函数

func NewCore(enc Encoder, ws WriteSyncer, enab LevelEnabler) Core {}

.Log Level:哪种级别的日志将被写入

19.zapcore.LevelEnabler接口

type LevelEnabler interface {
	Enabled(Level) bool
}

20.SugaredLogger和Logger使用场景

Zap提供了两种类型的日志记录器—Sugared Logger和Logger:
1.在性能很好但不是很关键的上下文中,使用SugaredLogger。它比其他结构化日志记录包快4-10倍,并且支持结构化和printf风格的日志记录。

2.在每一微秒和每一次内存分配都很重要的上下文中,使用Logger。它甚至比SugaredLogger更快,内存分配次数也更少,但它只支持强类型的结构化日志记录

21.创建Logger输出案例

image-20220301165030695

package main

import (
	"go.uber.org/zap"
	"net/http"
)

var logger *zap.Logger

func main() {
	InitLogger()
	defer logger.Sync()
	simpleHttpGet("www.google.com")
	simpleHttpGet("http://www.google.com")
}

func InitLogger() {
	//func NewProduction(options ...Option) (*Logger, error) {
	logger, _ = zap.NewProduction()
}

//我们将建立一个到URL的HTTP连接,并将状态代码/错误记录到日志文件中
func simpleHttpGet(url string) {
	resp, err := http.Get(url)
	if err != nil {
		logger.Error(
			"Error fetching url..",
			zap.String("url", url),
			zap.Error(err))
	} else {
		logger.Info("Success..",
			zap.String("statusCode", resp.Status),
			zap.String("url", url))
		resp.Body.Close()
	}
}
/*输出以下:
{"level":"error","ts":1646124280.6289413,"caller":"demo/main.go:26","msg":"Error fetching url..","url":"www.baidu.com","error":"Get \"www.baidu.com\": unsupported protocol scheme \"\"","stacktrace":"main.simpleHttpGet\n\tD:/all proj
ect/go/demo/main.go:26\nmain.main\n\tD:/all project/go/demo/main.go:13\nruntime.main\n\tD:/CodeSoftware/go/src/runtime/proc.go:255"}
{"level":"info","ts":1646124281.0494952,"caller":"demo/main.go:31","msg":"Success..","statusCode":"200 OK","url":"http://www.baidu.com"}

*/

打印输出JSON结构格式

22.创建SugaredLogger输出案例

image-20220301164216862

package main

import (
	"go.uber.org/zap"
	"net/http"
)

var sugarLogger *zap.SugaredLogger

func main() {
	InitLogger()
	defer sugarLogger.Sync()
	simpleHttpGet("www.baidu.com")
	simpleHttpGet("http://www.baidu.com")
}

func InitLogger() {
	logger, _ := zap.NewProduction()
	sugarLogger = logger.Sugar()
}

func simpleHttpGet(url string) {
	sugarLogger.Debugf("Trying to hit GET request for %s", url)
	resp, err := http.Get(url)
	if err != nil {
		sugarLogger.Errorf("Error fetching URL %s : Error = %s", url, err)
	} else {
		sugarLogger.Infof("Success! statusCode = %s for URL %s", resp.Status, url)
		resp.Body.Close()
	}
}
/*输出:

{"level":"error","ts":1646124478.833374,"caller":"demo/main.go:26","msg":"Error fetching URL www.baidu.com : Error = Get \"www.baidu.com\": unsupported protocol scheme \"\"","stacktrace":"main.simpleHttpGet\n\tD:/all project/go/demo
/main.go:26\nmain.main\n\tD:/all project/go/demo/main.go:13\nruntime.main\n\tD:/CodeSoftware/go/src/runtime/proc.go:255"}
{"level":"info","ts":1646124479.975356,"caller":"demo/main.go:28","msg":"Success! statusCode = 200 OK for URL http://www.baidu.com"}

*/

打印输出JSON结构格式

image-20220301164947180

23.自定义Logger将文件输入到终端

//自定义将文件输入到终端而不是控制台
package main

import (
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"net/http"
	"os"
)

var sugarLogger *zap.SugaredLogger

func getEncoder() zapcore.Encoder {
	//Encoder是编码器;
	/*我们希望将编码器从JSON Encoder更改为普通Encoder;
	为此,我们需要将NewJSONEncoder()更改为NewConsoleEncoder()*/
	return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
}

func getLogWriter() zapcore.WriteSyncer {
	//用于创建文件;返回的是*File
	file, _ := os.Create("./test.log")
	//指定日志将写到哪里去;我们使用zapcore.AddSync()函数并且将打开的文件句柄传进去。
	return zapcore.AddSync(file)
}

func InitLogger() {
	writeSyncer := getLogWriter()
	encoder := getEncoder()
	core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)

	logger := zap.New(core)
	sugarLogger = logger.Sugar()
}

func simpleHttpGet(url string) {
	sugarLogger.Debugf("Trying to hit GET request for %s", url)
	resp, err := http.Get(url)
	if err != nil {
		sugarLogger.Errorf("Error fetching URL %s : Error = %s", url, err)
	} else {
		sugarLogger.Infof("Success! statusCode = %s for URL %s", resp.Status, url)
		resp.Body.Close()
	}
}

func main() {
	InitLogger()
	defer sugarLogger.Sync()
	simpleHttpGet("www.baidu.com")
	simpleHttpGet("http://www.baidu.com")
}
//控制台不再会有输出

image-20220301160017773

image-20220301165707787

24.自定义Logger更改时间编码并添加调用者详细信息

鉴于我们对配置所做的更改,有下面两个问题:
时间是以非人类可读的方式展示,例如1.572161051846623e+09
调用方函数的详细信息没有显示在日志中

//自定义将文件输入到终端而不是控制台
package main

import (
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"net/http"
	"os"
)

var sugarLogger *zap.SugaredLogger

/*
我们要做的第一件事是覆盖默认的ProductionConfig(),并进行以下更改:
修改时间编码器
在日志文件中使用大写字母记录日志级别
*/
func getEncoder() zapcore.Encoder {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
	return zapcore.NewConsoleEncoder(encoderConfig)
}

func getLogWriter() zapcore.WriteSyncer {
	//用于创建文件;返回的是*File
	file, _ := os.Create("./test.log")
	//指定日志将写到哪里去;我们使用zapcore.AddSync()函数并且将打开的文件句柄传进去。
	return zapcore.AddSync(file)
}

func InitLogger() {
	writeSyncer := getLogWriter()
	encoder := getEncoder()
	core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
	//接下来,我们将修改zap logger代码,添加将调用函数信息记录到日志中的功能。为此,我们将在zap.New(..)函数中添加一个Option
	logger := zap.New(core, zap.AddCaller())
	sugarLogger = logger.Sugar()
}

func simpleHttpGet(url string) {
	sugarLogger.Debugf("Trying to hit GET request for %s", url)
	resp, err := http.Get(url)
	if err != nil {
		sugarLogger.Errorf("Error fetching URL %s : Error = %s", url, err)
	} else {
		sugarLogger.Infof("Success! statusCode = %s for URL %s", resp.Status, url)
		resp.Body.Close()
	}
}

func main() {
	InitLogger()
	defer sugarLogger.Sync()
	simpleHttpGet("www.baidu.com")
	simpleHttpGet("http://www.baidu.com")
}

image-20220301165556897

image-20220301163751011

25.Lumberjack介绍

image-20220301163646853

26.安装Lumberjack依赖

go get -u github.com/natefinch/lumberjack

image-20220301163701118

27.lumberjack.Logger结构体

type Logger struct {
	Filename string `json:"filename" yaml:"filename"`   //日志文件的位置
    MaxSize int `json:"maxsize" yaml:"maxsize"`  //在进行切割之前,日志的最大大小(以MB为单位)
	MaxAge int `json:"maxage" yaml:"maxage"` //保留旧文件的最大个数
	MaxBackups int `json:"maxbackups" yaml:"maxbackups"`  //保留就文件的最大天数
	LocalTime bool `json:"localtime" yaml:"localtime"`  //
	Compress bool `json:"compress" yaml:"compress"`   //是否压缩/归档旧文件
	size int64
	file *os.File
	mu   sync.Mutex
	millCh    chan bool
	startMill sync.Once
}

28.自定义Logger使用Lumberjack进行日志切割归档

image-20220303152357790

package main

import (
	"net/http"

	"github.com/natefinch/lumberjack"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

var sugarLogger *zap.SugaredLogger

func main() {
	InitLogger()
	defer sugarLogger.Sync()
	simpleHttpGet("www.baidu.com")
	simpleHttpGet("http://www.baidu.com")
}

func InitLogger() {
	writeSyncer := getLogWriter()
	encoder := getEncoder()
	core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)

	logger := zap.New(core, zap.AddCaller())
	sugarLogger = logger.Sugar()
}

func getEncoder() zapcore.Encoder {
	encoderConfig := zap.NewProductionEncoderConfig()
	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
	return zapcore.NewConsoleEncoder(encoderConfig)
}

func getLogWriter() zapcore.WriteSyncer {
	lumberJackLogger := &lumberjack.Logger{
		Filename:   "./test.log",
		MaxSize:    1,
		MaxBackups: 5,
		MaxAge:     30,
		Compress:   false,
	}
	return zapcore.AddSync(lumberJackLogger)
}

func simpleHttpGet(url string) {
	sugarLogger.Debugf("Trying to hit GET request for %s", url)
	resp, err := http.Get(url)
	if err != nil {
		sugarLogger.Errorf("Error fetching URL %s : Error = %s", url, err)
	} else {
		sugarLogger.Infof("Success! statusCode = %s for URL %s", resp.Status, url)
		resp.Body.Close()
	}
}

image-20220301190612350

image-20220301190634862

image-20220301190659200

image-20220301190722465

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GoGo在努力

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值