Linux三方库工具癖IT@程序员猿媛

C语言日志库zlog总结

2019-02-26  本文已影响31人  konishi5202

一、zlog是什么?

1.1 zlog介绍

zlog是一个可靠性、高性能、线程安全、灵活、概念清晰的纯C日志函数库。

事实上,在C的世界里面没有特别好的日志函数库(就像JAVA里面的log4j,或者C++的log4cxx)。C程序员都喜欢用自己的轮子。printf就是个挺好的轮子,但是没办法通过配置改变日志的格式或者输出文件。syslog是个系同级别的轮子,不过速度慢,而且功能比较单调。而zlog同时具有这些功能。

【zlog的特性】:

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

1.2 zlog兼容性说明

  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 ...都是好的,不过glibc 2.0不是。在这种情况下,用户需要自己来装一个C99兼容的vsnprintf,来crack这个函数库。我推荐ctrio,或者C99-snprintf。只要改buf.c就行,祝好运!
  3. zlog目前还没有计划支持windows,毕竟windows下已经有非常成熟的日志函数库。

二、zlog不是什么?

zlog的目标是称为一个简而精的日志函数库,不会直接支持网络输出或者写入数据库,不会直接支持日志内容的过滤和解析。

原因很明显,日志库是被应用程序调用的,所有花在日志库上的时间都是应用程序运行时间的一部分,而上面说的这些操作都很费时间,会拖慢应用程序的速度。这些事儿应该在别的进程或者别的机器上做。如果你需要这些特性,我建议使用rsyslog、hsLogFabric、Logstash,这些日志搜集、过滤、存储软件,当然是单独的进程,不是应用程序的一部分。

目前zlog已经支持用户自定义输出函数,用户可以自己实现一个输出函数,自由的把日志输出到其他进程或者其他机器,而把日志的分配匹配、日志的格式成型的工作交给zlog。

三、Hello World

3.1 应用程序调用和链接zlog

应用程序使用zlog很简单,只要在C文件里面加一行:

#include “hs_log.h”

链接zlog需要pthread库,命令是:

$ cc -c -o app.o app.c -I/usr/local/zlog/include
  # -I[where zlog.h is put]
$ cc -o app app.o -L/usr/local/zlog/lib –lzlog –lpthread
  # -L[where libzlog.so is put]

3.2 Hello World实现

3.2.1 写一个C程序

$ vim hello.c
#include <stdio.h>
#include "hs_log.h"
    
int main(int argc, char** argv)
{
    int rc;
        
    rc = hs_log_init();  //1 : first init log module
    if (rc) {
        printf("init zlog module failed\n");
        return -1;
    }
    
    // 2: call hs-log function print log
    hs_log_debug("hello, ha-log %s", "debug");
    hs_log_info("hello, hs-log %d", 100);
    hs_log_notice("%s %d","hello, hs-log", 100);
    hs_log_warn("hello, hs-log");
    hs_log_error("hello, hs-log");
    hs_log_fatal("hello, hs-log");
    
    hs_log_fini();
    
    return 0;
}

3.2.2 写一个配置文件

[rules]
hs_cat.*        >stderr;

将log.conf放在hello.c同一目录下。

3.2.3 编译-运行

$ cc -c -o test_hello.o test_hello.c -I/usr/local/zlog/include
$ cc -o test_hello test_hello.o -L/usr/local/lib –lzlog
$ ./test_hello
2016-06-18 15:06:31 DEBUG [14393:hs_log_hello.c:22] hello, ha-log debug
2016-06-18 15:06:31 INFO [14393:hs_log_hello.c:23] hello, hs-log 100
2016-06-18 15:06:31 NOTICE [14393:hs_log_hello.c:24] hello, hs-log 100
2016-06-18 15:06:31 WARN [14393:hs_log_hello.c:25] hello, hs-log
2016-06-18 15:06:31 ERROR [14393:hs_log_hello.c:26] hello, hs-log
2016-06-18 15:06:31 FATAL [14393:hs_log_hello.c:27] hello, hs-log

四、配置文件

大部分的zlog的行为取决于配置文件:把日志打到哪儿去,用什么格式,怎么转档。配置文件是zlog的黑活,我尽量把这个黑活设计的简单明了。这是个配置文件的例子:

