字节RPC框架Kitex的日志库klog竟然也这么小巧!

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 这篇文章将着重于分析字节跳动开源的RPC框架Kitex的日志库klog的源码,通过对比Go原生日志库log的实现,探究其作出的改进。

前言

这篇文章将着重于分析字节跳动开源的RPC框架Kitex的日志库klog的源码,通过对比Go原生日志库log的实现,探究其作出的改进。

为了平滑学习曲线,我写下了这篇分析Go原生log库的文章,希望你可以对比阅读:https://juejin.cn/post/7103790667595268126

本文的分析基于:github.com/cloudwego/kitex/pkg/klog的源码。

klog库的使用

image-20220602161518842

结果如下:

image-20220602103107720

klog.xxx能直接打印日志的原因

image-20220602162933166

通过观察源码,klog包的default.go文件中,封装了三类日志的打印的函数提供直接使用:普通日志、格式化的日志、格式化的Context日志

每一类包含了7个的日志输出级别的函数可使用:InfoDebugNoticeWarnErrorFatalTrace

并且这21个函数中频繁使用到了一个logger实例,只要我们引入klog包,logger就会完成初始化,并且作为默认的log实例。

image-20220602164445885

可以看到logger实例默认的日志打印级别是LevelInfoklog通过常量计数器定义了0~6种日志级别:

image-20220602172735438

FullLogger接口

image-20220602164335103

默认的logger实例是通过defaultLogger结构初始化的,且defaultLogger结构实现了FullLogger接口定义的所有函数(接口定义了上面说的三类,每一类7种日志打印函数)

image-20220602170223047

并且观察defaultLogger结构的属性stdlog,就是Go原生的日志库log定义的Logger类型,因此klog的所有日志操作,最终都是借助Go原生log库实现的。

image-20220602170523008

相当于klog在Go原生log库的基础上对格式化输出日志打印级别作了封装,便于直接使用。

