(1)go web开发之 zap日志库的使用及gin框架配置zap记录日志详细文档讲解分析

日志库 —— Go Zap 入门 本文通过丰富的代码示例演示了 zap 的基本功能,读者可以跟随这些示例,学习如何使用 zap。 阅读详情

(一)介绍

  • zap 是go 中比较火的一个日志库,提供不同级别的日志,并且速度快

  • 官方文档: https://pkg.go.dev/go.uber.org/zap#section-readme, 也可以github 直接搜索 zap。 文档上面有全面的介绍。鼓励大家观看文档, 可以有视频资料做相关引导,但学东西必须要看到官方文档。尤其是文档也已经适合入门了,先看Quick Start 部分,一般都有入门例子及整体框架介绍。

  • 官方文档介绍,非常清晰,还有相关数据对比,并且一般都会具有 example文件夹提供相关的编程实例。
    在这里插入图片描述

  • Quick Start(快速开始): zap提供了两种 日志记录器:

    • (1)SugaredLogger:(加了糖的 Logger)
      • 官方文档介绍: 在性能很好但不是很关键的环境中,使用SugaredLogger。它比其他结构化日志包快4-10倍,并且包含结构化和printf风格的api。

        // 创建一个 logger
        logger, _ := zap.NewProduction()
        defer logger.Sync() // flushes buffer, if any, 刷新缓冲区,存盘
        sugar := logger.Sugar()
        sugar.Infow("failed to fetch URL",
          // 结构化上下文为松散类型的键值对。
          "url", url,
          "attempt", 3,
          "backoff", time.Second,
        )
        sugar.Infof("Failed to fetch URL: %s", url)
        
    • (2)Logger
      • 当性能和类型安全至关重要时,使用Logger。它甚至比SugaredLogger还要快,并且分配的数量要少得多,但是它只支持结构化日志 。

        logger, _ := zap.NewProduction()
        defer logger.Sync()
        logger.Info("failed to fetch URL",
          // 作为强类型字段值的结构化上下文.
          zap.String("url", url),
          zap.Int("attempt", 3),
          zap.Duration("backoff", time.Second),
        )
        
    • 更多介绍可以看文档 choose a logger
      • 在Logger和SugaredLogger之间进行选择不需要在应用程序范围内进行决定:在两者之间进行转换既简单又便宜。从上面就可以看出来,二者创建使用区别很小。

        logger := zap.NewExample()
        defer logger.Sync()
        sugar := logger.Sugar()
        plain := sugar.Desugar()
        

(二)讲解 zap

1. 日志级别

  • 在 const 文档下面,有介绍日志级别的定义
    const (
    	// DebugLevel logs are typically voluminous, and are usually disabled in
    	// production.
    	DebugLevel = zapcore.DebugLevel
    	// InfoLevel is the default logging priority.
    	InfoLevel = zapcore.InfoLevel
    	// WarnLevel logs are more important than Info, but don't need individual
    	// human review.
    	WarnLevel = zapcore.WarnLevel
    	// ErrorLevel logs are high-priority. If an application is running smoothly,
    	// it shouldn't generate any error-level logs.
    	ErrorLevel = zapcore.ErrorLevel
    	// DPanicLevel logs are particularly important errors. In development the
    	// logger panics after writing the message.
    	DPanicLevel = zapcore.DPanicLevel
    	// PanicLevel logs a message, then panics.
    	PanicLevel = zapcore.PanicLevel
    	// FatalLevel logs a message, then calls os.Exit(1).
    	FatalLevel = zapcore.FatalLevel
    )
    

2. 构建 looger

  • 在文档的 Configuring Zap中:
    构建Logger最简单的方法是使用zap固有的预设:NewExample、NewProduction和NewDevelopment。这些预置用一个函数调用构建一个日志记录器。
  • 三者创建的 logger 是有区别的, 我们可以在官方文档的 type logger下面找到三个函数的介绍, 对应不同的场景。
    • func NewExample(options ...Option) *Logger
      • NewExample构建了一个专门为zap的可测试示例设计的Logger。它将DebugLevel及以上的日志作为JSON写入标准输出,但省略了时间戳和调用函数,以保持示例输出的简短和确定性
    • func NewProduction(options ...Option) (*Logger, error)
      • NewProduction构建了一个合理的生产日志记录器,它将infollevel及以上的日志以JSON的形式写入标准错误。
      • 它是NewProductionConfig(). build(…Option)的快捷方式。
    • func NewDevelopment(options ...Option) (*Logger, error)
      • NewDevelopment构建一个开发日志记录器,它以人类友好的格式将DebugLevel及以上级别的日志写入标准错误。
      • 这是NewDevelopmentConfig().Build(…选项)的快捷方式
      • 就好比去商店卖商品,初上自带了几个配置好的模式。 通过配置生成对应的 logger。 我们也可以自定义 配置,生成自己自定义的 logger。

