C语言日志类库 zlog 使用指南(第五章 配置文件)

第五章 配置文件

zlog库的大多数操作都依赖于配置文件:在哪里输出日志、如何轮转日志文件、如何格式化输出等。配置文件使用专用的DSL(领域特定语言)来控制库的操作。以下是一个示例的zlog.conf配置文件:

bash 复制代码
# 注释
[global]
strict init = true
reload conf period = 1M
buffer min = 1024
buffer max = 2MB
rotate lock file = /tmp/zlog.lock
default format = "%d.%ms %-6V (%c:%F:%L) - %m%n"
file perms = 600
fsync period = 1K

[levels]
TRACE = 10
CRIT = 130, LOG_CRIT

[formats]
simple = "%m%n"
normal = "%d(%F %T) %m%n"

[rules]
default.* >stdout; simple
*.* "%12.2E(HOME)/log/%c.log", 1MB*12; simple
my_.INFO >stderr;
my_cat.!ERROR "/var/log/aa.log"
my_dog.=DEBUG >syslog, LOG_LOCAL0; simple
my_mice.* $user_define;

配置文件的结构如下:

  1. [] 表示一个部分的开始,各部分的顺序是固定的,按照 global-levels-formats-rules 的顺序。

关于单位的注意事项:

  • 当需要指定内存大小或大数字时,可以使用常见的单位形式,如 1k、5GB、4M 等:
  • 1k 表示 1000 字节
  • 1kb 表示 1024 字节
  • 1m 表示 1000000 字节
  • 1mb 表示 1024*1024 字节
  • 1g 表示 1000000000 字节
  • 1gb 表示 1024*1024*1024 字节
  • 单位不区分大小写,所以 1GB、1Gb、1gB 是一样的。

关键配置项解释:

[global] 部分:
  • strict init:设为 true 时,严格初始化。
  • reload conf period:配置文件重新加载周期。
  • buffer minbuffer max:缓冲区的最小和最大大小。
  • rotate lock file:日志旋转锁文件的位置。
  • default format:默认的日志输出格式。
  • file perms:文件权限设置。
  • fsync period:同步周期。
[levels] 部分:
  • 定义日志级别,如 TRACE 和 CRIT 等。
[formats] 部分:
  • 定义日志输出格式,如 simple 和 normal 等。
[rules] 部分:
  • 定义日志输出规则。例如:
  • default.* >stdout; simple:默认情况下,所有日志输出到标准输出,使用 simple 格式。
  • my_dog.=DEBUG >syslog, LOG_LOCAL0; simple:将 my_dog 分类的 DEBUG 日志输出到 syslog,使用 simple 格式。

通过这些配置,用户可以灵活地控制 zlog 库的日志输出行为。

5.1 全局

以下是 zlog 用户指南中关于全局配置部分的总结:

全局(Global)配置节

全局配置节以 [global] 开头,这一部分可以省略。每个配置项的语法是 (key) = (value)

1. strict init
  • strict init = true:zlog_init() 会严格检查所有格式和规则的语法,任何错误都会导致 zlog_init() 失败并返回 -1。
  • strict init = false:zlog_init() 会忽略格式和规则的语法错误。默认值为 true。
2. reload conf period
  • 自动重新加载配置文件的周期(以每个进程的日志次数为单位)。到达指定次数后,自动调用 zlog_reload()。
  • 默认值是 0,表示从不自动重新加载。
3. buffer min 和 buffer max
  • 每个线程的日志缓冲区大小:
  • buffer min:初始化时分配的缓冲区大小。
  • buffer max:扩展缓冲区的最大值。超过此值的日志内容会被截断。
  • 默认:buffer min是 1K,buffer max是 2MB。
4. rotate lock file
  • 用于在多进程环境中安全地旋转日志文件的锁文件路径。
  • 默认值为 self,表示使用配置文件本身作为锁文件。
5. default format
  • 未指定格式的规则使用的默认格式。
  • 默认值:"%d %V [%p:%F:%L] %m%n",输出示例:2012-02-14 17:03:12 INFO [3758:test_hello.c:39] hello, zlog
6. file perms
  • 创建日志文件时的文件权限(受用户的 umask 影响)。
  • 默认值为 600,意味着仅用户可以读取和写入。
7. fsync period
  • 每个规则(仅限文件)记录一定次数的日志后,调用 fsync(3) 告诉操作系统立即将数据写入磁盘。
  • 默认值为 0,表示由操作系统自行决定何时刷新输出缓冲区。

