首页 > 其他分享 >如何用zap定制日志格式

如何用zap定制日志格式

时间:2023-03-13 11:12:45浏览次数:56  
标签:zapcore string args func 格式 日志 zap

简介

最近在写一个全栈项目需要用到日志功能,去找了一下日志库发现zap的Star很高。

zap 是 uber 开源的 Go 高性能日志库,支持不同的日志级别, 能够打印基本信息等,但不支持日志的分割,所以我们还需要用另一个日志分割的包——lumberjack,这也是zap官方推荐的包。

日志分割:如果把全部的日志都打印到一个文件里,那势必这个文件会非常大,打开会耗费非常久的时间,同时我们也不方便找哪一天的记录。于是就需要日志分割!

日志分割可以按照日期,大小,份数进行分割

结合这两个库我们就能达到:

  • 能够将事件记录到文件中,而不是应用程序控制台;
  • 日志切割能够根据文件大小、时间或间隔等来切割日志文件;
  • 支持不同的日志级别,例如 DEBUG , INFO , WARN , ERROR 等;
  • 能够打印基本信息,如调用文件、函数名和行号,日志时间等;

导入与使用

先把zap下过来。在terminal输入:

go get github.com/uber-go/zap

zap 提供了两种类型的日志记录器—和 LoggerSugared Logger 。之间的区别是:

  • 在每一微秒和每一次内存分配都很重要的上下文中,使用Logger。它甚至比SugaredLogger更快,内存分配次数也更少,但它只支持强类型的结构化日志记录。
  • 在性能很好但不是很关键的上下文中,使用SugaredLogger。它比其他结构化日志记录包快 4-10 倍,并且支持结构化和 printf 风格的日志记录。

SugaredLogger就不在这里介绍了,因为它其实就是Logger + fmt.Printf,然后SugardLogger也是需要调用Logger的Sugar()方法获得的。

zap.Logger

zap.Logger对外提供了两个生成Logger的方法:NewProductionNewDevelopment,这两个方法主要就是设置两个内置的LoggerConfigure去生成Logger。

让我们来看看Configure长啥样:

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不是很美观,打印出来是JSON格式,而且调用栈打印不全以及没有颜色,于是我们就需要考虑定制zap logger:

定制 Zap 的 Logger

下面我们把 zap 做进一步的自定义配置,让日志不光能输出到控制台,也能输出到文件,再把日志时间由时间戳格式,换成更容易被人类看懂的DateTime时间格式。

下面少说话,直接上代码,必要的解释放在了注释里。

var logger *zap.Logger
func init() {
	// 采用默认zap提供的日志打印设置
	encoderConfig := zap.NewProductionEncoderConfig()
	// 设置日志记录中时间的格式
	encoderConfig.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
		enc.AppendString(t.Format("2006-01-02 15:04:05"))
	}
    // 设置
	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
	// 生成打印到日志文件中的encoder
	fileEncoder := zapcore.NewConsoleEncoder(encoderConfig)

	// 将日志等级标识设置为大写并且有颜色
	encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
	// 返回完整调用路径
	encoderConfig.EncodeCaller = zapcore.FullCallerEncoder
	// 生成打印到console的encoder
	consoleEncoder := zapcore.NewConsoleEncoder(encoderConfig)

	core := zapcore.NewTee(
		// 同时向控制台和文件写日志, 生产环境记得把控制台写入去掉,日志记录的基本是Debug 及以上,生产环境记得改成Info
		zapcore.NewCore(consoleEncoder, zapcore.AddSync(os.Stdout), zapcore.DebugLevel),
		zapcore.NewCore(fileEncoder, zapcore.AddSync(&logConfig.logRoll), zapcore.DebugLevel),
	)
    // 返回调用栈
	logger = zap.New(core, zap.AddCaller(), zap.AddCallerSkip(2))
}

封装Logger

我们想要像Sugared Logger一样使用我们当前的Logger,那么我们就需要封装一下。

SugaredLogger部分封装代码:

// Debug uses fmt.Sprint to construct and log a message.
func (s *SugaredLogger) Debug(args ...interface{}) {
   s.log(DebugLevel, "", args, nil)
}

// Info uses fmt.Sprint to construct and log a message.
func (s *SugaredLogger) Info(args ...interface{}) {
   s.log(InfoLevel, "", args, nil)
}

// Warn uses fmt.Sprint to construct and log a message.
func (s *SugaredLogger) Warn(args ...interface{}) {
   s.log(WarnLevel, "", args, nil)
}

// Error uses fmt.Sprint to construct and log a message.
func (s *SugaredLogger) Error(args ...interface{}) {
   s.log(ErrorLevel, "", args, nil)
}
func Info(msg string) {
   do(zap.InfoLevel, "", msg)
}