3. 方法使用

  • 在文档的 types/logger 和 types/SaguredLogger 里面记录了相关的looger记录消息的使用方法。
    • 以logger为例子:
      在这里插入图片描述

    • 都接受一个 msg String, 后面是可选的一些字段。Field 类型,可以查看文档有很多的类型。

    • 写一个Get请求访问相应的网址,记录日志信息

      package main
      
      import (
      	"net/http"
      	"time"
      	"go.uber.org/zap"
      )
      
      func main() {
      	// 创建一个 logger, 可以选择其他预置创建,会有不同的输出效果
      	logger, _ := zap.NewProduction()
      	defer logger.Sync() // flushes buffer, if any, 刷新缓冲区,存盘
      	// 定义url
      	url := "http://www.baidu.com"
      	resp, err := http.Get(url)
      	// 直接出错日志
      	if err != nil {
      		logger.Error("访问失败了",
      			zap.String("url", url),
      			// 跟上错误信息
      			zap.Error(err),
      		)
      	}else {
      		// info 级别的日志信息打印
      		logger.Info("成功访问!",
      			// 作为强类型字段值的结构化上下文.
      			zap.String("url", url),
      			zap.String("status", resp.Status),
      			zap.Duration("backoff", time.Second),
      		)
      		resp.Body.Close()
      	}
      
      }
      
      • 结果分别为:
      {"level":"info","ts":1637632861.812557,
      "caller":"zap日志库学习/main.go:25",
      "msg":"成功访问!",
      "url":"http://www.baidu.com",
      "status":"200 OK","backoff":1}
      
      {"level":"error",
      "ts":1637633063.4520404,
      "caller":"zap日志库学习/main.go:18",
      "msg":"访问失败了",
      "url":"http://www.xxx.com",
      "error":"Get \"http://www.xxx.com\": dial tcp 69.171.228.20:80: connectex: A connection attempt failed because the connected party did not properly respond after a p
      eriod of time, or established connection failed because connected host has failed to respond.","stacktrace":"main.main\n\tD:/gofiles/go-learning
      -notes/go-learning/zap日志库学习/main.go:18\nruntime.main\n\tD:/software/go/go1.15/src/runtime/proc.go:204"}
      
      • 可以看到执行程序后终端提示相关的信心。 msg 就是自己设置的,时间, url等。 还有一个 caller 调用者信息,指明问题出现的行数。
    • NewDevelopment() 创建的生成日志是这样的: 空格隔开。 缺少调用者

      2021-11-23T10:08:26.171+0800    INFO    zap日志库学习/main.go:25        成功访问!     
       {"url": "http://www.baidu.com", "status": "200 OK", "backoff": "1s"}
      
    • NewExample()

      {"level":"info","msg":"成功访问!","url":"http://www.baidu.com","status":"200 OK","backoff":"1s"}
      

4. 定制化 logger

  • 查看NewProduction 的源码就能知道,实际底层就是: NewProductionConfig().Build(options...)

    • 调用了 NewProductionConfig()方法,内部初始化创建,返回了一个 Config 对象。

    • Build, 内部通过 Config对象的配置, 利用New方法生成相应的 logger对象,并返回。

    • 也就是说,这是 zap库给我们预置的 NewProduction()等方法,内部是按照指定的配置,生成相应的 logger 日志对象。 我们也可以自己调用内部的相关方法, 模仿 NewProductionConfig().Build(options…) 相关过程,自己创建,定制化 logger对象。

    • 观察New方法 生成logger 所需要的东西。在Build 函数中:

      // 返回一个 Core对象, 需要的是 三个参数
      func NewCore(enc Encoder, ws WriteSyncer, enab LevelEnabler) Core
      func New(core zapcore.Core, options ...Option) *Logger
      
      log := New(
      		zapcore.NewCore(enc, sink, cfg.Level),
      		cfg.buildOptions(errSink)...,
      	)
      
    • 官方对 Core的介绍 :需要打开对应的包,查看文档
      Core是一个最小的、快速的记录器接口。它是为库作者设计的,用来封装更友好的API。

  • 关于 NewProductionConfig()函数, 返回对应的Config 对象,Build 函数根据这个配置,进行生成 logger对象。

  • 我们当然可以自定义这个, 来实现生成自己的logger. 下面看下它的源码

    // NewProductionConfig是一个合理的生产日志配置。
    //在infollevel及以上级别启用日志记录。
    //它使用JSON编码器,写入标准错误,并启用采样。
    // stacktrace会自动包含在ErrorLevel及以上的日志中。
    func NewProductionConfig() Config {
    	return Config{
    		// 日志级别
    		Level:       NewAtomicLevelAt(InfoLevel),
    		Development: false,
    		Sampling: &SamplingConfig{
    			Initial:    100,
    			Thereafter: 100,
    		},
    		// 编码方式
    		Encoding:         "json",
    		// EncoderCofig, 配置 encoder 编辑器的默认配置。
    		EncoderConfig:    NewProductionEncoderConfig(),
    		// 打开的文件, 写入日志信息到这里。
    		OutputPaths:      []string{"stderr"},
    		ErrorOutputPaths: []string{"stderr"},
    	}
    }
    

(1)如何写入日志文件

  • 按照上面的自定义 logger, 创建核心Core需要三个参数,其中就有控制 写入文件的。

  • Encoder: 编辑器。提供了两种信息的编辑方式。

    • 需要传递的参数,可以使用 默认的EncoderConfig: NewProductionEncoderConfig() 传递进去。
    • 一种 文本样式的, 一种 json 样式的信息输入。
      在这里插入图片描述
  • WriteSyncer: 指定日志写到哪里。可以定义自己指定的文件路径
    在这里插入图片描述

    • 通过func AddSync(w io.Writer) WriteSyncer 方法,返回一个。
      AddSync用于转换io。Writer到WriteSyncer。它试图是智能的:如果具体类型的io。Writer实现了WriteSyncer,我们将使用现有的Sync方法。如果没有,我们将添加一个无操作同步。

      	// 创建文件对象
      	file, _ := os.Create("./getLog.log")  // 或者是用 OpenFile函数,在原来基础上追加。
      	// file, _ := os.OpenFile("./getLog.log", os.O_APPEND | os.O_RDWR, 0744)
      	// 生成 WriteSyncer
      	wSy := zapcore.AddSync(file)
      
  • LevelEnabler: 设置哪种级别的日志将被写入

    • 对应的就是前面介绍的日志级别;如:
      zapcore.DebugLevel
  • 创建自定义logger:

    • 根据上面三点参数的理解,就可以指定文件建立了

      // 还剩一个后面的配置信息没有传入,但是已经可以了
      // 默认我们调用 NewProduction()方法也是没有传递啥配置进去的。
      log := New(
      		zapcore.NewCore(传递编辑器(两种), 自定义文件输出, cfg.Level(级别)),
      		)
      
      • 按照这样的建立完成之后就可以使用了,往指定文件里打印日志。
    • 还有 zap 预置的生成 logger的方式,都是通过 NewProductionConfig() 来生成相关配置的, 也可以不用这么麻烦,我觉得直接, 自定义一个 NewProductionConfig() 然后,按着相应的步骤就可以了。 Build方法 就是通过 配置的 Config 对象,来生成的 logger。

    • 这里有个系列教程文章:https://blog.youkuaiyun.com/weixin_39620252/article/details/111136566

    • 像我这样:重写方法,只需要加个文件名就可以了

      func myNewProduction(options ...zap.Option) (*zap.Logger, error) {
      	return myNewProductionConfig().Build(options...)
      }
      
      func myNewProductionConfig() zap.Config {
      	config := zap.NewProductionConfig()
      	// 配置输出路径为 ./test.log   其他都不变, 内部默认是 json 编码。 只要 json 和 console两种。可以看 Encoding 字段的介绍。
      	config.OutputPaths = []string{"./test.log"}
      	return config
      }
      
      // 创建logger 对象
      logger, _ := myNewProduction()
      

(2)更改时间编码 及 添加调用者的详细信息

时间格式(或者说是编码)更改
  • 时间显示这一块,我们可以看到,默认的 NewProductionConfig()函数里创建的字段 中的 对 Encoder 消息编辑器对象的配置 EncoderConfig
    在文档中也能找到它的描述: https://pkg.go.dev/go.uber.org/zap@v1.19.1/zapcore#EncoderConfig

    func NewProductionEncoderConfig() zapcore.EncoderConfig {
    	return zapcore.EncoderConfig{
    		// 包含key的全部都是定义的输出字段名字。
    		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,
    	}
    
  • 我们顺着文档去找或者是 直接点进去 EncodeTime: zapcore.EpochTimeEncoder。 EncodeTime 顾名思义: 时间编码。就是这里控制的,点进去后,该函数下方还有好几个设置时间编码的函数, 文档中也能找到。
    在这里插入图片描述

  • 那么我们可以直接修改 对应的配置, 满足我们的自定义要求。

    • 选择 ISO8601TimeEncoder: “2006-01-02T15:04:05.000Z0700” 比较容易看懂的格式。

    • 按照上面修改导入文件的方法:

      func myNewProductionConfig() zap.Config {
      	config := zap.NewProductionConfig()
      	// 修改导入文件路径
      	config.OutputPaths = []string{"./test.log"}
      	// 修改时间格式:
      	config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
      	return config
      }
      
      • 再次运行,时间编码就会改变
      {"level":"info","ts":"2021-11-23T17:13:39.430+0800","caller":"zap日志库学习/main.go:43","msg":"成功访问!","url":"http://www.baidu.com","status":"200 OK","backoff":1}
      
    • 我们这里的写法,还是不断的 修改 NewProductionConfig() 为我们预置的配置。 如果想大改,创建自己的话, 就一步步创建 Core 核心。 然后 重新定义自己的 NewProductionEncoderConfig()。

显示详细的 添加调用者的详细信息。
  • 根据时间的理解, 这里很容易想到: 配置项当中的 EncodeCaller, 也可以指定相关的函数,用来打印 调用者的信息。

  • 还有一种方式是: 我们创建Core, zap.New()创建 logger, 分析第二个参数的 Options 类型, 文档中可以找到相关的方法,就有添加调试 显示调用人信息的方法。

    func New(core zapcore.Core, options ...Option) *Logger
    log := zap.New(
    		zapcore.NewCore(enc, sink, cfg.Level),
    		zap.AddCaller(),
    	)
    

在这里插入图片描述

5. Lumberjack 进行日志文件的切割归档

  • 这部分就比较难了,没有实际操作过,就不懂。 参考文章或者相关视频,二者结合使用。

  • 日志存的多的话, 日志文件越来越大, 几个G的话,打开和操作就太麻烦了! 那么就需要对日志文件进行分割,归档。

  • 需要安装第三方库 :Lumberjack: go get -u github.com/natefinch/lumberjack

  • 使用时仍然是打开文件, 配合 zap使用,需要创建新的 zapcore.WriteSyncer。

  • lumberjack.Logger 实现了 io.writer 接口,可以作为参数。

    func getLogWriter() zapcore.WriteSyncer {
    	lumberJackLogger := &lumberjack.Logger{
    		Filename: "./test.log",  // 导入文件名
    		MaxSize: 10, // 大小M兆
    		MaxBackups: 5,  // 最大备份数量
    		MaxAge: 30,		// 最大备份天数
    		Compress: false,  // 是否压缩
    	}
    	return zapcore.AddSync(lumberJackLogger)
    }
    
  • 进行测试:

    // 创建新的logger对象
    encoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())
    writeSyncer := getLogWriter()
    // 创建核心
    newCore := zapcore.NewCore(encoder, writeSyncer, zapcore.InfoLevel)
    // 创建logger
    logger := zap.New(newCore)
    // 插入日志
    for i := 0; i < 10000; i++ {
    	logger.Info("成功访问!",
    		// 作为强类型字段值的结构化上下文.
    		zap.String("url", "测试归档"),
    		zap.String("status", "添加数据"),
    		zap.Duration("backoff", time.Second),
    	)
    }
    defer logger.Sync() // flushes buffer, if any, 刷新缓冲区,存盘
    
    • 效果展示:以当前时间戳建立新文件
      在这里插入图片描述
    • 归档文件中有八万多数据
      在这里插入图片描述
    • test.log 中只有: 3000多条 在这里插入图片描述

6. gin框架中配置zap记录日志

  • 在上一篇文章中: 《详细讲解go web框架之gin框架源码解析记录及思路流程和理解》

    • 讲解了 gin.Default 创建引擎, 默认的添加了两个中间件。 一个是 logger 日志,一个是 recover 恢复。 gin 自带的 logger 就是在这里实现起作用的。
      • 那么我们也需要将 zap封装为 logger 中间件(HandlerFunc)。
      • 具体实现可以参考 给出的两个 logger recover 中间件的实现,加以修改。
  • 下面进行实际讲解, logger,recover 可以自己实现,也有现成的:https://github.com/gin-contrib/zap/blob/master/zap.go#L3

  • yaml格式的配置文件:

    app:
      name: "web_app"
      # 开发模式
      mode: "dev"
      port: 8080
    
    
    log:
      level: "debug"
      filename: "web_app.log"
      # 最大存储大小
      max_size: 200
      # 最大存储时间
      max_age: 30
      # 备份数量
      max_backups: 7
    
  • 实现logger:

    package logger
    
    import (
    	"github.com/gin-gonic/gin"
    	"github.com/natefinch/lumberjack"
    	"github.com/spf13/viper"
    	"go.uber.org/zap"
    	"go.uber.org/zap/zapcore"
    	"net"
    	"net/http"
    	"net/http/httputil"
    	"os"
    	"runtime/debug"
    	"strings"
    	"time"
    )
    
    
    // 初始化Logger
    func Init() (err error) {
    	// 创建Core三大件,进行初始化
    	writeSyncer := getLogWriter(
    		viper.GetString("log.filename"),
    		viper.GetInt("log.max_size"),
    		viper.GetInt("log.max_backups"),
    		viper.GetInt("log.max_age"),
    		)
    	encoder := getEncoder()
    	// 定义一个日志级别类型指针
    	var l = new(zapcore.Level)
    	// 将 yaml 配置文件中的 表示级别的文本,转换为 相应的 级别类型,赋值给 创建的指针
    	err = l.UnmarshalText([]byte(viper.GetString("log.level")))
    	if err != nil {
    		return
    	}
    	// 创建核心
    	core := zapcore.NewCore(encoder, writeSyncer, l)
    	// 创建 logger 对象
    	log := zap.New(core, zap.AddCaller())
    	// 替换全局的 logger, 后续在其他包中只需使用zap.L()调用即可
    	zap.ReplaceGlobals(log)
    	return
    }
    
    
    func getEncoder() zapcore.Encoder {
    	// 使用zap提供的 NewProductionEncoderConfig
    	encoderConfig := zap.NewProductionEncoderConfig()
    	// 设置时间格式
    	encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
    	// 时间的key
    	encoderConfig.TimeKey = "time"
    	// 级别
    	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
    	// 显示调用者信息
    	encoderConfig.EncodeCaller = zapcore.ShortCallerEncoder
    	// 返回json 格式的 日志编辑器
    	return zapcore.NewJSONEncoder(encoderConfig)
    }
    
    
    func getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer {
    	// 使用 lumberjack 归档切片日志
    	lumberJackLogger := &lumberjack.Logger{
    		Filename:   filename,
    		MaxSize:    maxSize,
    		MaxBackups: maxBackup,
    		MaxAge:     maxAge,
    	}
    	return zapcore.AddSync(lumberJackLogger)
    }
    
    
    // GinLogger 接收gin框架默认的日志
    func GinLogger() gin.HandlerFunc {
    	logger := zap.L()
    	return func(c *gin.Context) {
    		start := time.Now()
    		path := c.Request.URL.Path
    		query := c.Request.URL.RawQuery
    		c.Next()
    
    		cost := time.Since(start)
    		logger.Info(path,
    			zap.Int("status", c.Writer.Status()),
    			zap.String("method", c.Request.Method),
    			zap.String("path", path),
    			zap.String("query", query),
    			zap.String("ip", c.ClientIP()),
    			zap.String("user-agent", c.Request.UserAgent()),
    			zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
    			zap.Duration("cost", cost),
    		)
    	}
    }
    
    
    // GinRecovery
    func GinRecovery(stack bool) gin.HandlerFunc {
    	logger := zap.L()
    	return func(c *gin.Context) {
    		defer func() {
    			if err := recover(); err != nil {
    				// Check for a broken connection, as it is not really a
    				// condition that warrants a panic stack trace.
    				var brokenPipe bool
    				if ne, ok := err.(*net.OpError); ok {
    					if se, ok := ne.Err.(*os.SyscallError); ok {
    						if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
    							brokenPipe = true
    						}
    					}
    				}
    
    				httpRequest, _ := httputil.DumpRequest(c.Request, false)
    				if brokenPipe {
    					logger.Error(c.Request.URL.Path,
    						zap.Any("error", err),
    						zap.String("request", string(httpRequest)),
    					)
    					// If the connection is dead, we can't write a status to it.
    					c.Error(err.(error)) // nolint: errcheck
    					c.Abort()
    					return
    				}
    
    				if stack {
    					logger.Error("[Recovery from panic]",
    						zap.Any("error", err),
    						zap.String("request", string(httpRequest)),
    						zap.String("stack", string(debug.Stack())),
    					)
    				} else {
    					logger.Error("[Recovery from panic]",
    						zap.Any("error", err),
    						zap.String("request", string(httpRequest)),
    					)
    				}
    				c.AbortWithStatus(http.StatusInternalServerError)
    			}
    		}()
    		c.Next()
    	}
    }
    

