跳到主要内容

日志记录

本节将介绍如何在 Go-Sail 中进行日志记录。

简介

日志记录几乎是每个系统都会执行的任务。原因很简单:日志可以帮助你追踪事件,从而便于定位和排查问题。

为了帮助你更好地洞察应用内部的运行状况,Go-Sail 提供了强大的日志服务,支持将日志信息记录到文件或通过导出器(exporter)输出。

配置

Go-Sail 的日志组件基于 uber/zap 日志库,并在服务启动时自动激活。你可以通过配置来指定其行为。

main.go
// 代码见原文,省略...

上面高亮的示例代码配置了 warn 级别,日志输出到当前目录下名为 running.log 的文件,并限制单个文件最大 100MB。Go-Sail 的日志组件支持日志轮转,无需担心日志文件体积过大或过多导致磁盘被填满。MaxBackups 表示最多保留 10 个最近的日志文件,且滚动的旧日志将被压缩,以进一步节省空间。

日志等级

日志等级决定哪些级别的日志会被记录,这一筛选是在配置阶段完成的,与具体调用什么日志方法无关。日志级别从低到高分为:

  • debug
  • info
  • warn
  • error

调用时可根据紧急和重要性优先选择。例如,普通信息可用 Info() 方法输出,当配置等级高于 info 时,代码里的此类日志不会被写入日志文件,从而实现全局管控。

main.go
import (
"github.com/keepchen/go-sail/v3/sail"
)

func main() {
sail.GetLogger().Info("记录一些日志...")
}

当设置的日志等级高于 info 时,以上代码的日志不会被写入文件,通过此机制实现统一的日志输出策略。

控制台输出(ConsoleOutput)

默认情况下,日志只输出到文件。但有时你可能希望开发调试时同时在终端看到日志,这时候只需要在配置中加上 ConsoleOutput 配置即可:

main.go
// 代码见原文,省略...

多模块(Modules)

模块配置的作用是将不同来源的日志分类记录到不同文件里,便于管理。比如可以将定时任务的日志单独写入名为 schedule 的文件。由于定时任务定期执行,日志重要但不多,分离日志便于后续查看。

在配置中声明需要的模块即可,使用时再调用对应模块:

main.go
// 代码见原文,省略...
提示

如果你调用了一个没有配置的模块,日志会默认写到默认日志文件。

例如下面两行日志,都会被记录到同一个地方:

sail.GetLogger().Info("...")
sail.GetLogger("no-existent-module").Info("...")

日志导出器(Exporter)

通常情况下,Go-Sail 日志会输出到本地文件。这在开发环境并无问题,但在复杂的服务器或微服务环境下,本地存储不可靠且不可持久,日志通常需要转发到中央日志服务或 ELK 等日志中心。这时就可以使用日志导出器。

Go-Sail 的日志导出器会将日志同步到消息队列类中间件,后续由其它消费者消费和处理。目前支持以下中间件:

  • Redis(单机)
  • Redis(集群)
  • Nats
  • Kafka
提示

注意,为保证数据不丢失,Redis 的导出器不是采用发布-订阅,而是用 List 结构执行 RPush,消费端应使用 LPop 获取。

声明日志导出器时,相关中间件如 Redis、NATS、Kafka 的连接信息需要分别单独配置。

这种设计是有意为之,为了核心服务的稳定性,初版就将日志导出配置与核心服务配置分离。若你没此类需求,也可以把两部分配置写成一样即可。

分布式链路追踪

用法

在 Web 应用程序中,记录和追踪整条链路(请求-处理-响应)很常见。

我们可能希望追踪关键事务或错误日志的全流程,便于排查与优化。这时就可用上分布式链路追踪。Go-Sail 启动时会自动启用链路追踪,你可以在路由处理函数中自由调用。

main.go
// 代码见原文,省略...

组件透传

要启用链路日志,推荐从上下文中获取日志组件进行输出,也可以将日志组件往下层函数传递实现更统一的日志记录。

main.go
// 代码见原文,省略...

微服务间链路追踪

在微服务环境下,多服务之间相互发起请求很常见。要想串联整个请求链路,需要传递请求唯一标识。如果你所有服务都用 Go-Sail,实现会自动处理。

以 Http 请求为例,只需在请求头加上 requestIdX-Request-Id 字段,值从当前请求上下文读取即可。

示例:

main.go
// 代码见原文,省略...
提示

目前 Go-Sail 只自动处理 HTTP 请求,如果你的服务使用其它协议(如 gRPC),需要自行集成,但原理一致。