前言

这篇文章将着重于分析字节跳动开源的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个的日志输出级别的函数可使用:InfoDebugNoticeWarnErrorFatalTrace

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

可以看到logger实例默认的日志打印级别是LevelInfoklog通过常量计数器定义了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原生日志库logOutput()函数,打印日志(这一部分在上一篇分析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,因此日志输出操作被忽略:

小结

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

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

字节开源RPC框架Kitex的日志库klog源码解读的更多相关文章

  1. 标准库path源码解读

    先看标准库 作用:关于路径的一些实用操作 https://github.com/golang/go/blob/master/src/path/path.go 源码地址 func IsAbs func ...

  2. Go RPC 框架 KiteX 性能优化实践 原创 基础架构团队 字节跳动技术团队 2021-01-18

    Go RPC 框架 KiteX 性能优化实践 原创 基础架构团队 字节跳动技术团队 2021-01-18

  3. 各大开源rpc 框架 比较

    各大开源rpc 框架 比较   1. 前言 随着现在互联网行业的发展,越来越多的框架.中间件.容器等开源技术不断地涌现,更好地来服务于业务,解决实现业务的问题.然而面对众多的技术选择,我们要如何甄别出 ...

  4. 如何优雅的阅读 GitHub 上开源 js 框架和库的源码

    如何优雅的阅读 GitHub 上开源 js 框架和库的源码 step 先总后分,即先了解一下啊框架的大体架构,又一个全局的认识,在选择某些和感兴趣的部分,仔细阅读,各个击破: 带着问题阅读,用到了什么 ...

  5. 微前端框架 之 qiankun 从入门到源码分析

    封面 简介 从 single-spa 的缺陷讲起 -> qiankun 是如何从框架层面解决 single-spa 存在的问题 -> qiankun 源码解读,带你全方位刨析 qianku ...

  6. Go 源码解读|如何用好 errors 库的 errors.Is() 与 errors.As() 方法

    前言 快一个月没有更新技术文章了,这段时间投注了较多的时间学习字节的开源项目 Kitex/Hertz ,并维护一些简单的 issue ,有兴趣的同学也可以去了解: https://www.cloudw ...

  7. 源码解读SLF4J绑定日志实现的原理

    一.导读 我们使用log4j框架时,经常会用slf4j-api.在运行时,经常会遇到如下的错误提示: SLF4J: Class path contains multiple SLF4J binding ...

  8. 美团动态线程池实践思路开源项目(DynamicTp),线程池源码解析及通知告警篇

    大家好,这篇文章我们来聊下动态线程池开源项目(DynamicTp)的通知告警模块.目前项目提供以下通知告警功能,每一个通知项都可以独立配置是否开启.告警阈值.告警间隔时间.平台等,具体代码请看core ...

  9. php+gd库的源码安装

    php+gd库的源码安装     PHP+GD安装   一.下载软件 gd-2.0.35.tar.gz          http://www.boutell.com/gd/ jpegsrc.v6b. ...

随机推荐

  1. IO流入门

    @ 目录 总结内容 1. IO流是什么 2. 字符流和字节流 3. File常用API(前面类型为返回类型) 4. 编码转换 5. IO流实现流程 6. 输入输出流简单实现 7. 输入输出流简单实现 ...

  2. vuecli中配置webpack加快打包速度

    webpack4中webpack 的DllPlugin插件可以将常见的库文件作为dll文件来,每次打包的时候就不用再次打包库文件了. 但是游鱼西在vuecli中已经去除这个选项,意识到带来的打包速度提 ...

  3. FastAPI(七十二)实战开发《在线课程学习系统》接口开发-- 留言列表开发

    之前我们分享了FastAPI(七十一)实战开发<在线课程学习系统>接口开发-- 查看留言,这次我们分享留言列表开发. 列表获取,也需要登录,根据登录用户来获取对应的留言.逻辑梳理如下. 1 ...

  4. Warmup小记

    什么是warmup 热身,在刚刚开始训练时以很小的学习率进行训练,使得网络熟悉数据,随着训练的进行学习率慢慢变大,到了一定程度,以设置的初始学习率进行训练,接着过了一些inter后,学习率再慢慢变小: ...

  5. JavaWeb入门day10-JSP

    JSP 什么是JSP Java Server Pages:Java服务器端页面,也和Servlet一样,用于动态Web技术 最大特点: 写JSP就像在写HTML 区别 HTML只给用户提供静态数据 J ...

  6. Java学习day33

    线程池: 背景:经常创建和销毁.使用量特别大的资源,比如并发情况下的线程,对性能影响很大 思路:提前创建好多个线程.实现重复利用. 好处:提高响应速度,减少了创建新线程的时间:降低资源消耗,重复利用线 ...

  7. 生成二维码,并且保存,指定位置的view成图片,并且保存到本地相册

    效果图: 保存的图片效果是: 保存到本地的,是整个视图,不只是单单的二维码的图片, 在了解的一番过程之后,我知道了, 1.首先要去获取保存图片的写入权限:(使用 https://github.com/ ...

  8. axios 内存泄漏

    有一个项目需要post后台几万条数据,每次只能请求三五条,所以只能在每次请求成功后自调用这个请求函数. 但这样请求了成千上万次后,浏览器会崩溃并报出 out of Memory 错误,经查询,这是内存 ...

  9. Java语言学习day06-7月05日

    今日内容介绍流程控制语句之循环语句循环高级###10for循环_1 * A: for循环_1 * a: 使用格式 for(初始化变量 ; 条件 ; 增量){ 循环体; } * b: 各模块解释 初始化 ...

  10. LVM从VG中删除PV及删除未知PV

    当我们的硬盘发被删除掉了,我们的PV卷会变成[unknown] 一.首先我们要备份我们的文件,然后再删除lv分区 二. VG中去除PV unknown device: