18、zlog 日志库使用简介

本文介绍了zlog日志库的特性、编译方法、配置详解、使用示例及配置文件规则。涵盖了Linux、ARM和Windows平台的编译步骤,以及如何通过配置文件灵活控制日志输出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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 抓取日志)

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日志模块基础

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值