7. gin中使用日志的补充

  • 我们上面代码生成的 核心,默认的级别以及是向文件中进行输出的。 那么我们在生产环境中的时候,就会很不方便查看日志的信息。 解决办法,可以定义一个 mode string 类型的参数。根据需要去创建 core满足,在控制台输出的需求。

  • 更改上述代码中创爱核心的操:

    // 新增: 根据 mode 参数创建
    	var core zapcore.Core
    	if mode == "dev" {
    		// 开发模式,日志输出到终端
    		consoleEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())
    		// NewTee创建一个核心,将日志条目复制到两个或多个底层核心中。
    		core = zapcore.NewTee(
    			zapcore.NewCore(encoder, writeSyncer, l),
    			zapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel),
    		)
    	} else {
    		// 创建核心
    		core = zapcore.NewCore(encoder, writeSyncer, l)
    	}
    
    • 这里面 我们上面讲过json 形式的日志, 还有就是 zapcore.NewConsoleEncoder想控制台打印信息。使用的也是默认自带的配置。
    • 其中zap的 zapcore.NewTee方法,可以支持多个核心。像上面这样 日志既会正常的在文件中写入,还会在终端中打印。
  • 另外对于 gin 日志的相关,gin文档中也有相关的描述:

    • 写日志文件: https://www.kancloud.cn/shuangdeyu/gin_book/949424

      func main() {
          // 禁用控制台颜色
          gin.DisableConsoleColor()
      
          // 创建记录日志的文件
          f, _ := os.Create("gin.log")
          gin.DefaultWriter = io.MultiWriter(f)
      
          // 如果需要将日志同时写入文件和控制台,请使用以下代码
          // gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
      
          router := gin.Default()
          router.GET("/ping", func(c *gin.Context) {
              c.String(200, "pong")
          })
      
          router.Run(":8080")
      }
      
      • 写日志文件里有对上面定义的: os.Stdout的描述,但是zap里不能直接使用, 还要通过 Lock() 函数进行类型转换下。
    • 自定义日志格式:https://www.kancloud.cn/shuangdeyu/gin_book/949425

  • 另外 gin 默认的是debug 模式,会输出一些信息在终端:
    在这里插入图片描述

  • 也提示我们 如果要在生产环境中 可以通过两种方式实现, 第二种是在程序中,创建 engine 实例之前调用就行了。

  • 下面是 gin源码里的三种模式常量

    const (
    	// DebugMode indicates gin mode is debug.
    	DebugMode = "debug"
    	// ReleaseMode indicates gin mode is release.
    	ReleaseMode = "release"
    	// TestMode indicates gin mode is test.
    	TestMode = "test"
    )
    
高性能日志库 Zap:Go 开发者的必备工具 在现代微服务架构中,日志不仅是系统运行状态的记录,更是调试、分析问题的重要手段。Go 语言中有不少优秀的日志库,其中 **Zap** 凭借其 **高性能** 和 **灵活性**,成为了众多开发者的首选工具。 本篇文章将详细介绍 Zap 的核心功能、优势及如何在项目中使用它来提高日志处理的效率。 阅读详情

相关推荐

Zap:Go 的高性能日志库

/ 配置日志级别、输出目标、时间格式等config.Level = zap.NewAtomicLevelAt(zap.DebugLevel) // 启用 Debug 级别config.OutputPaths = []string{"stdout", "/var/log/app.log"} // 输出到控制台和文件config.EncoderConfig.TimeKey = "timestamp" // 自定义时间字段名。

m0_66925868的博客 1424

go zap日志库的使用,以及封装。

参考zap日志的基本使用(go必会知识*)一张图项目在开发阶段,如果出现问题,一般会去查看日志,来定位问题,这是非常有效的,上线后更加需要日志那么我们需要怎么样的日志呢?能打印最基本的信息,例如调用的文件,函数名称,行号,日志时间等支持不同的日志级别,例如: info、debug、error 等能够将记录的日志保存在文件里面,并且可以根据时间或者文件大小来切割日志文件,而zap就完全满足了,他非常的高效,并且是结构化的,分级的go日志库。如果不想将日志信息打印在终端,那么可以自定义配置,使用。.......

weixin_48536164的博客 5056

Go Web应用骨架构建:从Gin、GORM到Zap的现代化实践

在Web后端开发中,构建一个可复用、高效且符合最佳实践的项目骨架是提升团队协作和开发效率的关键。其核心在于合理的架构设计与技术选型,通过分层解耦确保代码的可维护性和可扩展性。在Go语言生态中,Gin框架以其高性能和丰富的中间件生态成为Web层的热门选择,而GORM作为ORM工具简化了数据持久化操作,Zap则提供了高性能的结构化日志解决方案。这些技术组合在一起,能够为业务逻辑开发提供稳固的基础设施支持,尤其适用于需要快速迭代和高并发处理的API服务、微服务等场景。本文将以webgoc项目为例,详细解析如何集成

weixin_31528001的博客 351

Go语言日志库zap

Go语言日志库zap

YIYIYI 2278

Gin框架日志处理:从基础配置到高级定制

在Web开发中,日志是系统的“黑匣子”——它记录了请求的轨迹、错误的线索和系统的状态。Gin作为Go语言中最流行的高性能Web框架,其日志处理机制灵活但需要合理配置。本文将覆盖Gin日志的基础使用(如默认日志配置)、进阶操作(如自定义格式、多输出目标)和高级定制(如集成Zap、日志钩子),帮助开发者构建“看得懂、查得到、管得住”的日志系统。用“快递单”类比理解Gin日志的核心概念;演示默认日志的使用和基础配置;讲解自定义格式、多输出目标等进阶技巧;