示例

  • fsync period 的效果可以显著影响程序的性能和数据安全性。频繁调用 fsync 会大大降低性能,但能确保日志迅速写入磁盘。
shell 复制代码
$ time ./test_press_zlog 1 10 100000

real	0m1.806s
user	0m3.060s 
sys	 0m0.270s

 

$ wc -l press.log  

1000000 press.log  

 

$ time ./test_press_zlog 1 10 100000 #fsync period = 1K

real	0m41.995s 
user	0m7.920s 
sys	 0m0.990s

 

$ time ./test_press_zlog 1 10 100000 #fsync period = 10K

real	0m6.856s 
user	0m4.360s 
sys	 0m0.550s

通过这些全局配置,您可以根据实际的需求调整 zlog 的行为,以便更好地适应不同的日志处理场景。

5.2 日志级别 (Levels)

  • 定义部分 :此部分以 [levels] 开始,允许用户定义应用程序的日志级别。

  • 匹配宏:这些值需要与源文件中的用户定义宏匹配。

  • 语法

    (level string) = (level int), (syslog level, optional)

例如:

DEBUG = 1, LOG_DEBUG
  • 级别范围level int 应在 [1,253] 范围内,数值越高,重要性越大。syslog 级别是可选的,如果未设置,则使用 LOG_DEBUG

5.3 格式 (Formats)

  • 定义部分 :此部分以 [formats] 开始,用户可以定义首选的日志模式。

  • 语法

    (name) = "(actual formats)"

例如:

detailed = "%d(%m-%d %T) %-5V [%p:%F:%L] %m%n"
  • 用途name 将在接下来的 [rules] 部分中使用。该名称由字母、数字和下划线 _ 组成。actual format 应放在引号中,通过转换模式 (conversion patterns) 构建。

5.4 转换模式 (Conversion pattern)

  • 类似C语言 printf 函数 :转换模式与 C 语言的 printf 函数的转换模式密切相关。它由文本和格式控制表达式(称为转换说明符)组成。
  • 用途:转换模式用于规则的文件路径和格式的模式中。
  • 文本插入:可以在转换模式中插入任意文本。
  • 转换说明符

每个转换说明符都以百分号 (%) 开头,后跟可选的格式修饰符和一个转换字符。转换字符指定数据的类型,如类别、日志级别、日期、线程ID等。格式修饰符控制字段宽度、填充、左右对齐等。

例如:

"%d(%m-%d %T) %-5V [%p:%F:%L] %m%n"

这段格式定义了日志的日期、时间、日志级别、进程ID、文件名、行号以及实际的日志消息。

示例执行:

c 复制代码
zlog_info(c, "hello, zlog");

会输出:

shell 复制代码
02-14 17:17:42 INFO [4935:test_hello.c:39] hello, zlog

%+5V 意味着日志事件的级别左对齐宽度为五个字符。

总结以上内容有助于理解 zlog 配置的基本结构和它的格式化日志输出机制。这些配置选项让你能高度自定义日志输出的内容和格式。

