Skynet第三方之:zlog日志

JavenLaw

什么是zlog

先了解什么是zlog

zlog是一个高可靠性、高性能、线程安全、灵活、概念清晰的纯C日志函数库

官网:一个可靠的纯C日志库 zlog library homepage (hardysimpson.github.io)

GitHub:HardySimpson/zlog: A reliable, high-performance, thread safe, flexsible, clear-model, pure C logging library. (github.com)

中文文档:zlog使用手册 (hardysimpson.github.io)


Skynet日志存在的问题

为什么需要使用zlog是我们首选需要回答的问题

回顾Skynet的日志,主要有《Skynet源码之:service_logger》和《Skynet源码之:日志打印》,但是以上2者都存在问题

《Skynet源码之:service_logger》:同个进程内,所有的服务需要打印日志的话,就不能再使用printf(c层面),print(lua层)

甚至标准输入/输出都不再使用(详细见《Skynet源码之:守护进程》中的 redirect_fds() 输出重定向)

因此每个服务都需要调用 skynet.error() 函数来打印日志,这又会造成2个问题:

​ 假设某个节点内,开启了1000个服务,每个服务可能都需要一定数量的打印

​ 因此所有服务都会给这个节点内的logger服务发送大量的消息

​ 这必然会造成logger服务占用工作线程来打印日志,造成性能损失

​ 因为Skynet并没有对日志的划分做一些设计

​ 因此在实际的工作中,例如日志划分、转存、格式几乎没有好的支持

​ 即在实际工作项目中,这个日志是很难用的

所以我们需要引入一个第三方的日志库zlog来帮助我们实现日志的划分、存档、格式打印等功能


如何在Skynet中引入zlog

很显然,zlog是一个纯c的日志库,我们需要封装一下给lua层的服务调用

做法就是c-lua的动态库的制作,一般为

​ 通过luaopen_zlog(lua_State *L)函数在lua层调用了zlog的c函数

​ (详细的c-lua的交互还是直接看代码比较清晰)

最后我们得到一个zlog.so动态库,在lua层直接获取

​ local zlog = require “zlog”

然后根据skynet中 local c = skynet.core 中同样的用法

​ c = c.command()

就能直接在lua层使用c的函数


zlog的封装使用

zlog的实质使用并没有那么简单,它还需要一些初始化步骤

​ 1,配置好zlog.conf,里面包含日志的格式,分档的规则,日志打印级别等

​ 2,在进程启动的时候,需要调用zlog.init()进程初始化(一般在main.lua服务中初始化)

​ 3,进行一些封装,使得更容易使用,例如添加每个服务的地址,严重日志加颜色等

问题:

我在一个进程内开启了多个lua虚拟机,并且在其中A虚拟机中调用了zlog.init()函数

为什么我就可以直接在B虚拟机中直接使用在zlog了?

并且在A,B,C中打印的日志,是根据时间顺序排列的?

(A虚拟机本质就是skynet节点启动的一个服务,例如main.lua)

(B虚拟机本质就是由main服务,通过skynet.newservice() 启动的另一个服务)

(B虚拟机此时不再需要调用zlog.init(),直接就可以使用在log了)

这是因为 zlog 作为一个日志库,在初始化时通常会设置全局的配置和状态,而这些配置和状态是跨越整个进程的

具体来说,以下是一些可能的原因和机制:

全局状态:zlog 初始化时会设置一些全局变量或状态,这些状态在整个进程的上下文中是共享的

​ 当你在一个 Lua 虚拟机(例如 A 虚拟机)中调用 zlog.init(),它会初始化这些全局状态

​ 而这些状态在其他 Lua 虚拟机(例如 B 虚拟机)中也是可见的

单例模式:zlog 很可能采用了单例模式来管理日志记录

​ 这意味着无论你在多少个 Lua 虚拟机中使用 zlog

​ 它们实际上都在使用同一个日志管理器实例

共享资源:在进程内,多线程或多虚拟机可以共享资源

​ 由于 zlog 使用的是共享资源(例如文件、网络连接等)来记录日志

​ 这些资源在多个 Lua 虚拟机中都是共享的

线程安全:zlog 库可能实现了线程安全的日志写入机制,这样多个虚拟机或线程在记录日志时

​ 不会发生竞争条件,日志会根据时间顺序正确排列

在实际项目中,我们会根据自己的需要,再次封装一下zlog

设想一下这样的情景:

​ 某个进程内,开启了500个服务,每个服务都调用zlog.info()进行日志输出

​ 请问最后在日志中,你能分辨那条日志属于那个服务的吗?

​ 很显然,我们需要在每个服务的日志前面,添加一些信息

ok, core = pcall(require, "skynet.core")
if ok then
  -- 通过lua-c的交互,从c层面获取到本服务的句柄handle
  self = string.format("%s[:%08x] ", _prefix, tonumber("0x" .. string.sub(core.command("REG"), 2)))
end

-- 把服务的句柄handle和真实日志,连接起来,再发送给zlog
_info(self .. tostring((...))) 

-- 另外我们可以加一些颜色标记,是的日志具有颜色区分

更多细节看logger.lua