AI Agent Harness Engineering 934

Kylin-Desktop-V10-SP1-General-Release-2303-X86_64-海光版(hygon c86)安装与配置指南

本文提供了在国产海光C86处理器平台上安装与配置Kylin Desktop V10 SP1操作系统的详细指南。内容涵盖从下载专用镜像、制作启动盘、BIOS关键设置、磁盘分区方案,到系统安装后的驱动优化、必备软件安装及常见故障排除,旨在帮助用户顺利完成hygon c86平台的系统部署与优化。

tree的博客 1349

Go语学习笔记 - 配置文件使用、日志配置 | Web框架Gin(二)

我还是想按照正常的项目结构调整一下,这篇笔记主要是三个部分:调整项目目录结构、增加配置文件使用、增加日志配置,很常规而且也是每个项目都需要用到的。

阿良的博客 3725

基于51单片机的多功能计算器全套设计

当今社会,随着人们物质生活的不断提高,电子产品已经走进了家家户户,无论是生活或学习,还是娱乐和消遣几乎样样都离不开电子产品,大型复杂的计算能力是人脑所不能胜任的,而且人脑比较容易出错。计算器作为一种快速通用的计算工具方便了用户的使用。计算器可谓是我们最亲密的电子伙伴之一。本设计着重在于分析计算器设计开发过程中的环节和步骤,并从实践经验出发对计算器设计做了详细的分析和研究。 本设计是以STC89C52单片机为核心的计算器模拟系统设计,输入采用5×8矩阵键盘,可以进行加、减、乘、除等十几种数字运算,同时支持括号