# comments
[global]
strict init = true;
buffer min = 1024;
buffer max = 2MB;
default format = “%d.%us %-6V (%c:%F:%L) - %m%n”;
file perms = 600;
[formats]
simple = “%m%n”
normal = “%d %m%n”
[rules]
default.*       >stdout; simple
*.*         “%12.2E(HOME)/log/%c.log”, 1MB*12; simple
my_.INFO        >stderr;
hs_cat.!ERROR   >”/var/log/aa.log”
my_dog.=DEBUG   >syslog, LOG_LOCALO; simple
my_mice.*       $user_define;

有关单位使用说明:当设置内存大小或大数字时,可以设置1k 5GB 4M这样的单位:

# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes

单位大小写不敏感,所以1GB 1Gb 1gB是等效的。

4.1 全局参数

全局参数以[global]开头,==[]代表一个节的开始,四个小节的顺序不能变,依次为global-levels-formats-rules==。[global]这一节可以忽略不写,语法为:

(key) = (value)

【strict init】

如果“strict init”是true,hs_log_init()将会严格检查所有的格式和规则,任何错误都会导致hs_log_init()失败并返回-1。当“strict init”是false的时候,hs_log_init()会忽略错误的格式和规则。这个参数默认为true。

【buffer min/max】

zlog在堆上为每个线程申请缓存。“buffer min”是单个缓存的最小值,hs_log_init()的时候申请这个长度的内存。写日志的时候,如果单条日志长度大于缓存,缓存会自动扩充,直到“buffer max”。单条日志再长超过“buffer max”就会被截断。如果“buffer max”是0,意味着不限制缓存,每次扩充为原先的2倍,直到这个进程用完所有内存为止。缓存大小可以加上KB,MB或GB这些单位。默认来说“buffer min”是1k,“buffer max”是2MB。

【default format】

这个参数是缺省的日志格式,默认值为:

“%d %v [%p:%F:%L] %m%n”

这种格式产生的输出类似这样:

2016-06-12 17:03:12 INFO [3758:test_hello.c:39] hello, zlog

4.2 格式(Formats)

这一节以[formats]开始,用来定义日志的格式。语法为:

(name) = “(actual formats)”

很好理解,==(name)被后面的规则使用。(name)必须由数字和字母组成,下划线“_”也算字母==。(actual format)前后需要有双引号。(actual formats)可以由转换字符组成,见下一节。

syslog有一个通配符“*”,匹配所有的设施(facility)。zlog里面也一样,==“*”匹配所有的分类==。这提供了一个很方便的办法来重定向你的系统中各个组织的错误。只要这么写:

[rules]
*.error “/var/log/error.log”

==通配符“_”表示上级分类==。“my_”是“hs_cat”和“my_dog”的上级分类。还有一个通配符“!”,详见5.5.2节。

4.3 转换格式串

转换格式串的设计是从C的printf函数里面抄来的。一个转换格式串由文本字符和转换说明组成。转换格式串用在规则的日志文件路径和输出格式(format)中。你可以把任意的文本字符放到转换格式串里面。

==每个转换说明都是以百分号(%)打头的,后面跟可选的宽度修饰符,最后以转换字符结尾。转化字符决定了输出什么数据,例如分类名、级别、时间日期、进程号等等。宽度修饰符控制了这个字段的最大最小宽度、左右对齐==。下面是简单的例子。

如果转换格式串是:

“%d(%m-%d %T) %-5v [%p:%F:%L] %m%n”

源代码中写日志语句是:

hs_log_info(c,”hello, zlog”);

将会输出:

02-14 17:17:48 INFO [4938:test_hello.c:39] hello, zlog

可以注意到,在文本字符和转换说明之间没有显示的分隔符。zlog解析的时候知道哪里是转换说明的开头和结尾。在这个例子里面%-5p这个转换说明决定了日志级别要被左对齐,占5个字符宽度。

4.3.1 转换字符

可以被辨认的转换字符有:


image

4.3.2 宽度修饰符

一般来说数据按原样输出。不过,有了宽度修饰符,就能够控制最小字段宽度、最大字段宽度和左右对齐。当然这要付出一定的性能代价。可选的宽度修饰符放在百分号和转换字符之间。