串联一下日志打印函数执行的过程:

  • main函数中调用:klog.Info("一条普通的日志")
  • 进一步调用初始化好的defaultLogger实例(名为logger)的实现自FullLogger接口的函数:logger.Info()
  • 进一步调用ll.logf()函数(下面重点分析

ll.logf()

上面的这三类共21个日志打印函数最终都调用了ll.logf()方法,因此ll.logf()也是klog库的核心函数,看一下代码:

image-20220602172144229

  • 日志过滤:如果调用的打印函数代表的日志级别低于logger实例初始化的日志级别,则不会打印(如默认级别是LevelInfo == 2,则调用klog.Trace()将被过滤)
  • 格式化打印信息存入msg
  • 调用Go原生日志库logOutput()函数,打印日志(这一部分在上一篇分析Go的log库的文章中已经充分讲解)

关于calldepth的问题

calldepth表示调用层数,这里声明了4,是为了配合获取调用日志打印函数的文件名和所在行数。

image-20220602175219202

  • calldepth == 0,表示获取调用runtime.Caller(calldepth)的文件名和行数
  • calldepth == 1,表示获取调用std.Output()的文件名和行数
  • calldepth == 2,表示获取调用ll.logf()的文件名和行数
  • calldepth == 3,表示获取调用logger.Info()的文件名和行数
  • calldepth == 4,表示获取调用klog.Info()的文件名和行数(也就是main.go文件)

基于klog再度进行封装,在打印日志获取文件名时可能会有问题,下面是摘自Kitex文档的一句描述:

image-20220602180622096

猜测原因就是klog的封装,固定了calldepth == 4,确保其在获取文件信息时能定位到main.go文件中,而如果对klog再封几层,会导致calldepth需要更大才能定位到最外层main.go文件,而这个值并不能通过klog的提供的实现进行修改。

在初始化时通过log.New()函数指定了日志输出的位置需要打印的前置信息(文件名、行数、日期)

image-20220602175507724

定制自己的Logger

image-20220602181634895

可以使用klog.SetLogger()来替换掉默认的logger实现,需要传入一个实现了所有FullLogger接口中定义的方法的实例。

值得注意的是:SetLogger()函数并非是并发安全的,这个方法不应该在你使用了默认的defaultLogger定义实例之后再去使用(会覆盖掉默认的logger实例)。

当然完全重新定制比较复杂,大多数时候,我们只需要在默认的logger基础上重定向日志输出或者修改默认日志级别即可:

image-20220602183649139

下面修改日志打印级别为Notice(高于Info),并且重定向日志的输出:

image-20220602184046958

这里指定了日志输出到文件log.txt中,并且因为Info级别低于声明的Notice,因此日志输出操作被忽略:

image-20220602184350533

小结

通过分析,我们发现klogGo原生log库的基础上,进行了精简的二次封装,一定程度上约束了打印的日志的内容为:日期 + 时间微秒级 + 调用文件名 + 所在行数 + 日志级别 + 格式化的日志内容,使用十分便捷。

当然它也提供了SetLogger()方法去供你自己实现logger实例,以满足更多的定制化需求。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
8天前
|
JSON 中间件 Go
go语言后端开发学习(四) —— 在go项目中使用Zap日志库
本文详细介绍了如何在Go项目中集成并配置Zap日志库。首先通过`go get -u go.uber.org/zap`命令安装Zap,接着展示了`Logger`与`Sugared Logger`两种日志记录器的基本用法。随后深入探讨了Zap的高级配置,包括如何将日志输出至文件、调整时间格式、记录调用者信息以及日志分割等。最后,文章演示了如何在gin框架中集成Zap,通过自定义中间件实现了日志记录和异常恢复功能。通过这些步骤,读者可以掌握Zap在实际项目中的应用与定制方法
go语言后端开发学习(四) —— 在go项目中使用Zap日志库
|
3天前
|
XML Java Maven
Spring5入门到实战------16、Spring5新功能 --整合日志框架(Log4j2)
这篇文章是Spring5框架的入门到实战教程,介绍了Spring5的新功能——整合日志框架Log4j2,包括Spring5对日志框架的通用封装、如何在项目中引入Log4j2、编写Log4j2的XML配置文件,并通过测试类展示了如何使用Log4j2进行日志记录。
Spring5入门到实战------16、Spring5新功能 --整合日志框架(Log4j2)
|
5天前
|
存储 JSON 前端开发
一文搞懂 Go 1.21 的日志标准库 - slog
一文搞懂 Go 1.21 的日志标准库 - slog
15 2
|
5天前
|
JSON Go API
一文搞懂 Golang 高性能日志库 - Zap
一文搞懂 Golang 高性能日志库 - Zap
15 2
|
5天前
|
Java 数据库连接 数据库
后端框架的学习----mybatis框架(6、日志)
这篇文章介绍了如何在MyBatis框架中使用日志功能,包括配置MyBatis的日志实现、使用log4j作为日志工具,以及如何通过配置文件控制日志级别和输出格式。
|
9天前
|
存储 缓存 监控
【嵌入式SD NAND】基于FATFS/Littlefs文件系统的日志框架实现
综上所述,构建一个基于FATFS/Littlefs文件系统的日志框架需要对文件系统的操作有深入理解,并以此为基础设计一套完整的日志处理机制。这样的框架不仅能够确保日志数据的完整性和系统的鲁棒性,同时还能够满足嵌入式系统对于性能和资源使用的严格要求。
29 4
|
19天前
|
开发框架 缓存 前端开发
基于SqlSugar的开发框架循序渐进介绍(8)-- 在基类函数封装实现用户操作日志记录
基于SqlSugar的开发框架循序渐进介绍(8)-- 在基类函数封装实现用户操作日志记录
47 12
|
11天前
|
存储 安全 Python
[python]使用标准库logging实现多进程安全的日志模块
[python]使用标准库logging实现多进程安全的日志模块
|
18天前
|
XML 存储 JSON
(十二)探索高性能通信与RPC框架基石:Json、ProtoBuf、Hessian序列化详解
如今这个分布式风靡的时代,网络通信技术,是每位技术人员必须掌握的技能,因为无论是哪种分布式技术,都离不开心跳、选举、节点感知、数据同步……等机制,而究其根本,这些技术的本质都是网络间的数据交互。正因如此,想要构建一个高性能的分布式组件/系统,不得不思考一个问题:怎么才能让数据传输的速度更快?
|
25天前
|
测试技术 UED 存储
SLS Prometheus存储问题之在使用内置降采样时,SLS自动选择适配的指标库该如何解决
SLS Prometheus存储问题之在使用内置降采样时,SLS自动选择适配的指标库该如何解决

热门文章

最新文章