什么是zlog
先了解什么是zlog
zlog是一个高可靠性、高性能、线程安全、灵活、概念清晰的纯C日志函数库
官网:一个可靠的纯C日志库 zlog library homepage (hardysimpson.github.io)
中文文档: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