func Debug(msg string) {}
func Warn(msg string) {}
func Error(msg string) {}
func Panic(msg string) {}
func Fatal(msg string) {}

func Infof(tmplt string, args ...interface{}) {
   do(zap.InfoLevel, tmplt, args...)
}

func Debugf(tmplt string, args ...interface{}) {}
func Warnf(tmplt string, args ...interface{}) {}
func Errorf(tmplt string, args ...interface{}) {}
func Panicf(tmplt string, args ...interface{}) {}
func Fatalf(tmplt string, args ...interface{}) {}

func do(l zapcore.Level, tmplt string, args ...interface{}) {
   // 留个field字段便于以后要插东西
   var field []zapcore.Field
   msg := formatMsg(tmplt, args)
   switch l {
   case zapcore.DebugLevel:
      logger.Debug(msg, field...)
   case zapcore.InfoLevel:
      logger.Info(msg, field...)
   case zapcore.WarnLevel:
      logger.Warn(msg, field...)
   case zapcore.ErrorLevel:
      logger.Error(msg, field...)
   case zapcore.PanicLevel:
      logger.Panic(msg, field...)
   case zapcore.FatalLevel:
      logger.Fatal(msg, field...)
   }
}

func formatMsg(template string, args []interface{}) string {
   if template == "" {
      return args[0].(string)
   }
   return fmt.Sprintf(template, args...)
}

最后使用:

package main
import "zlog"

func main() {
  zlog.Errorf("err: %s", errors.New("test~"))
}

完结撒花~

参考资料:

Go 第三方库之 uber-go/zap(记录日志到文件、支持自动分割日志、支持日志级别、打印调用文件、函数和行号)

Go语言Zap库Logger的定制化和封装使用详解

标签:zapcore,string,args,func,格式,日志,zap
From: https://www.cnblogs.com/Vikyanite/p/17210643.html

相关文章

  • 【磁盘空间不足问题解决】Docker 日志清理、
    问题描述:1、系统无法访问,提示“无法访问此网站”2、启动Docker镜像提示错误信息,如下:“Errorresponsefromdaemon:Cannotrestartcontainer7f812bfba45f:write/v......
  • 多线程结合自定义logback日志实现简单的工单日志输出
    前言这周学习了logback自定义日志格式、多线程基础、以及常见的定时器,本篇博客主要是结合以上知识实现一个简单的定时全部工单输出任务,再通过自定义的日志打印输出到......
  • 接口获取阿里日志服务
    接口获取日志服务的nginx日志周一的时候,领导给了个任务我:说怎么把nginx的访问日志做成api。更准确地说,就是用接口的方式拿到nginx日志指定几个字段(日期、时间、IP、访......
  • JPEG编码协议--压缩数据格式
    接上篇学习了JPEG的编码原理,本篇学习JPEG文件压缩数据格式,文章内容主要来自ITU-t81标准,加之个人的理解说明。一、文件结构  JPEG文件使用JFIF格式作为交换格式标准。......
  • MybatisPlus(十一)配置日志显示执行SQL(配置文件篇)
    一、Springboot整合mybatisPlus配置日志#配置mybatisPlus日志mybatis-plus:configuration:log-impl:org.apache.ibatis.logging.stdout.StdOutImpl 二、测......
  • 《原神飞车》——开发日志
    2023/03/11从头搭轮子也太麻烦了,不如用插件玩玩试试。用了RealisticCarController,再用FinalIK把草神放车上,虽然还有很多Bug,但至少可以开车了!2023/03/08做之前当然是......
  • MySQL8中如何估算redo日志的大小
    先说公式:mysql>pagergrepsequence;showengineinnodbstatus\Gselectsleep(60);showengineinnodbstatus\Gnopager;mysql>select(<second_value>-<firs......
  • mysql数据库binlog日志太大解决办法
    解决办法一:1.在mysql中修改查看binlog过期时间,这个值默认是0天,也就是说不自动清理,可以根据生产情况修改,目前环境是设置的60天。showvariableslike‘expire_logs_days......
  • jvm配置+OmitStackTraceInFastThrow 导致不打印日志具体信息
    jvm配置+OmitStackTraceInFastThrow导致不打印日志具体信息参考https://blog.51cto.com/u_15246373/5363973最后查到该问题的解决方法有三种1、查询历史日志,如果日志......
  • Markdown格式
    标题井号+空格+标题内容字体粗体两边都加2个星号斜体两边加1个星号斜体加粗两边加3个星号删除线两边加2个波浪号引用大于号加空格分割线3个星号或3个减号......