==第一个可选的宽度修饰符是左对齐标识,减号(-)==。++然后是可选的最小字段宽度,这是一个十进制数字常量,表示最少有几个字符会被输出。如果数据本来没有那么多字符,将会填充空格(左对齐或者右对齐)直到最小字段宽度为止。默认是填充在左边也就是右对齐。当然你也可以使用左对齐标志,指定为填充在右边来左对齐。填充字符为空格(space)。如果数据的宽度超过最小字段宽度,则按照数据的宽度输出,永远不会截断数据++。

这种行为可以用最大字段宽度来改变。++最大字符宽度是放在一个句点号(.)后面的十进制数字常量。如果数据的宽度超过了最大字段宽度,则尾部多余的字符(超过最大字段宽度的部分)将会被截取++。最大字段宽度是8,数据的宽度是10,则最后两个字符会被丢弃。这种行为和C的printf是一样的,把后面的部分截断。

下面是各种宽度修饰符和分类转换字符符合一起用的例子。


image

4.3.3 时间字符

这里是转换字符d支持的时间字符。所有字符都是由strftime(2)生成的,在我的linux操作系统上支持的是:


image

4.4 规则(Rules)

这一节以[rules]开头。这个描述了日志是怎么被过滤、格式化以及被输出的。这节可以忽略不写,不过这样就没有日志输出了,嘿嘿。语法是:

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

当hs_log_init()被调用的时候,所有规则都会被读到内存中,当hs_log_get_category()被调用,规则就被分配给分类(5.5.2节)。在实际写日志的时候,例如hs_log_info()被调用的时候,就会比较这个INFO和各条规则的等级,来决定这条日志会不会通过这条规则输出。当hs_log_reload()()被调用的时候,配置文件会被重新读入,包括所有的规则,并且重新计算分类对应的规则。

4.4.1 级别匹配

==zlog有6个默认的级别:“DEBUG”、“INFO”、“NOTICE”、“WARN”、“ERROR”和“FATAL”==。就像其他的日志函数库那样,aa.DEBUG意味着任何大于等于DEBUG级别的日志会被输出。当然还有其他表达式。配置文件中的级别是大小写不敏感的。


image

用户还可以自定义等级,详细见7.3节。

4.4.2 分类匹配

分类必须由数字和字母组成,下划线“_”也算字母。


image

4.4.3 输出动作

目前zlog支持若干种输出,语法是:

[输出], [附加选型, 可选]; [format(格式)名, 可选]
image

stdout, stderr, syslog:如表格描述,其中只有syslog的附加选项是有意义并且必须写的。值得注意的是,zlog在写日志的时候会用这样的语句:

write(STDOUT_FILENO, hs_log_buf_str(a_thread->msg_buf), hs_log_buf_len(a_thread->msg_len));

而如果你的程序是个守护进程,在启动的时候把STDOUT_FILENO也就是1的文件描述关掉的话,会发生什么结果呢?

4.5 文档转档

为什么需要将日志文件转档?我已经在实际的运行环境中不止一次的看到过,因为日志文件过大,导致系统硬盘被撑爆,或者单个日志文件过大而即使用grep也要花费很多时间来寻找匹配的日志。对于日志转档,我总结了如下几种范式。

4.5.1 按固定时间段来切分日志

例如每天生成一个日志:

aa.2012-08-02.log
aa.2012-08-03.log
aa.2012-08-04.log

这种日志适合的场景是管理员大概知道每天生产的日质量,然后希望在n个月之后能精确的找出某天的所有日志。这种日志切分最好由日志库来完成,其次的方法是用cronos-plit这种软件来分析日志内容的时间字符串来进行后期的切分,较差的办法是用crontab+logrotate或mv来定期移动(但这并不精确,会造成若干条当天的日志被放到上一天的文件里面去)。

在zlog里面,这种需求不需要用日志转档功能来完成,简单的在日至文件名里面设置时间日期字符串就能解决问题。

*.* “aa.%d(%F).log”

或者用cronolog来完成,速度会更快一点。

*.* | cronolog aa.%F.logs

4.5.2 按照日志大小切分

多用于开发环境,适合的场景是:程序在短时间内生成大量的日志,而用编辑器vi,ue等能快速打开的日志大小是有限的,或者大的日志打开来极慢。同样,这种日志的切分可以在事后用split等工具来完成,但对于开发而言会增加步骤,所以最好的也是由日志库来完成。值得一提的是存档有两种模式,nlog里面称之为Sequence和Rolling,在Sequence情况下:

