第五章 配置文件
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;
配置文件的结构如下:
[]
表示一个部分的开始,各部分的顺序是固定的,按照 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 min
和buffer 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 转换字符
- %c
- 效果:输出日志事件的类别。
- 示例 :
aa_bb
- %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).%ms
:17:17:42.035
%d
或%d()
:2012-02-14 17:03:12
- %E()
- 效果:输出环境变量的值。
- 示例 :
%E(LOGNAME)
->simpson
- %ms
- 效果:输出3位数的毫秒数。
- 示例 :
013
- %us
- 效果:输出6位数的微秒数。
- 示例 :
002323
- %F
- 效果 :输出发出日志请求的文件名,来自
__FILE__
宏。 - 示例 :
test_hello.c
或绝对路径,如/home/zlog/src/test/test_hello.c
- %f
- 效果 :输出源文件名,仅保留
%F
后的最后一个/
后的部分。 - 示例 :
test_hello.c
- %g()
- 效果:以UTC时间而非本地时间输出日志事件日期。
- 示例:
%g(%F)
:2011-12-01
%g(%m-%d %T)
:12-01 15:17:42
%g(%T).%ms
:15:17:42.035
%g
或%g()
:2012-02-14 15:03:12
- %H
- 效果 :输出系统的主机名,来自
gethostname
。 - 示例 :
zlog-dev
- %k
- 效果 :输出内核线程ID,Linux使用
syscall(SYS_gettid)
,OSX使用pthread_threadid_np
。 - 示例 :
2136
- %L
- 效果 :输出发出日志请求的行号,来自
__LINE__
宏。 - 示例 :
135
- %m
- 效果:输出与日志事件关联的应用程序提供的信息。
- 示例 :
hello, zlog
- %M(key)
- 效果:输出与线程生成的日志事件相关的MDC(映射诊断上下文)。需要跟随一个键名。
- 示例 :
%M(clientNumber)
->12345
- %n
- 效果:输出Unix换行符,暂不支持MS-Windows行分隔符。
- 示例 :
\n
- %p
- 效果 :输出生成日志事件的进程ID,来自
getpid
`。 - 示例 :
2134
- %U
- 效果 :输出发出日志请求的函数名,来自
__func__
(C99)或__FUNCTION__
(gcc)宏。 - 示例 :
main
- %V
- 效果:输出日志事件的级别(大写)。
- 示例 :
INFO
- %v
- 效果:输出日志事件的级别(小写)。
- 示例 :
info
- %t
- 效果 :输出生成日志事件的线程ID的16进制格式,来自
pthread_self
。 - 示例 :
ba01e700
- %T
- 效果:等同于%t,但输出长格式。
- 示例 :
140633234859776
- %%
- 效果:输出一个百分号。
- 示例 :
%
- %[other char]
- 效果 :解析为错误语法,导致
zlog_init()
失败。
了解这些转换字符,可以更灵活地控制zlog的日志输出格式,使日志信息更加清晰和易于调试。
5.4.2 格式修饰符
在zlog中,格式修饰符(Format Modifier)允许你在输出日志信息时,对信息的最小字段宽度、最大字段宽度以及对齐方式进行调整。这种操作会对每个日志事件产生轻微的性能影响。接下来,我们详细介绍这些格式修饰符的使用和效果。
格式修饰符的位置
格式修饰符是可选的,位于百分号(%)和转换字符之间。
常见格式修饰符
- 左对齐标志:使用"-"符号。如果没有该标志,默认是右对齐(即左填充)。
- 最小字段宽度:一个十进制常量,表示要输出的最小字符数。如果数据项的字符数少于这个宽度,会在左侧或右侧填充空格(或其他填充字符)。默认情况下是左侧填充空格(右对齐)。
- 最大字段宽度:用一个点号(.)和一个十进制常量表示。如果数据项的字符数超过这个最大宽度,会从数据项的开头截断多余的字符。
格式修饰符的示例说明
格式修饰符 | 左对齐 | 最小宽度 | 最大宽度 | 说明 |
---|---|---|---|---|
%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
管道输出的限制
- POSIX.1-2001规定小于 PIPE_BUF 字节的 write 操作必须是原子的。在 Linux 上,PIPE_BUF 是 4096 字节。
- 当单个日志超出 PIPE_BUF 字节,并且多个进程通过一个管道写日志时,会发生日志混杂。
- 不相关的多个进程写入同一个管道日志文件时,也会导致日志混杂。
总结:
- 单进程写入时,单个日志长度无实际限制,多线程单进程中 zlog 确保了原子写入。
- 相关联的多个进程,单个日志长度不应超过 PIPE_BUF 字节。
- 不相关的多个进程,不论单个日志长度,会导致日志混杂。
文件输出 (file)
文件路径
可以是绝对路径或相对路径,使用双引号引用。可以在文件路径中使用转换模式。例如:
plaintext
%E(HOME)/log/out.log
如果程序的环境变量 $HOME 是 /home/harry,那么日志文件路径将是 /home/harry/log/output.log。
文件输出示例
- 输出到命名管道(FIFO):
plaintext
*.* "/tmp/pipefile"
- 输出到空设备,等于不记录日志:
plaintext
*.* "/dev/null"
- 输出到控制台:
plaintext
*.* "/dev/tty"
- 输出到每个线程ID的日志文件:
plaintext
*.* "%T.log"
- 输出到以进程ID命名的文件,每天一个文件,在 $HOME/log 目录下,日志轮转设定为1GB,保留5个文件:
plaintext
*.* "%E(HOME)/log/aa.%p.%d(%F).log",1GB*5
- 每个
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
这些错误信息的详细解释:
- 错误信息:
sscanf [aaa] fail, category or level is null
- 表明在解析配置文件中的某一行
[aaa]
规则时,sscanf
函数失败了,可能是因为类别或级别为空。你需要仔细检查该行的规则定义。
- 错误信息:
zlog_rule_new fail [aaa]
- 由于上一步的解析失败,导致创建新的 zlog 规则失败。这是一个连锁反应。
- 错误信息:
parse configure file[zlog.conf] line[126] fail
- 明确指出了错误发生在配置文件的第126行。你可以直接前往该行进行排查。
- 错误信息:
zlog_conf_read_config fail
- 读取配置文件失败,可能因为之前的错误导致无法继续解析。
- 错误信息:
zlog_conf_build fail
- 构建 zlog 配置失败,持续受到之前错误的影响。
- 错误信息:
conf_file[zlog.conf], init conf fail
和zlog_init_inner[zlog.conf] fail
- 最终,由于初始化配置失败,整个日志系统初始化失败。
总结
通过 zlog-chk-conf
工具,你可以快速定位和修复配置文件中的语法错误,确保你的 zlog 日志系统能够正常运行。每当你修改配置文件后,建议运行上述检查命令,避免潜在的配置错误影响系统的日志功能。