当前位置: 首页 > news >正文

学会使用golang zap日志库

日志

  • 一、日志是什么
  • 二、 Sugared Logger vs Logger
  • 三、 zap的基本配置
    • zap配置包含哪几方面
  • 四、 自定义logger
    • core
    • options
  • 五、 使用Lumberjack进行日志切割归档

一、日志是什么

首先需要明确什么是日志

在程序运行的过程中,我们不可能总是能在控制台看到全部信息

因为

  • 程序是黑盒运行的

    一旦程序启动,内部状态、变量值、执行路径都是“看不到的”,除非你在特定位置打日志或调试

  • 生产环境不能用调试器(debug)

    在开发环境你可以断点调试,但上线后的服务不能暂停调试,只能通过日志了解程序的行为

  • 问题多是间歇性或难复现的

    比如用户在某个特定时间点请求超时,过一会又正常,这类问题无法稳定复现,只有日志能还原问题。

  • 多人协作/远程运维需要可追溯

    如果有运维、测试、后端、前端一起协作,只靠“肉眼”观察或靠猜是远远不够的

这时我们要想知道程序运行过程中更多的信息,就需要用到日志,给出日志的概念

Go语言中的日志(logging),是指在程序运行过程中记录信息的机制,通常用于追踪程序执行流程、调试问题、记录错误、监控运行状态等

说白了,日志就是记录把程序运行时关键的信息记录下来(作用是 调试程序、监控服务、追溯时间、异常报警

举个例子,下面是一条比较完整的日志信息

2025/07/30 10:25:00 [ERROR] [user_handler.go:45] 登录失败:用户名不存在,
userID=1234, IP=192.168.1.10

这条日志告诉我们:

  • 时间:2025/07/30 10:25:00

  • 级别:ERROR(表示错误)

  • 文件位置:user_handler.go 的第 45 行

  • 内容:用户登录失败

  • 附加信息:userID=1234,IP=192.168.1.10

所以总的来说,日志是你程序“说话”的方式,它尽可能多地还原程序在某一刻的状态、操作内容、上下文,帮助开发者和运维人员快速定位问题

已经知道了日志是什么,接下来就该了解如何使用日志了

二、 Sugared Logger vs Logger

zap提供了两种日志类型:Sugared Logger 和 Logger

  • 在性能很好但不是很关键的上下文中,使用 SugaredLogger 。它比其他结构化日志记录包快4-10倍,并且支持结构化和printf风格的日志记录。

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

这样说还是太吃理解了,能不能说人话!

举个例子,想象你在写日志,其实就是“说一句话”告诉别人发生了什么:

Logger 就像一个非常讲究的人,说话必须格式规范、用词准确。

SugaredLogger 就像你平时聊天,语法宽松,说什么都行,只要别人听懂。

  1. zap.Logger(原生 logger)
    🧱 特点:强类型、高性能、格式固定

    你必须这么说:

    logger.Info("用户登录", zap.String("用户名", "alice"), zap.Int("ID", 1001))
    

    就像你必须填一张表格,每一栏都得按格式来写,不允许乱来

    优点:格式清晰、适合机器分析、性能高
    缺点:写起来略麻烦,不像日常说话

  2. *zap.SugaredLogger(带糖 logger)
    🍬 特点:语法更轻松,像聊天一样记录日志

    你可以这样说:

    sugar.Infof("用户 %s 登录成功,ID=%d", "alice", 1001)
    sugar.Infow("用户登录成功", "用户名", "alice", "ID", 1001)
    

    就像你发微信说话,不拘小节,怎么说都行。

    优点:写起来简单、省事
    缺点:性能略低一点(但对大多数业务没影响)

开发阶段我们常用 SugaredLogger,
高性能系统或日志采集模块则更推荐用 Logger

如果你现在用的是 zap.NewProduction() 创建的 logger,只要加一行就可以用带糖版本:

sugar := logger.Sugar()

反过来,如果你用了 SugaredLogger,也可以通过 sugar.Desugar() 还原成 Logger

三、 zap的基本配置

前面提到了日志包含的几部分内容,这些内容是需要配置的
zap给我们提供了两个预设工厂函数zap.NewProduction()zap.NewDevelopment() ,可以快速创建一个配置好的logger,当然了,如果不想用这两个函数,也可以自己配置一个logger,这里我们稍后再说

默认情况下,zap.NewProduction() 和 zap.NewDevelopment() 创建的 logger 都会把日志打印到终端(标准输出,也就是 os.Stdout)。具体表现如下:

  • zap.NewProduction()
    输出格式是 JSON,适合生产环境日志采集和分析
    默认日志级别是 Info 及以上
    日志内容会打印到终端(控制台)

  • zap.NewDevelopment()
    输出格式是易读的控制台文本格式,适合开发调试
    默认日志级别是 Debug,能打印更多详细信息
    日志内容也打印到终端,方便开发时即时查看

它们的作用总结如下:

函数名适用场景日志格式默认日志级别额外特性
zap.NewProduction()生产环境JSON 格式Info自动添加 caller 信息、stacktrace
zap.NewDevelopment()开发调试人类可读文本Debug更详细、更宽松、更适合调试

zap配置包含哪几方面

zap的基本配置,可以理解为一个日志要包含哪些方面的内容:

  1. 输出位置(Output Paths)

    决定日志打印到哪:

    • 终端(console)
    • 文件(如 logs/app.log)
    • 同时打印多个位置
  2. 日志级别(Level)
    你可以控制哪些日志会被打印:

  • debug:调试用,最详细
  • info:普通日志(业务日志)
  • warn:警告,不影响业务但需要注意
  • error:错误日志(业务或系统异常)
  • fatal:致命错误,程序会崩溃

你可以通过配置或代码动态设置级别,过滤不需要的日志。

  1. 编码方式(Encoding)
    决定日志内容的格式:

    • json:结构化,适合线上,便于日志系统解析
    • console:可读性强,适合本地开发调试
  2. 字段格式(EncoderConfig)
    控制时间戳、日志级别、调用位置的格式,示例:

    zapcore.EncoderConfig{TimeKey:        "time",LevelKey:       "level",NameKey:        "logger",CallerKey:      "caller",MessageKey:     "msg",StacktraceKey:  "stacktrace",EncodeLevel:    zapcore.CapitalColorLevelEncoder,EncodeTime:     zapcore.ISO8601TimeEncoder,EncodeCaller:   zapcore.ShortCallerEncoder,
    }
    

    这个是非常重要的配置,决定你打印出来的日志长什么样。

  3. 是否打印调用位置(Caller)
    打印日志时是否带上调用该日志的文件和行号:

    logger = logger.WithOptions(zap.AddCaller())
    
  4. 开发/生产环境区别

    • zap.NewDevelopment():
      默认日志级别是 debug
      编码是 console
      更适合本地调试

    • zap.NewProduction():
      默认日志级别是 info
      编码是 json
      默认输出文件和标准输出

四、 自定义logger

想自定义一个logger,需要用到函数zap.New()

zap.New() 是 zap 库中最底层、最核心的函数之一,用于创建一个 Logger 实例。你可以把它理解为 “构建一个完整日志器的工厂函数”。它的作用是把你配置好的日志核心(Core)和可选的日志选项(Options)组合在一起,生成一个真正能用的 Logger

函数签名

func New(core zapcore.Core, options ...Option) *Logger

参数解释:

参数作用
core必须;日志的核心组件,定义“日志要写到哪、写什么、写多少级别”。必须使用 zapcore.NewCore() 来创建。
options…可选;附加功能,用于增强 logger,比如是否记录调用行号、是否打印堆栈、默认字段等。

core

我们先看第一个参数core

core的类型是 zapcore.Core, 我们找到Core的源码,可以知道core是一个接口类型

zapCore.Core:

type Core interface {LevelEnablerWith([]Field) CoreCheck(Entry, *CheckedEntry) *CheckedEntryWrite(Entry, []Field) errorSync() error
}

core必须使用zapcore.NewCore() 来创建

func NewCore(enc Encoder, ws WriteSyncer, enab LevelEnabler) Core {return &ioCore{LevelEnabler: enab,enc:          enc,out:          ws,}
}

可以看到返回一个 *ioCore 类型的结构体
那么再看一下 ioCore 的源码

type ioCore struct {LevelEnablerenc Encoderout WriteSyncer
}

可以看到这个结构体由三部分组成,

  1. encoder:日志内容格式化方式
    决定了日志内容以什么格式输出
    常用的 encoder 有:

    • zapcore.NewConsoleEncoder(cfg):适合开发环境,输出人类能看懂的日志(例如 key=value 的形式)

    • zapcore.NewJSONEncoder(cfg):适合生产环境,输出 JSON 格式的结构化日志,方便 ELK 等系统采集

  2. writeSyncer:写日志的地方
    告诉 zap:日志要写到哪去,可以是:

    • os.Stdout:控制台

    • 文件:通过 lumberjack 实现日志切割

    • 组合:zapcore.NewMultiWriteSyncer() 支持同时写多个地方

  3. logLevel:最低日志级别
    只有大于等于这个级别的日志才会被记录,比如:

    go语言中关于日志级别的定义:

    const (
    DebugLevel Level = iota - 1
    InfoLevel
    WarnLevel
    ErrorLevel
    DPanicLevel
    PanicLevel
    FatalLevel_minLevel = DebugLevel
    _maxLevel = FatalLevelInvalidLevel = _maxLevel + 1
    

options

后面的参数是可选的,它们是一些 功能性增强选项,比如:

可选参数含义
zap.AddCaller()日志中加入调用的源码文件名和行号
zap.AddCallerSkip(n)调整调用栈深度(比如封装日志函数时常用)
zap.AddStacktrace(lv)在某个级别及以上打印调用堆栈
zap.Fields(…)给 logger 添加默认的字段信息(结构化日志)
zap.Development()启用开发模式(比如日志校验更严格)

五、 使用Lumberjack进行日志切割归档

当服务产生日志很多,且希望控制文件大小/数量/时长时,就该用 Lumberjack 来切割归档日志

Lumberjack 是 Go 中一个轻量级的日志滚动库,常和 zap 搭配使用,用于日志切割与归档。这在生产环境中非常实用,能避免日志无限增长导致磁盘爆满

什么是 Lumberjack?
Lumberjack 是一个实现了 io.Writer 接口的日志文件滚动库
用一句话说:它可以根据文件大小、备份数量、保留天数等规则自动切割日志文件。

常用配置项
使用 *lumberjack.Logger 配置日志行为:

&lumberjack.Logger{Filename:   "./logs/server.log", // 日志文件路径MaxSize:    100,                 // 每个日志文件最大尺寸(MB)MaxBackups: 5,                   // 最多保留的旧日志文件数量MaxAge:     30,                  // 最长保留时间(天)Compress:   true,                // 是否压缩归档旧日志
}

和 zap 结合使用
因为 zapcore.AddSync(io.Writer) 接收的是 io.Writer,而 lumberjack.Logger 实现了这个接口,所以可以无缝对接。

完整示例:

import ("go.uber.org/zap""go.uber.org/zap/zapcore""gopkg.in/natefinch/lumberjack.v2"
)func main() {// 1. 创建 lumberjack loggerwriteSyncer := zapcore.AddSync(&lumberjack.Logger{Filename:   "./logs/server.log",MaxSize:    10,  // 10 MBMaxBackups: 3,   // 最多3个备份MaxAge:     7,   // 保留7天Compress:   true,})// 2. 设置编码器encoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())// 3. 创建 corecore := zapcore.NewCore(encoder, writeSyncer, zapcore.InfoLevel)// 4. 创建 loggerlogger := zap.New(core)defer logger.Sync()// 5. 打印日志logger.Info("这是一个日志条目", zap.String("user", "tinG"))
}

Lumberjack 的优势

优点说明
自动切割避免单个日志文件过大
自动清理根据时间或数量清理旧日志
自动压缩节省磁盘空间
与 zap 无缝配合不需要额外适配代码

补充:zap 自带不支持切割 zap 默认是将日志打印到文件或终端,但不自带日志切割能力

http://www.lryc.cn/news/604343.html

相关文章:

  • 【动态规划算法】斐波那契数列模型
  • 嵌入式开发学习———Linux环境下数据结构学习(五)
  • 服务器与电脑主机的区别,普通电脑可以当作服务器用吗?
  • 从结构到交互:HTML5进阶开发全解析——语义化标签、Canvas绘图与表单设计实战
  • MCP提示词工程:上下文注入的艺术与科学
  • 【机器学习11】“分类算法“评估矩阵:从对数损失、AUC和ROC、混淆矩阵与分类报告等角度来评估算法
  • 小架构step系列30:多个校验注解
  • Mysql事务基础
  • LeetCode Hot 100:15. 三数之和
  • 大模型赋能:台风“竹节草”精细化路径预测实践
  • 硬件电路设计(基本元器件)
  • 深入理解C++编译器优化:从O0到O3及构建模式详解
  • 【从零实践Onvif】01、Onvif详细介绍(从Onvif客户端开发的角度认识Onvif、Web Servies、WSDL、SOAP)
  • 压测合格标准
  • 智能体产品化的关键突破:企业智能化转型的“最后一公里”如何迈过?
  • 【刷题】东方博宜oj 1307 - 数的计数
  • 亮数据MCP智能服务助力数据服务
  • AD域设计与管理-批量创建域用户
  • Java 14 新特性解析与代码示例
  • 力扣刷题日常(7-8)
  • day 40 打卡-装饰器
  • 让科技之光,温暖银龄岁月——智绅科技“智慧养老进社区”星城国际站温情纪实
  • 品牌侵权查询怎么查?跨境电商怎样查品牌是否侵权?
  • 笔记本电脑开机慢系统启动慢怎么办?【图文详解】win7/10/11开机慢
  • Apache Ignite 中如何配置和启用各类监控指标
  • T113-i Linux系统完整构建指南:从SDK开箱到内核镜像量产烧录全流程
  • 计算机网络1-3:三种交换方式
  • 【38】WinForm入门到精通 ——WinForm平台为AnyCPU 无法切换为x64,也无法添加 x64及其他平台
  • 15.10 单机8卡到千卡集群!DeepSpeed实战调参手册:A100训练效率翻倍,百万成本优化实录
  • 文心大模型4.5开源:国产AI的破茧时刻与技术普惠实践