aa.log (new)
aa.log.2 (less new)
aa.log.1
aa.log.0 (old)

而在Rolling的情况下:

aa.log (new)
aa.log.0 (less new)
aa.log.1
aa.log.2 (old)

很难说哪种更加符合人的直觉。

如果只有若干个最新的文件是有意义的,需要日志库来做主动的删除旧日志的工作。由外部程序是很难判定哪些日志是旧的。最简单的zlog的转档配置为:

*.* “aa.log”, 10MB

这个配置是Rolling的情况,每次aa.log超过10MB的时候,会做这样的重命名:

aa.log.2 -> aa.log.3
aa.log.1 -> aa.log.2
aa.log.0 -> aa.log.1
aa.log -> aa.log.0

上面的配置可以写的更加啰嗦一点:

*.* “aa.log”, 10MB * 0 ~ “aa.log.#r”

逗号后第一个参数表示文件达到多大后开始进行转档;第二个参数表示保留多少个存档文件(0代表不删除任何存档文件);第三个参数表示转档的文件名,其中#r表示存档文件的序号,r是rolling的缩写,如果写成#s,则是sequence的方式。转档文件名必须包含#r或者#s。

4.5.3 按照日志大小切分同时加上时间标签

如下的效果:

aa.log
aa.log-20070305.00.log
aa.log-20070501.00.log
aa.log-20070501.01.log
aa.log-20071008.00.log

这种情况适合于程序本身的日志一般不是很受关注,但是又在某一天想要找出来看的情况。当然,在这种情况下,万一在20070501这一天日志的量超过了指定值,例如100MB,就又要退回到第二种状态,在文件名中加后缀。其对应zlog的配置为:

*.* “aa.log”, 100MB ~ “aa-%d(%Y%m%d).#2s.log”

每到100MB的时候转档,转档文件名也支持转换字符,可以把转档当时的时间串作为转档文件名的一部分。#2s的意思是序号的长度最少为2位,从00开始编号,Sequence转档。这是zlog对转档最复杂的支持了!

4.5.4 压缩-移动-删除旧的日志

首先,压缩不应该由日志库来完成,因为压缩消耗时间和CPU。日志库的任务是配合压缩。对于第一种和第三种,管理较为简单,只要符合某些文件名规则或修改日期的,可以用shell脚本+crontab轻易的压缩、移动和删除。对于第二种,其实不是非常需要压缩,只需要删除就可以了。

如果一定需要转档的同时进行压缩,只有logrotate能干这活,毕竟它是独立的程序,能在转档的同时搞压缩,不会有混淆的问题。

4.5.6 zlog对外部转档工具的支持

zlog的转档功能已经极为强大,当然也有几种情况是zlog无法处理的,例如按时间条件进行转档,转档前后调用一些自制的shell脚本。。。这会把zlog的配置和表达式弄得过于复杂而缺乏美感。

这时候你也许喜欢用一些外部转档工具,例如logrotate来完成这些工作。问题是,在linux操作系统下,转档工具重命名日志文件名后,应用程序还是往原来的文件描述符写日志,没办法重新打开日志文件写新的日志。标准的做法是给应用程序一个信号,让它重新打开日志文件,对于syslogd是:

kill -SIGHUP ‘cat /var/run/syslogd.pid’

对于zlog,因为是个函数库,不适合接收信号。zlog提供了函数接口hs_log_reload(),这个函数会重载配置文件,重新打开所有的日志文件。应用程序在logrotate的信号,或者其他途径,例如客户端命令后,可以调用这个函数,来重新打开所有的日志文件。

五、高阶使用

5.1 MDC

MDC是什么?在log4j里面解释为Mapped Diagnostic Context。听起来是个很复杂的技术,其实MDC就是一个键-值对表。一旦某次你设置了,后面库可以帮你自动打印出来,或者成为文件名的一部分。

$ cat test_mdc.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include “hs_log.h”
int main(int argc, char **argv)
{
    int rc;
    rc = hs_log_init();
    if(rc){
        printf(“init zlog module failed\n”);
        return -1;
    }
    hs_log_info(“1.hello, zlog”);
    hs_log_put_mdc(“myname”, ”Zhang”);
    hs_log_info(“2.hello, zlog”);
    hs_log_put_mdc(“myname”, ”Li”);
    hs_log_info(“3.hello, zlog”);
    hs_log_fini();
    return 0;
}

对应的配置文件为:

$ cat test_mdc.conf
[formats]
mdc_format = “%d(%F %X.%ms)” %-6V (%c:%F:%L) [%M(myname)] - %m%n
[rules]
hs_cat.*        > stdout; mdc_format

对应的日志输出为:

2012-03-12 09:26:37.740 INFO (hs_cat:test_mdc.c:47) [] - 1.hello. zlog
2012-03-12 09:26:37.740 INFO (hs_cat:test_mdc.c:51) [zhang] - 2.hello, zlog
2012-03-12 09:26:37.740 INFO (hs_cat:test_mdc.c:55) [Li] - 3.hello, zlog

你可以看到hs_log_put_mdc()在表里面设置键“myname”对应值“Zhang”,然后在配置文件里面%M(myname)指出了在日志的哪个位置需要把值打出来。第二次,键“myname”的值被覆盖写成“Li”,然后日志里面也有相应的变化。

MDC什么时候有用呢?往往在用户需要同样的日志行为区分不同业务数据的时候。比如说,C源代码是:

hs_log_put_mdc(“customer_name”, get_customer_name_from_db());
hs_log_info(“get in”);
hs_log_info(“pick product”);
hs_log_info(“pay”);
hs_log_info(“get out”);

在配置文件里面是:

&format “%M(customer_name) %m%n”

当程序同时处理两个用户的时候,打印出来的日志可能是:

Zhang get in
Li get in
Zhang pick product
Zhang pay
Li pick product
Li pay
Zhang get out
Li get out

这样,你就可以用grep命令把这两个用户的日志分开来了。

$ grep Zhang aa.log > Zhang.log
$ grep Li aa.log > Li.log

或者,还有另外一条路,一开始在文件名里面做区分,看配置文件:

*.* “mdc_%M(customer_name).log”;

这就会产生3个日志文件:mdc_.log、mdc_Zhang.log mdc_Li.log。如果用户想知道自己在干什么,这是一条接近。

MDC是每个线程独有的,所以可以把一些线程专有的变量设置进去。如果单单为了区分线程,可以用转换字符里面的%t来搞定。

5.2 诊断zlog本身

OK,至今为止,我假定zlog库本身不出毛病的。zlog帮助用户程序写日志,帮助程序员debug程序。但是如果zlog内部出错了呢?怎么知道在哪里呢?其他的程序可以用日志库来debug,但日志库自己怎么debug?答案很简单,zlog有自己的日志——诊断日志。这个日志通常是关闭的,可以通过环境变量来打开。

$ export ZLOG_PROFILE_DEBUG=/tmp/zlog.debug.log
$ export ZLOG_PROFILE_ERROR=/tmp/zlog.error.log

诊断日志只有两个级别debug和error。设置好环境变量后,再跑test_hello程序,然后debug日志为:

$ more zlog.debug.log
03-13 09:46:56 DEBUG (7503:zlog.c:115) ------hs_log_init start, compile time[Mar 12 2012]
03-13 09:46:56 DEBUG (7503:spec.c:825) spec: [0x7fdf96b7c010][%d(%F %T)][%F %T 29][]
03-13 09:46:56 DEBUG (7503:spec.c:825) spec: [0x7fdf96b7c010][ ][ 0 ][]
. . . . . .
03-13 09:52:40 DEBUG (8139:zlog.c:291) ------hs_log_fini end------

zlog.error.log日志没有产生,因为没有错误发生。你可以看出来,debug日志展示了zlog是怎么初始化和清理的。不过在hs_log_info()执行的时候没有日志打出来,这是为了效率。

如果zlog库有任何问题,都会打印日志到ZLOG_PROFILE_ERROR所指向的错误日志。比如说,在hs_log_info()上用一个错误的printf的语法:

hs_log_info(zc, “%1”, 1);

然后编译执行程序,ZLOG_PROFILE_ERROR的日志会是:

$ cat zlog.error.log
03-13 10:04:58 ERROR (10102:buf.c:189) vsnprintf fail, errno[0]
03-13 10:04:58 ERROR (10102:buf.c:191) nwrite[-1], size_left[1024], format[%1]
03-13 10:04:58 ERROR (10102:spec.c:329) hs_log_buf_vprintf maybe fail or overflow
03-13 10:04:58 ERROR (10102:spec.c:467) z_spec->gen_buf fail
03-13 10:04:58 ERROR (10102:format.c:160) hs_log_spec_gen_msg fail
03-13 10:04:58 ERROR (10102:rule.c:265) hs_log_format_gen_msg fail
03-13 10:04:58 ERROR (10102:category.c:164) hzb_log_rule_output fail
03-13 10:04:58 ERROR (10102:zlog.c:632) hs_log_output fail, scrfile[test_hello.c]

这样,用户就能知道为啥期待的输出没有产生,然后搞定这个问题。运行时诊断会带来一定的性能损失。一般来说,我在生产环境把ZLOG_PROFILE_ERROR打开,LOG_PROFILE_DEBUG关闭。

还有另外一个办法来诊断zlog。我们都知道hs_log_init()会把所有配置信息读入内存,在整个写日志的过程中,这块内存保持不变。如果用户程序因为某种原因损坏了这块内存,那么就会造成问题。还有可能是内存中的信息和配置文件的信息不匹配。所以我设计了一个函数,把内存的信息展现到ZLOG_PROFILE_ERROR指向的错误日志。

代码见$(top_builddir)/test/test_profile.c:

$ cat test_profile.c
#include <stdio.h>
#include <hs_log.h>
int main(int argc, char **argv)
{
    int rc;
    rc = hs_log_init();
    if(rc) {
        printf(“init failed\n”);
        return -1;
    }
    hs_log_info(“hello, zlog”);
    hs_log_profile();
    hs_log_fini();
    return 0;
}

hs_log_profile()就是这个函数。配置文件非常简单:

$ cat test_profile.conf
[formats]
simple = “%m%n”
[rules]
hs_cat.*        > stdout; simple

然后zlog.error.log会是:

$ cat /tmp/zlog.error.log
06-01 11:21:26 WARN (7063:zlog.c:783) ------hs_log_profile start------
06-01 11:21:26 WARN (7063:zlog.c:784) init_flag:[1]
06-01 11:21:26 WARN (7063:conf.c:75) -conf[0x2333010]-
06-01 11:21:26 WARN (7063:conf.c:76) --global—
06-01 11:21:26 WARN (7063:conf.c:77) ---file[test_profile.conf], mtime[2012-06-01 11:23:12]
06-01 11:21:26 WARN (7063:conf.c:78) --strict init[1]---
06-01 11:21:26 WARN (7063:conf.c:79) ---buffer min[1024]---
06-01 11:21:26 WARN (7063:conf.c:80) ---buffer max[2097152]---
06-01 11:21:26 WARN (7063:conf.c:82) ---default_format---
06-01 11:21:26 WARN (7063:format.c:48) ---format[0x235ef60][default = %d(%F %T) %V  [%p:%F:%L] %m%n”]
06-01 11:21:26 WARN (7063:conf.c:85) ---file perms[0600]---
06-01 11:21:26 WARN (7063:conf.c:87) ---rotate lock file[/tmp/zlog.lock]---
06-01 11:21:26 WARN (7063:rotater.c:48) ---rotater[0x233b8d0][0x233b7d0,/tmp/zlog.lock.log]
06-01 11:21:26 WARN (7063:level_list.c:37) --level_list[0x2335490]--
06-01 11:21:26 WARN (7063:level.c:37) ---level[0x23355c0][0, *.*, 1, 6]---
06-01 11:21:26 WARN (7063:level.c:37) ---level[0x23355e0][20, DEBUG, debug, 5, 7]---
06-01 11:21:26 WARN (7063:level.c:37) ---level[0x2339600][40, INFO, info, 4, 6]---
06-01 11:21:26 WARN (7063:level.c:37) ---level[0x233b830][60, NOTICE, notice, 6, 5]---
06-01 11:21:26 WARN (7063:level.c:37) ---level[0x2339600][80, WARN, warn, 4, 4]---
06-01 11:21:26 WARN (7063:level.c:37) ---level[0x2339600][100, ERROR, error, 5, 3]---
上一篇 下一篇

猜你喜欢

热点阅读