Goland中使用zap日志库
运行下面的命令安装zap
go get -u go.uber.org/zap
一 简单的logger实例
通过调用
zap.NewProduction()
/zap.NewDevelopment()
或者zap.Example()
创建一个Logger在性能很好但不是很关键的上下文中,使用
SugaredLogger
。它比其他结构化日志记录包快4-10倍,并且支持结构化和printf风格的日志记录。在每一微秒和每一次内存分配都很重要的上下文中,使用
Logger
。它甚至比SugaredLogger
更快,内存分配次数也更少,但它只支持强类型的结构化日志记录。
package main
import (
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// 两种类型的日志记录器sugarlogger 和logger
var sugarlogger *zap.SugaredLogger
var logger *zap.Logger
func main() {
InitLogger()
defer sugarlogger.Sync()
r := gin.Default()
r.GET("/sugerlogger", func(c *gin.Context) {
sugarlogger.Info("sugerlogger:访问了sugerlogger")
logger.Info("logger:访问了sugerlogger")
})
_ = r.Run(":8888")
}
func InitLogger() {
// zap.NewExample() 用于测试
//{"level":"info","msg":"sugerlogger:访问了sugerlogger"}
//zap.NewProduction() 用于生产环境
//{"level":"info","ts":"2021-09-22T20:17:15.979+0800","caller":"zap_log_demo/SugaredLoggerDemo.go:16","msg":"sugerlogger:访问了sugerlogger"}
//zap.NewDevelopment() 用户开发环境
//2021-09-22T20:17:53.011+0800 INFO zap_log_demo/SugaredLoggerDemo.go:16 sugerlogger:访问了sugerlogger
logger, _ = zap.NewDevelopment()
sugarlogger = logger.Sugar()
}
二 、定制logger
将日志写入文件而不是终端
我们将使用 zap.new()方法配置
package main
import (
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
)
var sugarlogger *zap.SugaredLogger
//日志格式
func getEncoder() zapcore.Encoder {
// 以下两种都是EncoderConfig类型 可以使用源码中封装的 也可以自定义
// zap.NewProductionEncoderConfig()
// zap.NewDevelopmentEncoderConfig()
// return zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
//自定义 我们可以修改里面的key和value值实现日志的定制
encodingConfig := 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.ISO8601TimeEncoder, //时间格式更改
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}
return zapcore.NewJSONEncoder(encodingConfig)
}
// 日志写到哪里
func getWriteSyncer() zapcore.WriteSyncer {
file, _ := os.OpenFile("path", os.O_CREATE|os.O_APPEND, 666)
return zapcore.AddSync(file)
}
func InitLogger() {
// zapcore.Core需要三个配置——Encoder,WriteSyncer,LogLevel
encoder := getEncoder()
writerSync := getWriteSyncer()
core := zapcore.NewCore(encoder, writerSync, zapcore.DebugLevel)
//logger := zap.New(core)
//添加AddCaller方法可以查看那个文件那一行调用了该日志
//{"level":"info","ts":"2021-09-22T22:26:03.874+0800","caller":"go_zap_new/zap定制logger.go:55","msg":"Success...状态码200"}
logger := zap.New(core,zap.AddCaller())
sugarlogger = logger.Sugar()
}
func main() {
InitLogger()
defer sugarlogger.Sync()
r := gin.Default()
r.GET("/sugerlogger", func(c *gin.Context) {
sugarlogger.Info("自定义的log:访问了sugerlogger")
})
_ = r.Run(":8888")
}
三、日志切割
目前来说日志全是写在一个文件里的 当所有的日志全在一个文件里的时候 查阅是不友好的
安装
go get -u github.com/natefinch/lumberjack
使用
由于是文件的切割 所以我们只修改getWriteSyncer方法
func getWriteSyncer() zapcore.WriteSyncer {
lumberJackLogger := &lumberjack.Logger{
Filename: "./go_zap_loglib/selflog.log", //文件位置
MaxSize: 10,//在进行切割之前,日志文件的最大大小(以MB为单位)
MaxBackups: 5, //保留旧文件的最大个数
MaxAge: 30, // 保留旧文件的最大天数
Compress: false,// 是否压缩/归档旧文件
}
return zapcore.AddSync(lumberJackLogger)
}
四、分析源码 logger如何创建的
//默认的EncoderConfig结构
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"`
}
//默认的config配置
type Config struct {
// Level is the minimum enabled logging level. Note that this is a dynamic
// level, so calling Config.Level.SetLevel will atomically change the log
// level of all loggers descended from this config.
Level AtomicLevel `json:"level" yaml:"level"`
// Development puts the logger in development mode, which changes the
// behavior of DPanicLevel and takes stacktraces more liberally.
Development bool `json:"development" yaml:"development"`
// DisableCaller stops annotating logs with the calling function's file
// name and line number. By default, all logs are annotated.
DisableCaller bool `json:"disableCaller" yaml:"disableCaller"`
// DisableStacktrace completely disables automatic stacktrace capturing. By
// default, stacktraces are captured for WarnLevel and above logs in
// development and ErrorLevel and above in production.
DisableStacktrace bool `json:"disableStacktrace" yaml:"disableStacktrace"`
// Sampling sets a sampling policy. A nil SamplingConfig disables sampling.
Sampling *SamplingConfig `json:"sampling" yaml:"sampling"`
// Encoding sets the logger's encoding. Valid values are "json" and
// "console", as well as any third-party encodings registered via
// RegisterEncoder.
Encoding string `json:"encoding" yaml:"encoding"`
// EncoderConfig sets options for the chosen encoder. See
// zapcore.EncoderConfig for details.
EncoderConfig zapcore.EncoderConfig `json:"encoderConfig" yaml:"encoderConfig"`
// OutputPaths is a list of URLs or file paths to write logging output to.
// See Open for details.
OutputPaths []string `json:"outputPaths" yaml:"outputPaths"`
// ErrorOutputPaths is a list of URLs to write internal logger errors to.
// The default is standard error.
//
// Note that this setting only affects internal errors; for sample code that
// sends error-level logs to a different location from info- and debug-level
// logs, see the package-level AdvancedConfiguration example.
ErrorOutputPaths []string `json:"errorOutputPaths" yaml:"errorOutputPaths"`
// InitialFields is a collection of fields to add to the root logger.
InitialFields map[string]interface{} `json:"initialFields" yaml:"initialFields"`
}
logger 实例创建的源码分析
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"},
}
}
//config的build方法
func (cfg Config) Build(opts ...Option) (*Logger, error) {
enc, err := cfg.buildEncoder()
if err != nil {
return nil, err
}
sink, errSink, err := cfg.openSinks()
if err != nil {
return nil, err
}
if cfg.Level == (AtomicLevel{}) {
return nil, fmt.Errorf("missing Level")
}
//关键之方法new()以及里面的zapcore.NewCore(enc, sink, cfg.Level)方法中的三个参数
// enc -> zapcore.Encoder 意思以什么格式写入日志
// sink -> zapcore.WriteSyncer 日志写到什么地方
// 日志等级cfg.Level 那种级别的日志将别写入
log := New(
zapcore.NewCore(enc, sink, cfg.Level),
cfg.buildOptions(errSink)...,
)
if len(opts) > 0 {
log = log.WithOptions(opts...)
}
return log, nil
}