5.4.1 转换字符
  1. %c
  • 效果:输出日志事件的类别。
  • 示例aa_bb
  1. %d()
  • 效果 :输出日志事件的日期。可以跟随日期格式说明符,例如%d(%F)%d(%m-%d %T)。如果没有指定格式,则默认%d(%F %T)。日期格式与strftime一致。
  • 示例
  • %d(%F)2011-12-01
  • %d(%m-%d %T)12-01 17:17:42
  • %d(%T).%ms17:17:42.035
  • %d%d()2012-02-14 17:03:12
  1. %E()
  • 效果:输出环境变量的值。
  • 示例%E(LOGNAME) -> simpson
  1. %ms
  • 效果:输出3位数的毫秒数。
  • 示例013
  1. %us
  • 效果:输出6位数的微秒数。
  • 示例002323
  1. %F
  • 效果 :输出发出日志请求的文件名,来自__FILE__宏。
  • 示例test_hello.c或绝对路径,如/home/zlog/src/test/test_hello.c
  1. %f
  • 效果 :输出源文件名,仅保留%F后的最后一个/后的部分。
  • 示例test_hello.c
  1. %g()
  • 效果:以UTC时间而非本地时间输出日志事件日期。
  • 示例
  • %g(%F)2011-12-01
  • %g(%m-%d %T)12-01 15:17:42
  • %g(%T).%ms15:17:42.035
  • %g%g()2012-02-14 15:03:12
  1. %H
  • 效果 :输出系统的主机名,来自gethostname
  • 示例zlog-dev
  1. %k
  • 效果 :输出内核线程ID,Linux使用syscall(SYS_gettid),OSX使用pthread_threadid_np
  • 示例2136
  1. %L
  • 效果 :输出发出日志请求的行号,来自__LINE__宏。
  • 示例135
  1. %m
  • 效果:输出与日志事件关联的应用程序提供的信息。
  • 示例hello, zlog
  1. %M(key)
  • 效果:输出与线程生成的日志事件相关的MDC(映射诊断上下文)。需要跟随一个键名。
  • 示例%M(clientNumber) -> 12345
  1. %n
  • 效果:输出Unix换行符,暂不支持MS-Windows行分隔符。
  • 示例\n
  1. %p
  • 效果 :输出生成日志事件的进程ID,来自getpid`。
  • 示例2134
  1. %U
  • 效果 :输出发出日志请求的函数名,来自__func__(C99)或__FUNCTION__(gcc)宏。
  • 示例main
  1. %V
  • 效果:输出日志事件的级别(大写)。
  • 示例INFO
  1. %v
  • 效果:输出日志事件的级别(小写)。
  • 示例info
  1. %t
  • 效果 :输出生成日志事件的线程ID的16进制格式,来自pthread_self
  • 示例ba01e700
  1. %T
  • 效果:等同于%t,但输出长格式。
  • 示例140633234859776
  1. %%
  • 效果:输出一个百分号。
  • 示例%
  1. %[other char]
  • 效果 :解析为错误语法,导致zlog_init()失败。

了解这些转换字符,可以更灵活地控制zlog的日志输出格式,使日志信息更加清晰和易于调试。

5.4.2 格式修饰符

在zlog中,格式修饰符(Format Modifier)允许你在输出日志信息时,对信息的最小字段宽度、最大字段宽度以及对齐方式进行调整。这种操作会对每个日志事件产生轻微的性能影响。接下来,我们详细介绍这些格式修饰符的使用和效果。

格式修饰符的位置

格式修饰符是可选的,位于百分号(%)和转换字符之间。

常见格式修饰符
  1. 左对齐标志:使用"-"符号。如果没有该标志,默认是右对齐(即左填充)。
  2. 最小字段宽度:一个十进制常量,表示要输出的最小字符数。如果数据项的字符数少于这个宽度,会在左侧或右侧填充空格(或其他填充字符)。默认情况下是左侧填充空格(右对齐)。
  3. 最大字段宽度:用一个点号(.)和一个十进制常量表示。如果数据项的字符数超过这个最大宽度,会从数据项的开头截断多余的字符。
格式修饰符的示例说明
格式修饰符 左对齐 最小宽度 最大宽度 说明
%20c 20 如果分类名称少于20个字符,会在左侧填充空格
%-20c 20 如果分类名称少于20个字符,会在右侧填充空格
%020c 20 如果分类名称少于20个字符,会在左侧填充0
%.30c N/A 30 如果分类名称超过30个字符,会从末尾截断
%20.30c 20 30 如果分类名称少于20个字符,会在左侧填充空格;如果分类名称超过30个字符,会从末尾截断
%-20.30c 20 30 如果分类名称少于20个字符,会在右侧填充空格;如果分类名称超过30个字符,会从末尾截断
示例解析
  • %20c:假设分类名称是"zlog",输出结果为" zlog"(左侧填充空格,总长度为20)。
  • %-20c:假设分类名称是"zlog",输出结果为"zlog "(右侧填充空格,总长度为20)。
  • %020c:假设分类名称是"zlog",输出结果为"0000000000000000zlog"(左侧填充0,总长度为20)。
  • %.30c:假设分类名称是"this_is_a_very_long_category_name",输出结果为"this_is_a_very_long_category_"(超过30个字符,从末尾截断)。
  • %20.30c:假设分类名称是"short",输出结果为" short"(左侧填充空格,总长度为20);假设分类名称是"this_is_a_very_long_category_name",输出结果为"this_is_a_very_long_category_"(超过30个字符,从末尾截断)。
  • %-20.30c:假设分类名称是"short",输出结果为"short "(右侧填充空格,总长度为20);假设分类名称是"this_is_a_very_long_category_name",输出结果为"this_is_a_very_long_category_"(超过30个字符,从末尾截断)。
总结

使用zlog的格式修饰符可以灵活地调整日志信息的显示格式,使其更加美观和易读。不过,务必注意这种操作会导致轻微的性能损耗,因此需要根据实际需要合理使用。

5.4.3 时间字符

在使用 zlog 时,时间字符支持由转换字符 d 来实现。所有的时间字符都是由 C 语言库中 strftime(3) 函数支持的。下面是我的 Linux 系统中支持的时间字符:

字符 效果 显例
%a 当前区域设置下面的简写星期几名称。 Wed
%A 当前区域设置下面的完整星期几名称。 Wednesday
%b 当前区域设置下面的简写月份名称。 Mar
%B 当前区域设置下面的完整月份名称。 March
%c 当前区域设置下面的首选日期和时间表示。 Thu Feb 16 14:16:35 2012
%C 世纪数字(年份/100)作为两个数字的整数来表示。(SU) 20
%d 当月的某一天作为十进制数(范围01到31)。 06
%D 相当于%m/%d/%y(仅对美国用户有效,美国用户需注意在其他国家,格式%d/%m/%y更常见,在国际上下文中这种格式是模糊不清的,不应使用)(SU) 02/16/12
%e 类似%d,但替换前导零为空格。(SU) 6
%F 相当于%Y-%m-%d(ISO 8601日期格式)(C99) 2012-02-16
%G ISO 8601基于星期的年份(参见备注),用世纪作为十进制数表示。ISO星期号对应的4位年份(参见%V)。这个格式和数值与%Y相同,但如果ISO星期号属于上一个年度或下一个年度,则使用那一年。(TZ) 2012
%g 类似%G,但没有世纪,是以两位年份(00-99)表示。(TZ) 12
%h 相当于%b。(SU) Feb
%H 以十进制数表示的小时数,使用24小时制(范围00到23)。 14
%I 以十进制数表示的小时数,使用12小时制(范围01到12)。 02
%j 以十进制数表示的年中的某一天(范围001到366)。 047
%k 以十进制数表示的小时数(24小时制,范围0到23),单个数字前加一个空格(参见%H)(TZ) 15
%l 以十进制数表示的小时数(12小时制,范围1到12),单个数字前加一个空格。(参见%I)(TZ) 3
%m 以十进制数表示的月份(范围01到12)。 02
%M 以十进制数表示的分钟数(范围00到59)。 11
%n 换行符。(SU) \n
%p 根据给定的时间值,输出 "AM" 或 "PM",或当前区域设置的相应字符串。中午输出 "PM",午夜输出 "AM"。 PM
%P 类似%p,但为小写形式: "am" 或 "pm",或者当前区域设置的相应字符串。(GNU) pm
%r 以.a.m. 或 p.m. 表示的时间。在POSIX区域设置下这相当于%I:%M:%S %p。(SU) 03:11:54 PM
%R 24小时制的时间(%H:%M)。(SU) 包括秒的版本见%T。 15:11
%s 自 Epoch 以来的秒数,即自1970-01-01 00:00:00 UTC以来的秒数。(TZ) 1329376487
%S 以十进制数表示的秒数(范围00到60)(范围为60以包含偶尔出现的闰秒)。 54
%t 制表符。(SU)
%T 24小时制的时间(%H:%M:%S)。(SU) 15:14:47
%u 以十进制数表示的星期几,范围为1到7,星期一是1。参见%w。(SU) 4
%U 当前年份的星期数,以十进制数表示,范围为00到53,从第一个星期天为星期的第一天开始算第一周。参见%V和%W。 07
%V 当前年份的ISO 8601星期号,范围为01到53,第1周是包含至少4天的新一年的第一周。参见%U和%W。(SU) 07
%w 以十进制数表示的星期几,范围为0到6,星期天是0。参见%u。 4
%W 当前年份的星期数,以十进制数表示,范围为00到53,从第一个星期一为星期的第一天开始算第一周。 07
%x 当前区域设置中首选的日期表示,不包含时间。 02/16/12
%X 当前区域设置中首选的时间表示,不包含日期。 15:14:47
%y 两位数字表示的年份,范围00到99。 12
%Y 含有世纪的十进制年份。 2012
%z 时区与GMT的小时偏移量。为发出符合RFC 822的日期所需(使用"%a, %d %b %Y %H:%M:%S %z")。(GNU) +0800
%Z 时区或名称或缩写。 CST
%% 字面意义的 '%'。 %

5.5 规则 (Rules)

规则部分定义了日志信息如何被过滤、格式化和输出。此部分可以省略,但是如果省略了将不会有任何日志输出。规则的语法如下:

(category).(level)    (output), (option, optional); (format name, optional)

例如:

main.*               >stdout;
main.INFO            /var/log/main.log, 10MB * 5; debug_format 

在调用zlog_init()函数时,所有的规则都会被读取进内存。当调用zlog_get_category()时,多个匹配的规则会被分配给各个类别。当进行日志记录时,日志的等级会与对应的规则进行匹配,以决定该条日志是否会被输出。如果调用zlog_reload(),配置文件会重新读取进内存,并且所有的类别规则将会重新计算。

5.5.1. 等级匹配 (Level Matching)

zlog默认支持六种日志等级:"DEBUG"、"INFO"、"NOTICE"、"WARN"、"ERROR" 和 "FATAL"。在配置文件中,等级不区分大小写;例如,"DEBUG"和"debug"是等价的。

匹配表达式举例如下:

例子 含义
* 所有日志等级
aa.debug 日志等级>=debug
aa.=debug 日志等级==debug
aa.!debug 日志等级!=debug

用户可以定义自定义的等级,具体请参考相关章节(User-defined Level)。

5.5.2. 类别匹配 (Category Matching)

类别匹配非常简单。类别名称由字母、数字和下划线组成:

描述 配置文件中的类别字符串 类别匹配 类别不匹配
* 匹配所有类别 . aa, aa_bb, xx, yy ... NONE
以_结尾的字符串匹配父类别及子类别 aa_.* aa, aa_bb, aa_cc, aa_bb_cc xx, yy
精确匹配不以_结尾的字符串 aa.* aa aa_bb, aa_cc, aa_bb_cc
! 匹配没有规则匹配的类别 !.* xx aa(因为上面匹配了规则)
5.5.3. 输出操作 (Output Action)

zlog支持多种输出方法,语法如下:

(output action), (output option); (format name, optional)

例如:

*.*   >stdout;
*.*   | /usr/bin/cronolog /www/logs/example_%Y%m%d.log; normal
*.*   "/var/log/output.log";

表格描述了不同输出动作和选项:

输出类型 输出动作 输出选项
到标准输出 >stdout 无意义
到标准错误 >stderr 无意义
到syslog >syslog syslog facility, 必填
管道输出 cat 无意义
到文件 "(文件路径)" 支持旋转,具体见下文
同步I/O到文件 -"(文件路径)"
用户自定义输出 $name 记录函数的路径(动态或静态)

标准输出/标准错误,以守护进程运行时要非常小心,不建议设置>stdout或>stderr,因为守护进程会关闭其首个文件描述符,可能导致日志写入错误的文件。

5.5.4. 文件输出和日志轮转

zlog的文件输出非常强大,可以设置文件路径(支持相对和绝对路径)和旋转规则。例如:

*.*   "/var/log/app.log", 1MB * 3

表示文件的大小达到1MB时,会触发日志轮转,最多保留3个日志文件。

5.5.5. 同步I/O文件

设置同步I/O选项(在文件路径前加'-'),例如:

*.*   -"/var/log/app.log"

将确保每次日志写入操作等待系统将数据写入磁盘,尽管这会显著降低性能。

5.5.6. 自定义输出格式

zlog默认的输出格式在全局设置中定义:

[global]
default format = "%d %V [%p:%F:%L] %m%n"

也可以在规则中自定义格式,例如:

main.INFO            /var/log/main.log, 10MB * 5; debug_format
补充例子:

在使用 zlog 进行日志管理时,有几个重要的输出方式需要了解,包括 stdout、stderr、syslog、管道输出和文件输出。以下是这些输出方式的详细解释:

stdout, stderr, syslog

在 zlog 中,只有 syslog 需要指定一个有意义的输出选项。此外,还有一些关于守护进程的警告:

警告绝不要 在守护进程中使用 >stdout>stderr。因为守护进程会关闭它的第一个文件描述符。如果设置了 >stdout,zlog 将输出日志到文件描述符1。这会导致日志被写入到意想不到的文件里,比如配置文件。

示例
c 复制代码
write(STDOUT_FILENO, zlog_buf_str(a_thread->msg_buf), zlog_buf_len(a_thread->msg_buf));

这种情况下,日志文件将被写入文件描述符1。因此,守护进程不应该将任何规则输出设为 stdout 或 stderr,以避免未定义的行为。如果确实需要在 stdout 关闭时输出日志到控制台,可以使用 /dev/tty

管道输出 (pipeline output)
示例规则
plaintext 复制代码
*.* | /usr/bin/cronolog /www/logs/example_%Y%m%d.log ; normal

这是 zlog 将输出通过管道传递给 cronolog 的一个例子。具体实现方式是,在 zlog_init() 时调用 popen("/usr/bin/cronolog /www/logs/example_%Y%m%d.log","w"),之后的日志将被写入打开的描述符。通过管道和 cronolog 写日志比每次写日志时动态打开和关闭文件描述符要快。

性能对比

普通文件写入

plaintext 复制代码
[rules]

*.* "press%d(%Y%m%d).log"

$ time ./test_press_zlog 1 10 100000

real 0m4.240s
user 0m2.500s
sys 0m5.460s

通过管道写入

plaintext 复制代码
[rules]

*.* | /usr/bin/cronolog press%Y%m%d.log

$ time ./test_press_zlog 1 10 100000

real 0m1.911s
user 0m1.980s
sys 0m1.470s
管道输出的限制
  1. POSIX.1-2001规定小于 PIPE_BUF 字节的 write 操作必须是原子的。在 Linux 上,PIPE_BUF 是 4096 字节。
  2. 当单个日志超出 PIPE_BUF 字节,并且多个进程通过一个管道写日志时,会发生日志混杂。
  3. 不相关的多个进程写入同一个管道日志文件时,也会导致日志混杂。

总结:

  • 单进程写入时,单个日志长度无实际限制,多线程单进程中 zlog 确保了原子写入。
  • 相关联的多个进程,单个日志长度不应超过 PIPE_BUF 字节。
  • 不相关的多个进程,不论单个日志长度,会导致日志混杂。
文件输出 (file)
文件路径

可以是绝对路径或相对路径,使用双引号引用。可以在文件路径中使用转换模式。例如:

plaintext 复制代码
%E(HOME)/log/out.log

如果程序的环境变量 $HOME 是 /home/harry,那么日志文件路径将是 /home/harry/log/output.log。

文件输出示例
  1. 输出到命名管道(FIFO):
plaintext 复制代码
*.* "/tmp/pipefile"
  1. 输出到空设备,等于不记录日志:
plaintext 复制代码
*.* "/dev/null"
  1. 输出到控制台:
plaintext 复制代码
*.* "/dev/tty"
  1. 输出到每个线程ID的日志文件:
plaintext 复制代码
*.* "%T.log"
  1. 输出到以进程ID命名的文件,每天一个文件,在 $HOME/log 目录下,日志轮转设定为1GB,保留5个文件:
plaintext 复制代码
*.* "%E(HOME)/log/aa.%p.%d(%F).log",1GB*5
  1. 每个 aa_ 开头的类别,输出日志到各自的文件中:
plaintext 复制代码
aa_.* "/var/log/%c.log"
日志轮转

设定日志文件大小和数量。例如:

plaintext 复制代码
%E(HOME)/log/out.log,1M*3

当 out.log 超过 1M 时,日志轮转如下:

plaintext 复制代码
out.log -> out.log.1
out.log (新创建)

out.log.1 -> out.log.2
out.log -> out.log.1
out.log (新创建)

保留3个日志文件,超过会删除最旧的文件:

plaintext 复制代码
unlink(out.log.2)
out.log.1 -> out.log.2
out.log -> out.log.1
out.log (新创建)
同步I/O文件

带有减号 '-' 的选项表示使用同步 I/O。日志文件以 O_SYNC 方式打开,每个日志操作都会等待操作系统将数据写入磁盘。例如:

plaintext 复制代码
$ time ./test_press_zlog 100 1000

real 0m0.732s
user 1m1.030s
sys 1m1.080s

$ time ./test_press_zlog 100 1000  # 同步I/O打开

real 0m20.646s
user 0m2.570s
sys 0m6.950s
格式名

格式名是可选的。如果未设置,将使用全局设定中的默认格式:

plaintext 复制代码
[global]

default format = "%d %V [%p:%F:%L] %m%n"

欲了解更多自定义输出的细节,请参考用户定义输出 (User-defined-Output) 部分。

5.6 日志轮转

日志轮转的必要性

在生产环境中,不止一次看到由于硬盘被日志填满而导致系统停止工作,或者单个日志文件过大而无法打开或使用 grep 搜索内容。日志轮转和归档的几种方法如下:

1. 按日期或时间分割日志

例如,每天生成一个日志文件:

plaintext 复制代码
aa.2012-08-02.log
aa.2012-08-03.log
aa.2012-08-04.log

这种方式下,系统管理员能知道每天会产生多少日志,并且可以根据日期搜索日志文件。最好的方式是让 zlog 库处理日志拆分。另一种选择是使用 cronosplit 分析日志内容并拆分。但是,使用 crontab + logrotate 每天移动日志文件不是很准确,部分日志可能会被放到前一天的文件中。

使用 zlog 时,不需要外部旋转操作即可完成工作。可以通过在日志文件名中设置时间来实现:

plaintext 复制代码
*.*  "aa.%d(%F).log"

或者使用 cronolog 以提高性能:

plaintext 复制代码
*.*  | cronolog aa.%F.log
2. 按大小分割日志

这种方式通常适用于开发环境,在这种情况下,程序会在短时间内生成大量日志。尽管可以使用拆分工具在之后进行拆分,但这需要额外步骤。所以更好的方式是让日志库自己处理日志轮转。正如 nlog 所描述的,有两种轮转方式:序列和滚动。

  • 序列方式:
plaintext 复制代码
aa.log (新)
aa.log.2 (较新)
aa.log.1
aa.log.0 (旧)
  • 滚动方式:
plaintext 复制代码
aa.log (新)
aa.log.0 (较新)
aa.log.1
aa.log.2 (旧)

很难说哪种方式最适用。如果只有部分最新的日志对开发人员有用,日志库应该清理旧的日志文件,某些外部工具可能无法找到哪些文件是旧的。

最简单的 zlog 轮转配置如下:

plaintext 复制代码
*.*     "aa.log", 10MB

这是滚动方式。当 aa.log 大于10MB, zlog 会重命名文件如下:

plaintext 复制代码
aa.log.2 -> aa.log.3
aa.log.1 -> aa.log.2
aa.log.0 -> aa.log.1
aa.log -> aa.log.0

配置可以更复杂一些:

plaintext 复制代码
*.*     "aa.log", 10MB * 0 ~ "aa.log.#r"
  • 文件名后的第一个参数表示触发轮转的大小。
  • 文件名后的第二个参数表示保留多少归档文件,0 表示保留所有文件。
  • 文件名后的第三个参数显示归档文件的名称。#r 是归档文件的序列号。r 表示滚动,#s 表示序列。归档文件名必须包含 #r#s 之一。
3. 按大小分割日志并添加时间标签

例如:

plaintext 复制代码
aa.log
aa.log-20070305.00.log
aa.log-20070501.00.log
aa.log-20070501.01.log
aa.log-20071008.00.log

在这种情况下,日志文件通常不会经常查看,可能一天查看一次。当然,当一天的日志多于100MB时,应考虑存储到两个文件并添加后缀号。例如,如果日期用作模式的一部分(如20070501),zlog 的配置如下:

plaintext 复制代码
*.*     "aa.log", 100MB ~ "aa-%d(%Y%m%d).#2s.log"

每100MB进行一次轮转,归档文件名也支持转换字符串。#2s 意味着序列号至少有2位字节宽,从00开始。这是使用 zlog 归档最复杂的方式。

4. 压缩、移动和删除旧归档

压缩不应由日志库完成,因为压缩需要时间和CPU。日志库的任务是与压缩程序合作。

对于上述拆分日志的三种方式,方式1和方式3易于管理,可以通过文件名或修改时间找到旧的日志文件,然后使用 crontab 和 shell 脚本来压缩、移动和删除旧日志文件。

对于第二种方式,压缩没有用,删除是必须的,zlog 已经支持这一点。

如果你真的想同时轮转和压缩日志文件,建议使用 logrotate。这是一个独立的程序,不会使情况变得混乱。

5. zlog 对外部工具如 logrotate 的支持

zlog 的轮转支持非常强大,但仍有几种情况无法处理。例如按时间轮转,轮转前后调用一些用户定义的shell。这将使 zlog 变得太复杂。

在这种情况下,考虑使用外部工具如 logrotate。在 Linux 上,问题在于当一个工具重命名日志文件时,使用 inode 引用文件的工作进程不会自动重新打开新文件。标准方式是向程序发送信号并让其重新打开文件。对于 syslogd,命令如下:

plaintext 复制代码
kill -SIGHUP `cat /var/run/syslogd.pid`

对于 zlog 作为一个库来说,接收信号不是一个好办法。zlog 提供 zlog_reload() 函数,可以重新加载配置文件并重新打开所有日志文件。所以,如果你编写一个程序并想手动重新打开日志文件,可以写一些代码来完成这项工作:在接收到信号或客户的命令后,调用 zlog_reload()

5.7 配置文件工具

在使用 zlog 进行日志管理时,正确配置日志配置文件是至关重要的。zlog 提供了一个工具 zlog-chk-conf,用于检查和验证配置文件的语法是否正确。下面我将详细解释如何使用这一工具以及解读输出信息。

使用 zlog-chk-conf 工具

在命令行终端中输入以下命令可以查看 zlog-chk-conf 的使用方法:

sh 复制代码
$ zlog-chk-conf -h

使用该命令会显示以下帮助信息:

sh 复制代码
Usage: zlog-chk-conf [conf files]...

-q,  suppress non-error messages
-h,  show help message

该工具的用途是读取配置文件,检查其语法,并输出检查结果。建议每次创建或更改配置文件后,都使用这个工具进行检查,以确保配置文件没有语法错误。

检查配置文件

假设你的配置文件名为 zlog.conf,你可以使用以下命令来检查该文件:

sh 复制代码
$ ./zlog-chk-conf zlog.conf
输出示例及解释

运行上述命令后,可能会得到如下输出:

sh 复制代码
03-08 15:35:44 ERROR (10595:rule.c:391) sscanf [aaa] fail, category or level is null
03-08 15:35:44 ERROR (10595:conf.c:155) zlog_rule_new fail [aaa]
03-08 15:35:44 ERROR (10595:conf.c:258) parse configure file[zlog.conf] line[126] fail
03-08 15:35:44 ERROR (10595:conf.c:306) zlog_conf_read_config fail
03-08 15:35:44 ERROR (10595:conf.c:366) zlog_conf_build fail
03-08 15:35:44 ERROR (10595:zlog.c:66) conf_file[zlog.conf], init conf fail
03-08 15:35:44 ERROR (10595:zlog.c:131) zlog_init_inner[zlog.conf] fail

---[zlog.conf] syntax error, see error message above

这些错误信息的详细解释:

  1. 错误信息:sscanf [aaa] fail, category or level is null
  • 表明在解析配置文件中的某一行 [aaa] 规则时,sscanf 函数失败了,可能是因为类别或级别为空。你需要仔细检查该行的规则定义。
  1. 错误信息:zlog_rule_new fail [aaa]
  • 由于上一步的解析失败,导致创建新的 zlog 规则失败。这是一个连锁反应。
  1. 错误信息:parse configure file[zlog.conf] line[126] fail
  • 明确指出了错误发生在配置文件的第126行。你可以直接前往该行进行排查。
  1. 错误信息:zlog_conf_read_config fail
  • 读取配置文件失败,可能因为之前的错误导致无法继续解析。
  1. 错误信息:zlog_conf_build fail
  • 构建 zlog 配置失败,持续受到之前错误的影响。
  1. 错误信息:conf_file[zlog.conf], init conf failzlog_init_inner[zlog.conf] fail
  • 最终,由于初始化配置失败,整个日志系统初始化失败。
总结

通过 zlog-chk-conf 工具,你可以快速定位和修复配置文件中的语法错误,确保你的 zlog 日志系统能够正常运行。每当你修改配置文件后,建议运行上述检查命令,避免潜在的配置错误影响系统的日志功能。

相关推荐
Fairy_sevenseven11 分钟前
【二十八】【QT开发应用】模拟WPS Tab
开发语言·qt·wps
蜡笔小新星19 分钟前
Python Kivy库学习路线
开发语言·网络·经验分享·python·学习
凯子坚持 c19 分钟前
C语言复习概要(三)
c语言·开发语言
无限大.31 分钟前
c语言200例 067
java·c语言·开发语言
余炜yw32 分钟前
【Java序列化器】Java 中常用序列化器的探索与实践
java·开发语言
无限大.33 分钟前
c语言实例
c语言·数据结构·算法
篝火悟者33 分钟前
问题-python-运行报错-SyntaxError: Non-UTF-8 code starting with ‘\xd5‘ in file 汉字编码问题
开发语言·python
Death20036 分钟前
Qt 中的 QListWidget、QTreeWidget 和 QTableWidget:简化的数据展示控件
c语言·开发语言·c++·qt·c#
六点半88837 分钟前
【C++】速通涉及 “vector” 的经典OJ编程题
开发语言·c++·算法·青少年编程·推荐算法
惜.己37 分钟前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5