runweipa的博客 1741

常见的Go库和框架

入门推荐:从标准库开始学习,然后尝试Gin或Echo构建简单API。GORM和Viper是数据库和配置管理的首选。选择原则:根据项目规模:小项目用轻量库(如Chi + Zap),大项目选框架(如Beego)。检查GitHub stars和文档活跃度确保可靠性。资源:官方站点pkg.go.dev提供库搜索,社区资源如Awesome Go列出更多选项。如果您有具体场景(如开发Web API或CLI工具),我可以提供更针对性的建议!以下是关于Go语言(Golang)中常见的库和框架的详细介绍。

qq_40320924的博客 2198

ESP32-C3微型9轴感知模块的硬件工程实践

在嵌入式系统小型化趋势下,低功耗微控制器与多传感器融合成为智能终端的基础技术路径。其核心原理在于芯片能效比、射频合规性与电源噪声抑制的协同优化,技术价值体现在降低认证门槛、提升EMC鲁棒性及保障MEMS传感器精度。典型应用场景包括可穿戴设备、微型无人机姿态控制和工业边缘传感节点。本文围绕ESP32-C3平台展开,深入解析QFN封装选型逻辑、同步降压电源设计、LSM9DS1九轴校准策略等关键环节,覆盖FCC/CE认证规避、USB-C接口EMC防护等高频工程痛点。

weixin_42607969的博客 366

仓储控制系统(WCS)设计

WCS系统设计方法

cetclyb的博客 2万+

【亲测免费】 探索高效日志管理:zap在Gin框架中的应用

在现代软件开发中,日志管理是确保系统稳定性和可维护性的关键环节。本文将向您介绍一个强大的开源项目——`zap`,它为Gin框架提供了一种高效的日志记录解决方案。通过深入分析其技术特点和应用场景,我们相信您会对其产生浓厚的兴趣。 ## 项目介绍 `zap`是一个为Gin框架设计的日志中间件,它利用了Uber开发的`zap`日志库,提供了高性能和可配置的日志记录功能。通过集成`zap`,开发者可以

gitblog_00439的博客 464

手把手教你用TP4056和PMOS管搭建锂电池自动切换电路(含元器件选型指南)

本文详细介绍了如何使用TP4056和PMOS管搭建锂电池自动切换电路,包括核心架构设计、元器件选型指南和PCB布局优化。通过智能充电算法和电源路径切换模块,实现外部供电与锂电池的无缝切换,提升便携式设备的电源管理效率。

julia4scientist的博客 887

Go-Gin-API 终极日志指南:Zap 高性能日志系统深度解析

Go-Gin-API 是一个用于快速构建 Go 语言 API 的框架,提供丰富的中间件和模块化架构。本文将深入解析如何在 Go-Gin-API 项目中集成和优化 Zap 高性能日志系统,帮助开发者实现结构化、高吞吐的日志管理。 ## 为什么选择 Zap 作为 Go-Gin-API 的日志解决方案? Zap 是由 Uber 开源的结构化日志库,以其卓越的性能和低资源消耗著称。在 Go-Gin-A

gitblog_00108的博客 474

EPO: Hierarchical LLM Agents with Environment PreferenceOptimization

