zlog 使用笔记

zlog使用笔记,包含源码编译和移植、API详解、配置文件详解,穿插使用示例及个人总结。有需要的朋友可以留言邮箱,提供PDF版本、源码和官方文档
摘要由CSDN通过智能技术生成

简介

GitHub 网址:GitHub - HardySimpson/zlog: A reliable, high-performance, thread safe, flexsible, clear-model, pure C logging library.

zlog 是一个精简、可靠、高性能、线程安全、灵活、模型清晰、纯 C 日志库。

事实上,在 C 的世界里面没有特别好的日志函数库(就像JAVA里面的的log4j,或者C++的log4cxx)。syslog 又专为系统使用而设计,速度慢,功能比较单调。

zlog 在效率、功能、安全性上大大超过了 log4c,并且是用 c 写成的,具有比较好的通用性。

zlog 的目标是成为一个简而精的日志函数库,不会直接支持日志内容的过滤和解析、网络输出、数据库写入等特性。原因很明显,日志库是被应用程序调用的,所有花在日志库上的时间都是应用程序运行时间的一部分,而上面说的这些操作都很费时间,会拖慢应用程序的速度。这些事儿应该在别的进程或者别的机器上做。如果你需要这些特性,我建议使用 rsyslog、zLogFabric、Logstash,这些日志搜集、过滤、存储软件,当然这是单独的进程,不是应用程序的一部分。

zlog有这些特性:

  • syslog 模型,优于 log4j 模型
  • 日志格式自定义
  • 多个输出目标,包括静态文件路径、动态文件路径、stdout、stderr、syslog、用户定义的输出
  • 运行时手动或自动刷新配置(安全)
  • 高性能,每秒 250'000 个日志,比使用 rsyslogd 的 syslog(3) 快约 1000 倍
  • 用户可自定定义日志级别
  • 线程安全和进程安全的日志文件轮换
  • 微秒级精度
  • dzlog,一个默认的类别日志 API,便于使用
  • MDC,log4j 样式的键值映射
  • 可自调试,可在运行时输出ZLOG的自调试和错误日志
  • 没有外部依赖,只是基于 POSIX 系统和符合 C99 的 vsnprintf。

兼容性说明

  1. zlog 是基于 POSIX 的。目前我手上有的环境只有AIX和linux。在其他的系统下(FreeBSD, NetBSD, OpenBSD, OpenSolaris, Mac OS X...)估计也能行,有问题欢迎探讨。
  2. zlog 使用了一个C99兼容的vsnprintf。也就是说如果缓存大小不足,vsnprintf将会返回目标字符串应有的长度(不包括’\0’)。如果在你的系统上vsnprintf不是这么运作的,zlog就不知道怎么扩大缓存。如果在目标缓存不够的时候vsnprintf返回-1,zlog就会认为这次写入失败。幸运的是目前大多数c标准库符合C99标准。glibc 2.1,libc on AIX, libc on freebsd...都是好的,不过glibc2.0不是。在这种情况下,用户需要自己来装一个C99兼容的vsnprintf,来crack这个函数库。我推荐ctrioC99-snprintf
  3. 有网友提供了如下版本,方便其他平台上安装编译。但个人建议,如果能使用源码编译最好不用其他版本,因为这些可能不是最新版。

auto tools版本: GitHub - bmanojlovic/zlog: A reliable, high-performance, thread safe, flexsible, clear-model, pure C logging library.

cmake版本: GitHub - lisongmin/zlog: A reliable, high-performance, thread safe, flexsible, clear-model, pure C logging library.

windows版本: GitHub - lopsd07/WinZlog: Zlog on Windows

编译和使用

zlog 日志库 并非只有几个文件,一般不好包含到项目文件中一起编译,通常都是编译成动态库的形式使用;

【在 Linux 系统中编译】

$ tar -zxvf zlog-latest-stable.tar.gz
$ cd zlog-latest-stable/
$ make
$ sudo make install
or
$ make PREFIX=/usr/local/
$ sudo make PREFIX=/usr/local/ install

PREFIX 表示 zlog 的安装目标。安装后,刷新动态链接器以确保程序可以找到 zlog 库。其他系统类似。
$ sudo vi /etc/ld.so.conf
/usr/local/lib
$ sudo ldconfig

测试:我们将安装路径设置源码目录下的 __install 目录(新建的测试目录),

$ mkdir __install
$ make PREFIX=$(pwd)/__install
$ sudo make PREFIX=$(pwd)/__install instal

结果如下图所示:

【交叉编译】

修改源码目录下 src/makefile 里的 cc 为交叉编译器的 gcc,如果有必要再修改 ar 为交叉编译器的 ar,如下图所示:

这里我以 RK3568 板卡供应商提供的编译器为例,执行上述测试步骤,结果如下:

【应用程序调用和链接zlog】

应用程序使用 zlog 很简单,只要在C文件里面加一行 #include "zlog.h"

链接 zlog 需要 pthread 库,例如:

$ cc app.c -o app -lpthread -I/usr/local/include -L/usr/local/lib -lzlog
或者
$ cc -c -o app.o app.c -I/usr/local/include #编译
$ cc -o app app.o -L/usr/local/lib -lzlog -lpthread #链接

zlog的使用流程

首先要知道 zlog 中有 3 个重要的概念:

