字节开源RPC框架Kitex的日志库klog源码解读
前言
这篇文章将着重于分析字节跳动开源的RPC框架Kitex的日志库klog的源码,通过对比Go原生日志库log的实现,探究其作出的改进。
为了平滑学习曲线,我写下了这篇分析Go原生log库的文章,希望你可以对比阅读:https://juejin.cn/post/7103790667595268126
本文的分析基于:github.com/cloudwego/kitex/pkg/klog的源码。
klog库的使用
结果如下:
klog.xxx能直接打印日志的原因
通过观察源码,klog包的default.go文件中,封装了三类日志的打印的函数提供直接使用:普通日志、格式化的日志、格式化的Context日志。
每一类包含了7个的日志输出级别的函数可使用:Info、Debug、Notice、Warn、Error、Fatal、Trace。
并且这21个函数中频繁使用到了一个logger实例,只要我们引入klog包,logger就会完成初始化,并且作为默认的log实例。
可以看到logger实例默认的日志打印级别是LevelInfo,klog通过常量计数器定义了0~6种日志级别:
FullLogger接口
默认的logger实例是通过defaultLogger结构初始化的,且defaultLogger结构实现了FullLogger接口定义的所有函数(接口定义了上面说的三类,每一类7种日志打印函数)
并且观察defaultLogger结构的属性stdlog,就是Go原生的日志库log定义的Logger类型,因此klog的所有日志操作,最终都是借助Go原生log库实现的。
相当于klog在Go原生log库的基础上对格式化输出和日志打印级别作了封装,便于直接使用。
串联一下日志打印函数执行的过程:
main函数中调用:klog.Info("一条普通的日志")- 进一步调用初始化好的
defaultLogger实例(名为logger)的实现自FullLogger接口的函数:logger.Info()
- 进一步调用
ll.logf()函数(下面重点分析)
ll.logf()
上面的这三类共21个日志打印函数最终都调用了ll.logf()方法,因此ll.logf()也是klog库的核心函数,看一下代码:
- 日志过滤:如果调用的打印函数代表的日志级别低于
logger实例初始化的日志级别,则不会打印(如默认级别是LevelInfo == 2,则调用klog.Trace()将被过滤) - 格式化打印信息存入
msg - 调用Go原生日志库
log的Output()函数,打印日志(这一部分在上一篇分析Go的log库的文章中已经充分讲解)
关于calldepth的问题
calldepth表示调用层数,这里声明了4,是为了配合获取调用日志打印函数的文件名和所在行数。
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文档的一句描述:
猜测原因就是klog的封装,固定了calldepth == 4,确保其在获取文件信息时能定位到main.go文件中,而如果对klog再封几层,会导致calldepth需要更大才能定位到最外层main.go文件,而这个值并不能通过klog的提供的实现进行修改。
在初始化时通过log.New()函数指定了日志输出的位置和需要打印的前置信息(文件名、行数、日期)
定制自己的Logger
可以使用klog.SetLogger()来替换掉默认的logger实现,需要传入一个实现了所有FullLogger接口中定义的方法的实例。
值得注意的是:SetLogger()函数并非是并发安全的,这个方法不应该在你使用了默认的defaultLogger定义实例之后再去使用(会覆盖掉默认的logger实例)。
当然完全重新定制比较复杂,大多数时候,我们只需要在默认的logger基础上重定向日志输出或者修改默认日志级别即可:
下面修改日志打印级别为Notice(高于Info),并且重定向日志的输出:
这里指定了日志输出到文件log.txt中,并且因为Info级别低于声明的Notice,因此日志输出操作被忽略:
小结
通过分析,我们发现klog在Go原生log库的基础上,进行了精简的二次封装,一定程度上约束了打印的日志的内容为:日期 + 时间微秒级 + 调用文件名 + 所在行数 + 日志级别 + 格式化的日志内容,使用十分便捷。
当然它也提供了SetLogger()方法去供你自己实现logger实例,以满足更多的定制化需求。
字节开源RPC框架Kitex的日志库klog源码解读的更多相关文章
- 标准库path源码解读
先看标准库 作用:关于路径的一些实用操作 https://github.com/golang/go/blob/master/src/path/path.go 源码地址 func IsAbs func ...
- Go RPC 框架 KiteX 性能优化实践 原创 基础架构团队 字节跳动技术团队 2021-01-18
Go RPC 框架 KiteX 性能优化实践 原创 基础架构团队 字节跳动技术团队 2021-01-18
- 各大开源rpc 框架 比较
各大开源rpc 框架 比较 1. 前言 随着现在互联网行业的发展,越来越多的框架.中间件.容器等开源技术不断地涌现,更好地来服务于业务,解决实现业务的问题.然而面对众多的技术选择,我们要如何甄别出 ...
- 如何优雅的阅读 GitHub 上开源 js 框架和库的源码
如何优雅的阅读 GitHub 上开源 js 框架和库的源码 step 先总后分,即先了解一下啊框架的大体架构,又一个全局的认识,在选择某些和感兴趣的部分,仔细阅读,各个击破: 带着问题阅读,用到了什么 ...
- 微前端框架 之 qiankun 从入门到源码分析
封面 简介 从 single-spa 的缺陷讲起 -> qiankun 是如何从框架层面解决 single-spa 存在的问题 -> qiankun 源码解读,带你全方位刨析 qianku ...
- Go 源码解读|如何用好 errors 库的 errors.Is() 与 errors.As() 方法
前言 快一个月没有更新技术文章了,这段时间投注了较多的时间学习字节的开源项目 Kitex/Hertz ,并维护一些简单的 issue ,有兴趣的同学也可以去了解: https://www.cloudw ...
- 源码解读SLF4J绑定日志实现的原理
一.导读 我们使用log4j框架时,经常会用slf4j-api.在运行时,经常会遇到如下的错误提示: SLF4J: Class path contains multiple SLF4J binding ...
- 美团动态线程池实践思路开源项目(DynamicTp),线程池源码解析及通知告警篇
大家好,这篇文章我们来聊下动态线程池开源项目(DynamicTp)的通知告警模块.目前项目提供以下通知告警功能,每一个通知项都可以独立配置是否开启.告警阈值.告警间隔时间.平台等,具体代码请看core ...
- php+gd库的源码安装
php+gd库的源码安装 PHP+GD安装 一.下载软件 gd-2.0.35.tar.gz http://www.boutell.com/gd/ jpegsrc.v6b. ...
随机推荐
- java——封装
java--封装 java--封装1 封装的理解和好处2 封装的事项实现步骤3 将构造器和setXx结合4 this和super区分 1 封装的理解和好处 隐藏实现细节:[方法(连接数据库)<- ...
- 基于Apache Hudi在Google云构建数据湖平台
自从计算机出现以来,我们一直在尝试寻找计算机存储一些信息的方法,存储在计算机上的信息(也称为数据)有多种形式,数据变得如此重要,以至于信息现在已成为触手可及的商品.多年来数据以多种方式存储在计算机中, ...
- hutool工具类常用API整理
0.官网学习地址 https://www.hutool.cn/ 1.依赖 <dependency> <groupId>cn.hutool</groupId> < ...
- synchronized有几种用法?
在 Java 语言中,保证线程安全性的主要手段是加锁,而 Java 中的锁主要有两种:synchronized 和 Lock,我们今天重点来看一下 synchronized 的几种用法. 用法简介 使 ...
- POJ - 1321 A - 棋盘问题
A - 棋盘问题 http://poj.org/problem?id=1321 思路:不能搞双重循环嵌套,要注意可以跳过某行 代码 #include <cstdio> #include & ...
- 如何调试手机上的网页以及基于Cordova/Phonegap的Hybrid应用
开发手机页面以及Hybird应用时,调试曾经是个老大难问题,不时需要用写log等方式曲线救国. 实际上,Chrome和Android(需要4.4+版本)已经提供了不亚于电脑版本的调试功能,只是看样子还 ...
- Java语言学习day23--7月29日
今日内容介绍1.构造方法2.this关键字3.super关键字4.综合案例 ###01构造方法引入 * A:构造方法的引入 在开发中经常需要在创建对象的同时明确对象的属性值,比如员工入职公司就要明确他 ...
- 联想Y7000安装 debian10 wifi 不好使解决方法
联想Y7000安装 debian10 wifi 不好使解决方法 wifi硬件高通QCA9377 把联想驱动加入黑名单 文件路径 /etc/modprobe.d/blacklist.conf 添加内容 ...
- 全栈交叉编译X86完成过程经验分享
1 CMAKE的交叉编译配置 主要是C和C++编译器的配置和SYSROOT的配置. set (CMAKE_SYSTEM_NAME "Linux") set (CMAKE_SYSTE ...
- 6.1 SHELL脚本
6.1 SHELL脚本元素 第一行的脚本声明(#!)用来告诉系统使用哪种Shell解释器来执行该脚本: 第二行的注释信息(#)是对脚本功能和某些命令的介绍信息,使得自己或他人在日后看到这个脚本内容时, ...