zlog 日志库使用简介
文章目录
一、zlog 简介
1.1 zlog 特性
zlog
是一个高性能、线程安全、运行时可以手动或自动刷新配置文件的纯C
日志函数库(仅依赖 C 语言标准库,头文件中仅需要stdio.h、 stdarg.h
)- 支持多个用户定义的输出(控制台、日志文件、内存缓冲区等),每个输出都有自己的报告阈值级别
- 多线程和多进程环境下保证 安全转档
- 日志格式可以定制设置:时间戳-日志等级-线程/进程号-文件名-行号-函数-日志内容 等
- zlog 的其它优点:
- 可以同时输出不同类别记录器的日志
- 可以同时输出同一类别不同等级、不同格式、不同输出地址的日志
- 可以通过通配符进行输出期望的日志记录
- 通配符
*
:*.error
,匹配各个类别记录器中的错误信息;detection.*
,匹配detection
记录器中所有级别的信息 - 通配符
_
:plate_.error
,匹配以plate
开头记录器(plate、plate_cls、plate_rec 等
)中的错误信息 - 通配符
!
:匹配那些没有找到规则的分类
- 通配符
demo 或应用
起来后,只需更改配置文件
即可实现改变日志等级进行debug
- 本地调试:demo 不用重新编译,只需更改配置文件中的日志等级,重新运行 demo 即可(其它实现方式:设备初始化时读取环境变量, 涉及到
s_level = genenv("LOG_LEVEL") 、i_level = atoi(s_level) l_level = (LogLevel)i_level
) - 业务调试:业务起来后,此时只有事先在配置文件中设置了每写日志多少次自动重载(
zlog_reload
)一次参数,才能使得更改后的日志等级生效(其它实现方式:linux 系统可通过OSP
/ Android 系统可通过LogCat
抓取日志)
- 本地调试:demo 不用重新编译,只需更改配置文件中的日志等级,重新运行 demo 即可(其它实现方式:设备初始化时读取环境变量, 涉及到
1.2 zlog 三大模块(Category、Format、Rule)介绍
- 分类(Category):用于区分不同类别的记录器。代码中的分类变量的名字是一个字符串,在一个程序里面可以通过获取不同的分类名的
category
用来后面输出不同分类的日志,用于不同的目的 - 格式(Format):用来描述输出日志的格式,比如
时间戳-日志等级-线程/进程号-文件名-行号-函数-日志内容
等 - 规则(Rule):把
分类、日志级别、输出到哪里、格式
组合起来,决定一条代码中的日志是否输出,输出到哪里,以什么格式输出
二、zlog 编译
2.1 linux 下的编译
git clone https://github.com/HardySimpson/zlog.git
cd zlog
make -j16 # 编译,真正的 makefile 位于 src/makefile
sudo make install -j16 # 安装,默认安装在 /usr/local
# 也可以安装在指定目录
cd zlog
mkdir build_linux64
make -j16 # 编译,真正的 makefile 位于 src/makefile
make PREFIX=../build_linux64 install -j16 # 因为 makefile 位于 src 下面,所以是 ../
# make install 过程
make[1]: 进入目录“/home/manzp/zlog/src”
mkdir -p ../build_linux64/include ../build_linux64/lib ../build_linux64/bin
cp -a zlog.h ../build_linux64/include
cp -a zlog-chk-conf ../build_linux64/bin
cp -a libzlog.so ../build_linux64/lib/libzlog.so.1.2
cd ../build_linux64/lib && ln -sf libzlog.so.1.2 libzlog.so.1
cd ../build_linux64/lib && ln -sf libzlog.so.1 libzlog.so
cp -a libzlog.a ../build_linux64/lib
make[1]: 离开目录“/home/manzp/zlog/src”
# 编译后在 build_linux64 目录下出现如下文件
├── bin
│ └── zlog-chk-conf # 日志配置文件检查工具,执行 ./zlog-chk-conf zlog.conf 进行配置检查
├── include
│ └── zlog.h # 头文件
└── lib
├── libzlog.a # 静态库
├── libzlog.so -> libzlog.so.1
├── libzlog.so.1 -> libzlog.so.1.2
└── libzlog.so.1.2 # 动态库
# 将动态库路径加入系统路径
sudo vim /etc/ld.so.conf
# 在打开的文件中加入如下路径
/usr/local/lib 或者 $zlog_root/build_linux64/lib
# 更新配置
sudo ldconfig
2.2 arm 下的编译
- 修改
src/makefile
里的cc
为交叉编译器的gcc
,还有一处ar
修改为交叉编译器的ar
(用于静态库的创建) 即可
# 1、arm 下的编译和使用
cd zlog
mkdir build_arm64
make CC=$cross_compile/bin/aarch64-linux-gnu-gcc -j16
make PREFIX=../build_arm64 install -j16 # 因为 makefile 位于 src 下面,所以是 ../
# 2、readelf -h 显示 elf 文件开始的文件头信息,从头信息可以看出系统的架构和 OS/ABI
# readelf -h libzlog.so
ELF 头:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
类别: ELF64
数据: 2 补码,小端序 (little endian)
Version: 1 (current)
OS/ABI: UNIX - System V
ABI 版本: 0
类型: DYN (共享目标文件)
系统架构: AArch64
版本: 0x1
入口点地址: 0x45e0
程序头起点: 64 (bytes into file)
Start of section headers: 415656 (bytes into file)
标志: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 7
Size of section headers: 64 (bytes)
Number of section headers: 35
Section header string table index: 34
# 3、readelf -d(dynamic) 显示动态段的信息,可以看出其依赖的动态库信息
# readelf -d libzlog.so
Dynamic section at offset 0x17d70 contains 27 entries:
标记 类型 名称/值
0x0000000000000001 (NEEDED) 共享库:[libpthread.so.0]
0x0000000000000001 (NEEDED) 共享库:[libc.so.6]
0x000000000000000e (SONAME) Library soname: [libzlog.so.1.2]
0x000000000000000c (INIT) 0x3b80
0x000000000000000d (FINI) 0x11b60
0x0000000000000019 (INIT_ARRAY) 0x27d60
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
2.3 windows 下的编译
三、zlog 配置文件介绍
zlog
主要通过配置文件实现对日志的控制,配置文件主要包含四大部分global、levels、formats、rules
- 注意:以
#
开头的代表注释;配置文件中对内存大小写不敏感
3.1 global
# 全局参数,可忽略不写
[global]
# 默认为 true,zlog_init() 将会严格检查配置文件中所有格式和规则,任何错误都会导致失败并且返回-1
# strict init = true
# zlog 每写日志多少次自动重载(zlog_reload)一次配置文件,默认值是 0,表示自动重载是关闭的。
# reload conf period = 0
# zlog 在堆上为每个线程申请的缓存最小和最大值
# 写日志的时候,如果单条日志长度大于缓存,缓存会自动扩充,直到到 "buffer max"
# 单条日志再长超过 "buffer max" 就会被截断
# buffer min = 1KB # 单个缓存的最小值,默认 1KB
# buffer max = 2MB # 单个缓存的最大值,默认 2MB,如果 buffer max 设置为 0,代表不限制缓存
# 指定了一个锁文件,用来保证多进程情况下日志的安全转档
# 默认 rotate lock file = self,在这种情况下,zlog 不会创建任何锁文件,用配置文件作为锁文件
# rotate lock file = /tmp/zlog.lock
# 缺省日志格式。默认值 "%d %V [%p:%F:%L] %m%n"
# 输出格式:2012-02-14 17:03:12 INFO [3758:test_hello.c:39] hello, zlog
# 可以参考 zlog 使用手册进行修改。
default format = "[%d] [%c:%V] [%p:%f:%L:%U] [%m]%n"
# 指定了创建日志文件的缺省访问权限,默认只允许当前用户读写
# file perms = 600
# zlog 每写日志多少次把缓存日志写入磁盘,次数是每条规则单独统计的,并且在 zlog_reload() 后会被清 0
# 默认值 0,表示由操作系统决定什么时候写入磁盘
# fsync period = 0
3.2 levels
- 一般默认设置为
Notice
等级:可以输出关于设备的基本信息、SDK
版本号、算法处理是否成功等信息,也会输出算法内部发生错误时的反馈信息;Debug
等级:可以输出关于模型的基本信息,比如模型输入/输出 tensor 的维度、对齐方式、步长、数据类型等Verbose
最高等级:可保存模型 forward 前后的输入、输出 tensor到文件中,输入是图像的话会保存成相应图像格式,输出一般保存成float
格式文件,以C*H*W
方式存储,这里主要是为了方便与浮点模型进行对比分析,排查模型精度问题
# 日志等级自定义,一般不使用,默认为 LOG_DEBUG
[levels]
TRACE = 10
CRIT = 130, LOG_CRIT
# zlog 日志等级,代码中所有大于配置文件日志等级的日志都会输出
typedef enum {
ZLOG_LEVEL_DEBUG = 20,
ZLOG_LEVEL_INFO = 40,
ZLOG_LEVEL_NOTICE = 60,
ZLOG_LEVEL_WARN = 80,
ZLOG_LEVEL_ERROR = 100,
ZLOG_LEVEL_FATAL = 120
} zlog_level;
3.3 formats
- 每个转换说明都是以百分号(
%
)打头的,后面跟可选的宽度修饰符,最后以转换字符结尾- 宽度修饰:符控制了这个字段的最大最小宽度、左右对齐等
- 转换字符:决定了输出什么数据,例如分类名、级别、时间日期、进程号等
# 缺省日志格式,默认值 "%d %V [%p:%F:%L] %m%n"
# 输出格式:2022-08-04 21:52:08 INFO [3758:test_hello.c:39] hello, zlog
# 可以参考 zlog 使用手册进行修改。
default format = "[%d] [%c:%V] [%p:%f:%L:%U] [%m]%n"
# 输出格式:[2022-08-04 21:57:08] [det:DEBUG] [157509:main.cpp:20:main] [hello, zlog]
%d # 打日志的时间,输出格式:2012-02-14 17:03:12
%c # 日志记录器的类别
%-5V # 日志级别要被左对齐,占 5 个字符宽,日志级别大写,如 DEBUG;如果是小 v,则小写,如 debug
%p # 进程 ID,来源于 getpid()
%t # 16 进制的线程 ID,来源于pthread_self(),大写的 T 则以长整型输出
%F # 源代码文件名,来源于__FILE__宏,在某些编译器下 __FILE__是绝对路径,用 %f 来去掉目录只保留文件名
%L # 源代码行数,来源于__LINE__宏
%U # 调用函数名,来自于__func__(C99)或者__FUNCTION__(gcc)
%m # 用户输出的日志具体内容
%n # 换行符
3.4 rules
# 描述了日志是怎么被过滤、格式化以及被输出的,不写的话就没有日志输出;当代码中的日志等级大于各条规则的等级时,则日志会输出
# 格式:(category).(level) (output), (options, optional); (format name, optional)
# 1、日志是怎么被过滤:结合通配符进行配置,日志级别对大小写不敏感
aa.debug # 代码内等级>=debug
aa.=debug # 代码内等级==debug
aa.!debug # 代码内等级!=debug
# 2、输出:主要有两种 1、输出到控制台 stdout;2、输出到文件(支持相对路径和绝对路径)进行保存,支持转档(防止单个文档过大硬盘被撑爆)
每天生成一个日志:"aa.%d(%F).log",输出如 aa.2012-08-02.log、aa.2012-08-03.log
# 3、格式化:用 gloabl 中指定的 default format 即可
[rules]
det.DEBUG "./log/class-debug.log", 10MB * 5 ~ "./log/class-debug-#2r.log"
det.INFO "./log/class-info.log", 10MB * 5 ~ "./log/class-info-#2r.log"
det.INFO >stdout
# 日志文件的说明:
log 目录需要提前建立
* 左侧表示单个日志文件的大小(超过此大小会转档),*右侧表示保留多少个存档,5 表示 5 个存档(0 代表不删除任何存档文件)
~ 右侧为转档规则:可选 ‘#s 或 #r’,r 代表 Rolling(class-info.log 是最新日志, class-info-00.log 是次新日志,class-info-04.log 是最老日志)
2r 的意思是序号的长度最少为 2 位,从 00 开始编号,Rolling 转档
四、zlog 使用
- zlog 的使用只需包含头文件
#include "zlog.h"
,并给出配置文件即可;链接时主要加上libpthread.so
库文件 - 代码示例如下:
#include "zlog.h"
int main(int argc, char **argv) {
// 读取配置信息到内存
int ret_init = zlog_init("../test_hello.conf");
if (ret_init) {
printf("init failed\n");
return -1;
}
// 初始化一个日志记录器
zlog_category_t *c = zlog_get_category("class");
// 如果成功,返回 zlog_category_t 的指针;失败返回 NULL
if (!c) {
printf("get category failed\n");
zlog_fini();
return -1;
}
// 记录日志,zlog_fatal/error/warn/notice/info/debug
zlog_info(c, "hello, zlog");
zlog_fini(); // 释放资源
return 0;
}
// 控制台输出如下信息,同时 class-info.log 中也记录了此日志
[2022-08-05 11:31:59] [class:INFO] [166239:main.cpp:18:main] [hello, zlog]
// test_hello.conf 配置信息如下
[global]
default format = "[%d] [%c:%V] [%p:%f:%L:%U] [%m]%n"
[rules]
class.info "../log/class-info.log", 10KB*5 ~ "../log/class-info-#2r.log"
class.info >stdout
// dzlog 是忽略分类(zlog_category_t)的一组简单 zlog 接口
// 用户不需要操心创建、存储、传输zlog_category_t类型的变量
// 它采用内置的一个默认分类,这个分类置于锁的保护下
#include "zlog.h"
int main(int argc, char** argv)
{
int rc;
rc = dzlog_init("test_default.conf", "my_cat");
if (rc) {
printf("init failed\n");
return -1;
}
dzlog_info("hello, zlog");
zlog_fini();
return 0;
}
- CmakeLists.txt 配置示例如下:
cmake_minimum_required(VERSION 3.22)
project(zlog_demo)
set(CMAKE_CXX_STANDARD 11)
# 设置头文件搜索路径
include_directories(./include)
# 设置库文件搜索路径
#link_directories(/usr/local/lib)
# 设置源文件地址
#aux_source_directory(./src SRC_LIST)
# 设置生成可执行文件
add_executable(zlog_demo main.cpp) # $SRC_LIST
# 将第三方库链接在一起
target_link_libraries(zlog_demo zlog pthread)
五、参考资料
1、Python 异常处理、pdb调试&logging
2、https://github.com/armink/EasyLogger
3、https://github.com/HardySimpson/zlog
4、zlog 使用手册(*****)
5、嵌入式大杂烩周记:zlog(*****)
6、【C语言开源库】C语言开源库zlog的使用
7、日志系统模块基础、C语言实现一个日志模块、zlog日志模块基础