分类(Category):类别用于区分不同类型的日志,它用一个字符串来说明。

格式(Format):用来描述输出日志的格式,比如是否有带有时间戳,是否包含文件位置信息等。

规则(Rule):把分类、级别、输出文件、格式组合起来,决定一条代码中的日志是否输出,输出到哪里,以什么格式输出。

上述的分类、格式、规则等都是写在配置文件中的,配置文件由用户自己编写,我们写一个简单的配置文件 zlog.conf 示例:

[formats]
simple = "%d %m%n"

[rules]
my_cat.DEBUG    >stdout; simple

这个配置文件指定了类别为 “my_cat” 且级别 >= DEBUG 的日志将输出到标准输出,格式为 simple。

我们写个C程序测试一下:

#include <stdio.h> 
#include "include/zlog.h"

int main(int argc, char** argv)
{
    int rc;
    zlog_category_t *c;
    //根据配置文件初始化
    rc = zlog_init("zlog.conf");
    if(rc){ 
        printf("init failed\n"); 
        return -1; 
    }
    //将类别信息读取到内存中
    c = zlog_get_category("my_cat");
    if(!c){
        printf("get cat fail\n");
        zlog_fini();
        return -2;
    }
    //输出日志信息
    zlog_info(c, "hello, zlog");
    zlog_fini();
    return 0;
} 

编译运行结果如下:

由此可见,zlog 的使用就是先编写配置文件,然后调用日志库的 API 即可。详细的配置和 API 的介绍见下文。

zlog API 详解

zlog 的配置文件是核心,但在学习配置文件之前应该要知道 zlog 库有哪些接口?如何使用?了解了 API 之后再去学习配置文件,结合起来事半功倍。

【初始化和清理操作】

  • int zlog_init(const char *config);

zlog_init() 从配置文件 config 中读取配置。

如果 config 为空,它会查找环境变量 $ZLOG_CONF_PATH 以找到配置文件。如果 $ZLOG_CONF_PATH 也为空,则所有日志将以内部格式输出到 stdout。

每个进程只有第一次调用 zlog_init() 有效,后续调用将失败且不执行任何操作。

成功返回 0,失败返回 -1。详细错误会被写在由环境变量 $ZLOG_PROFILE_ERROR 指定的错误日志里面。

  • int zlog_reload(const char *config);

zlog_reload() 从配置文件 config 中重载配置,并根据这个配置文件来重计算内部的分类规则匹配、重建每个线程的缓存、并设置原有的用户自定义输出函数。

可以在配置文件发生改变后调用这个函数。这个函数使用次数不限。

如果 config 为 NULL,会重载上一次 zlog_init() 或者 zlog_reload() 使用的配置文件。

如果 zlog_reload() 失败,上一次的配置依然有效。所以 zlog_reload() 具有原子性。

成功返回 0,失败返回 -1。详细错误会被写在由环境变量 $ZLOG_PROFILE_ERROR 指定的错误日志里面。

  • void zlog_fini(void);

zlog_fini() 清理所有 zlog API 申请的内存,关闭它们打开的文件。使用次数不限。

【分类(Category)操作】

typedef struct zlog_category_s  zlog_category_t
zlog_category_t *zlog_get_category(const char *cname);

zlog_get_category() 从 zlog 的全局分类表里面找到分类,用于以后输出日志。如果没有的话,就建一个。然后它会遍历所有的规则,寻找和 cname 匹配的规则并绑定。

配置文件规则中的分类名匹配 cname 的规则如下:

  1. * 匹配任意 cname。
  2. 以下划线_结尾的分类名同时匹配本级分类和下级分类。例如 aa_ 匹配 aa, aa_, aa_bb, aa_bb_cc 这几个 cname。
  3. 不以下划线_结尾的分类名精确匹配 cname。例如 aa_bb 匹配 aa_bb 这个 cname。
  4. ! 匹配目前还没有规则的 cname。

这个匹配规则在配置文件中还会讲述到!

每个 zlog_category_t * 对应的规则,在 zlog_reload() 的时候会被自动重新计算。不用担心内存释放,zlog_fini() 最后会清理一切。

【日志输出函数及宏】

void zlog(zlog_category_t * category,
 const char *file, size_t filelen,
        const char *func, size_t funclen,
 long line, int level,
            const char *format, ...) ZLOG_CHECK_PRINTF(8,9);
void vzlog(zlog_category_t * category,
 const char *file, size_t filelen,
        const char *func, size_t funclen,
  long line, int level,
        const char *format, va_list args);
void hzlog(zlog_category_t * category,
 const char *file, size_t filelen,
        const char *func, size_t funclen,
 long line, int level,
        const void *buf, size_t buflen);

这3个函数是实际写日志的函数,输入的数据对应于配置文件中的 %m。

category 通过调用 zlog_get_category() 获得;

file 和 line 填写为__FILE__和__LINE__这两个宏,标识日志是在哪里发生的;

func 填写为 __func__ 或者 __FUNCTION__,如果编译器支持的话,如果不支持,就填写为"";

level 是一个整数,应该是在下面几个里面取值。

typedef enum {
    ZLOG_LEVEL_DEBUG = 20,
    ZLOG_LEVEL_INFO = 40,
    ZLOG_LEVEL_NOT
  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值