长期决策和规划任务依然是基于大型语言模型(LLM)的智能体面临的一大挑战(Valmeekam 等,2023;Liu 等,2023;Silver 等,2024)。这些任务需要智能体进行多步骤的规划,同时保持行为的一致性和目标导向,这对于通常被设计用于即时和局部预测的大型语言模型来说颇为困难。此外,针对具身智能体的大型语言模型微调面临的一个关键难题是大规模标注数据的需求(Reed 等,2022)。

qq_52053775的博客 142

基于FPGA的数字信号处理(11)--定点数的舍入模式(2)向最临近值取整nearest

基于FPGA的数字信号处理(11)--定点数的舍入模式(2)向最临近值取整nearest

孤独的单刀 5472

【Bluebell】项目总结:基于 golang 的前后端分离 web 项目实战

对 bluebell 项目进行总结。

YGGP的博客 2368

把 ABAP 开发对象装进 XML 行李箱:SAPlink 安装、插件、升级与协作迁移全指南

SAPlink是一款用于在SAP NetWeaver系统间导入导出ABAP对象的工具,特别适合临时协作、系统孤岛等场景。它通过XML文件(Slinkee和Nugget两种格式)传输开发对象,支持插件扩展多种对象类型。安装时需注意版本兼容性、权限和激活流程,使用时要区分覆盖选项的风险。相比正式运输系统,SAPlink更适用于快速共享、原型交付等非生产场景,但需配合命名规范和协作策略确保可维护性。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 151

GO GIN 推荐的库

在使用 Go 和 Gin 框架进行 Web 开发时,有许多第三方库可以增强功能和提高开发效率。

GitHub个人仓库地址:https://github.com/feiyuluoye ; 个人塔罗牌占卜平台:http://118.126.82.12:8080/ ; 欢迎广大Coding友共同交流讨论。 1501

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

探索高效API开发:Go-Gin-API框架全面解析

在如今快节奏的软件开发环境中,高效且稳定的API框架显得至关重要。今天我们要向您推荐的是一个强大的Go语言API开发框架——Go-Gin-API。它以Gin作为基础,集成了多种实用工具和组件,让您的API开发工作变得更加便捷和规范。 ## 项目介绍 Go-Gin-API是一个精心设计的API框架,通过模块化的结构和严谨的编码规则,助力开发者快速构建稳定可靠的业务系统。框架中包含了接口限流、异常...

gitblog_00835的博客 946

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

几个提升Go业务开发效率的流行框架和开源库

最近总有一些初学Go语言的小伙伴问我在业务开发中一般都使用什么web框架、开源中间件;所以我总结了我在日常开发中使用到的库,这些库不一定是特别完美的,但是基本可以解决日常工作需求,接下来我...

kevin_tech的博客,微信搜「网管叨bi叨」 919

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Golang常用Web开发库

Go语言拥有丰富的开源库生态,涵盖Web开发、数据库操作、配置管理等多个领域。常用库包括Gin/Echo(Web框架)、GORM/sqlx(数据库)、Viper(配置管理)、Logrus/Zap(日志)、Testify(测试)等。根据项目需求可选择不同工具,如高性能API服务推荐Gin,ORM需求推荐GORM。此外,Go标准库本身功能强大,建议结合官方文档和实际项目进行学习。这些工具能显著提升开发效率和代码质量。

才这么点的博客 1076

golang开发类库推荐

Golang开发类库推荐

飞翔码农的博客 978

Go API 项目教程

Go API 项目教程 项目介绍 go-api 是一个基于 Go 语言和 Gin 框架的 RESTful API 项目。该项目旨在提供一个简单、高效的 API 开发框架,帮助开发者快速构建和部署 RESTful 服务。go-api 封装了常用的功能,如跨域支持、JWT 签名验证、日志收集、异常捕获、链路追踪、监控指标、Swagger 文档生成等,使得开发者可以专注于业务逻辑的实现。 项目快速启动 ...

gitblog_01102的博客 1033

Go 开发中常用的几个开源库

哈喽,大家好,我是asong。最近总有一些初学Go语言的小伙伴问我在业务开发中一般都使用什么web框架、开源中间件;所以我总结了我在日常开发中使用到的库,这些库不一定是特别完美的,但是基本...

slphahaha的博客 1563

Go业务开发中常用的几个开源库

前言最近总有一些初学Go语言的小伙伴问我在业务开发中一般都使用什么web框架、开源中间件;所以我总结了我在日常开发中使用到的库,这些库不一定是特别完美的,但是基本可以解决日常工作需求,接下来我们就来看一下。GinGin是一个用Go编写的Web框架,它是一个类似于martini但拥有更好性能的API框架。基本现在每个Go初学者学习的第一个web框架都是Gin。在网上看到一个...

Seekload 729
上一篇: 详细讲解go web框架之gin框架源码解析记录及思路流程和理解
下一篇: (2)go web开发之 使用viper读取配置文件
我是py呀
博客等级 码龄8年 205粉丝 130原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值