C/C++开源库|日志系统模块基础、C语言实现一个日志模块、zlog日志模块基础


文章目录

    • 一、日志系统模块基础
      • 收集日志
      • 1. 日志等级一定要规范
      • 2. error日志内容一定要详实 ,info日志要简洁易懂
      • 3. error日志一定是全局统一收集的
      • 管理日志
      • 1. 单个文件的大小要控制
      • 2. 日志要便于浏览
      • 3. 日志的安全性要得到保障
      • 4. 日志要定期清理
      • 分析日志
    • 二、C语言实现一个日志系统模块
      • 1.C语言日志系统1
      • 2.C语言日志系统2
      • 3.zlog日志系统
        • Chapter 1 zlog是什么?
        • Chapter 2 zlog不是什么?
        • Chapter 3 Hello World
          • 3.1 编译和安装zlog
          • 3.2 应用程序调用和链接zlog
          • 3.3 Hello World 代码
          • 3.4 更简单的Hello World
        • Chapter 4 Syslog 模型
          • 4.1 分类(Category)、规则(Rule)和格式(Format)
          • 4.2 syslog模型和log4j模型的区别
        • Chapter 5 配置文件

一、日志系统模块基础 收集日志
日志管理的第一件事,就是日志的收集。日志收集是开发者必备的技巧,不管是哪个开发语言,哪个开发平台,日志收集的插件都是有很多选择的。例如:
.net 平台大家钟爱的log4net,支持多种存储方式(文件、数据库),多种格式,多种日志拆分方式。
java 平台主流的log4j、slf4j、logback,多种选择。
日志收集的组件这里就不一一说明了,使用都是很简单的,这里重点说明一下,日志我们收集应该注意的地方:
1. 日志等级一定要规范
等级说明debug调试信息info用来收集关注的信息warn警告信息error错误信息
好多开发工程师记录日志总是喜欢用info级别来记录日志,一般的组件默认级别都是info,所有info默认都是会被记录的,而debug信息发布后,是不会被记录的。这是一种偷懒的做法,但这也是很普遍的做法。正确的方式应该根据日志本身的特性去设置日志的级别,其实规范的日志级别是非常重要的:
  • 正确的级别便于运维。便于统一调整系统日志级别,如特殊情况可以只记录error错误
  • 没有正确的级别,对后期日志分析和处理是留下很大的隐患。error是需要去关注,并且处理掉的问题。info是普通日志的记录,大部分时候是无需关注的。
2. error日志内容一定要详实 ,info日志要简洁易懂
运营过大型系统的人都知道,除了数据库存储外,日志、图片、附件是存储的三大债主,他们是会占用非常非常大的空间,所有记录info的日志,要简洁易懂,避免空间浪费。 而对于error级别的错误,记录一定要详实,因为error的所有问题,是后期都要去解决的。
  • 请求的地址
  • 请求的参数
  • 请求的ip
  • 请求的用户
  • error具体信息
  • 输出的内容
为了能很好的反馈当时error产生场景,以上的这些内容都应该被记录,而且越详细越好。
3. error日志一定是全局统一收集的
前文说过,error的日志,不仅是我们需要关注的,还是我需要解决掉的问题,所有error日志非常重要。错误日志的收集,必须是全局统一收集的,AOP是你最好的伙伴,如果你发现你的errorr日志收集是在每个类中,到处是
try { ...... } catch() { log.error("......") }

这个一定要避免,不管你用那种语言,错误的处理,都是可以通过全局进行统一的处理,错误日志也要通过全局统一收集。
管理日志
每个开发人员对日志的收集,都是非常熟悉的,基本都是将日志按照日期的方式进行保存,日常使用日志的时候,也是有一些要求:
1. 单个文件的大小要控制
因为大家都是通过日期方式保存的,但是因为有的人不重视日志,经常会看到有的系统单个日志文件上百M,有的甚至是几G,而实际大家处理问题关注的都是最近的日志,所以控制单个日志文件的大小,对日志的性能以及后期的运维都是非常便利的。
2. 日志要便于浏览
日志文件小才便于浏览,日志最好能通过网址直接访问到,而不需要一波三折登录服务器,花10分钟下载下来,再来分析。
3. 日志的安全性要得到保障
日志内容有时会包含敏感信息,特别是error日志,直接把系统的具体错误抛出来,所以日志除了查看方便,还需要确保日志文件的安全。如果是日志文件是html或者txt,请一定记得把你的日志文件权限修改下,特定用户才能访问,不要随便开放,所有人都能访问。
4. 日志要定期清理
日志是非常占用存储的空间,日志太大对存储的性能也有一定的影响,所有日志要定期进行清理。
  • 空间充足可以保留半年
  • 空间不足最少也要保留3个月
当然,这个也不是一定的,根据每个系统的情况去制定清理计划就可以了。
如果大家是小型网站,一个系统一台服务器,日志管理就简单了。如果系统是做了高可用,后端用了均衡负载,那么,日志存在当前服务器是不太明智的做法,日志一定要统一存储,因为均衡负载随时都可能会切换服务器,当出现故障,你需要去找日志究竟存在哪个服务器,也是件很浪费时间的事情。日志文件也可以通过:
  • 共享虚拟目录来存储
  • 定时进行文件同步来存储
    日志存储也是对性能有一定影响的,文件同步虽然看起来麻烦一定,但是比共享虚拟目录的方式来说,性能会好,推荐使用这种方式。
【C/C++开源库|日志系统模块基础、C语言实现一个日志模块、zlog日志模块基础】说到日志的同步,就不得不提Logstash这个日志组件。Logstash是现在应用最广的日志收集组件,基于java平台。其实很多java平台的组件,是不用去了解java开发的,只要简单的配置就能使用。
Logstash支持文件同步,也可以结合rsyslog进行文件同步,当然,也支持通过tcp协议,与第三方对接,好伙伴当然是Elasticsearch。Elasticsearch下文也会做简单的介绍。
Logstash中文手册:点击这里
分析日志
日志的分析也是一个很大的概念,可能对于运维和安全人员关注的是系统的所有日志,包括访问日志、系统监测的日志等,但是开发人员对于日志更多的是:
  • 监控系统运行错误,并获取错误时的相关数据包
  • 记录重要的信息,某些时候便于后期检查
二、C语言实现一个日志系统模块 事实上,在C的世界里面没有特别好的日志函数库(就像JAVA里面的的log4j,或者C++的log4cxx)。C程序员都喜欢用自己的轮子。printf就是个挺好的轮子,但没办法通过配置改变日志的格式或者输出文件。syslog是个系统级别的轮子,不过速度慢,而且功能比较单调。log4c异常坑爹(有内存泄漏、效率低等等),而且已经停止开发。
所以,如果我们对日志系统模块的要求不是很高的话,我们可以考虑自己写一个。
1.C语言日志系统1
参考博客:用C语言打印日志(Log)
log.h 文件:
/** log.h **/ #ifndef __LOG_H__ #define __LOG_H__ #include "stdio.h" #include "string.h" #include "stdlib.h" #include "time.h" #include "stdarg.h" #include "unistd.h" #define MAXLEN (2048) #define MAXFILEPATH (512) #define MAXFILENAME (50) typedef enum{ ERROR_1=-1, ERROR_2=-2, ERROR_3=-3 }ERROR0; typedef enum{ NONE=0, INFO=1, DEBUG=2, WARN=3, ERROR=4, ALL=255 }LOGLEVEL; typedef struct log{ char logtime[20]; char filepath[MAXFILEPATH]; FILE *logfile; }LOG; typedef struct logseting{ char filepath[MAXFILEPATH]; unsigned int maxfilelen; unsigned char loglevel; }LOGSET; int LogWrite(unsigned char loglevel,char *fromat,...); #endif /* __LOG_H__ */

log.c 文件:
/** log.c **/ #include "log.h" #define MAXLEVELNUM (3) LOGSET logsetting; LOG loging; const static char LogLevelText[4][10]={"INFO","DEBUG","WARN","ERROR"}; static char * getdate(char *date); static unsigned char getcode(char *path){ unsigned char code=255; if(strcmp("INFO",path)==0) code=1; else if(strcmp("WARN",path)==0) code=3; else if(strcmp("ERROR",path)==0) code=4; else if(strcmp("NONE",path)==0) code=0; else if(strcmp("DEBUG",path)==0) code=2; return code; } static unsigned char ReadConfig(char *path){ char value[512]={0x0}; char data[50]={0x0}; FILE *fpath=fopen(path,"r"); if(fpath==NULL) return -1; fscanf(fpath,"path=%s\n",value); getdate(data); strcat(data,".log"); strcat(value,"/"); strcat(value,data); if(strcmp(value,logsetting.filepath)!=0) memcpy(logsetting.filepath,value,strlen(value)); memset(value,0,sizeof(value)); fscanf(fpath,"level=%s\n",value); logsetting.loglevel=getcode(value); fclose(fpath); return 0; } /* *日志设置信息 * */ static LOGSET *getlogset(){ char path[512]={0x0}; getcwd(path,sizeof(path)); strcat(path,"/log.conf"); if(access(path,F_OK)==0){ if(ReadConfig(path)!=0){ logsetting.loglevel=INFO; logsetting.maxfilelen=4096; } }else{ logsetting.loglevel=INFO; logsetting.maxfilelen=4096; } return &logsetting; } /* *获取日期 * */ static char * getdate(char *date){ time_t timer=time(NULL); strftime(date,11,"%Y-%m-%d",localtime(&timer)); return date; } /* *获取时间 * */ static void settime(){ time_t timer=time(NULL); strftime(loging.logtime,20,"%Y-%m-%d %H:%M:%S",localtime(&timer)); } /* *不定参打印 * */ static void PrintfLog(char * fromat,va_list args){ int d; char c,*s; while(*fromat) { switch(*fromat){ case 's':{ s = va_arg(args, char *); fprintf(loging.logfile,"%s",s); break; } case 'd':{ d = va_arg(args, int); fprintf(loging.logfile,"%d",d); break; } case 'c':{ c = (char)va_arg(args, int); fprintf(loging.logfile,"%c",c); break; } default:{ if(*fromat!='%'&&*fromat!='\n') fprintf(loging.logfile,"%c",*fromat); break; } } fromat++; } fprintf(loging.logfile,"%s","\n"); } static int initlog(unsigned char loglevel){ char strdate[30]={0x0}; LOGSET *logsetting; //获取日志配置信息 if((logsetting=getlogset())==NULL){ perror("Get Log Set Fail!"); return -1; } if((loglevel&(logsetting->loglevel))!=loglevel) return -1; memset(&loging,0,sizeof(LOG)); //获取日志时间 settime(); if(strlen(logsetting->filepath)==0){ char *path=getenv("HOME"); memcpy(logsetting->filepath,path,strlen(path)); getdate(strdate); strcat(strdate,".log"); strcat(logsetting->filepath,"/"); strcat(logsetting->filepath,strdate); } memcpy(loging.filepath,logsetting->filepath,MAXFILEPATH); //打开日志文件 if(loging.logfile==NULL) loging.logfile=fopen(loging.filepath,"a+"); if(loging.logfile==NULL){ perror("Open Log File Fail!"); return -1; } //写入日志级别,日志时间 fprintf(loging.logfile,"[%s] [%s] ",LogLevelText[loglevel-1],loging.logtime); return 0; } /* *日志写入 * */ int LogWrite(unsigned char loglevel,char *fromat,...) { intrtv = -1; va_list args; //[为支持多线程需要加锁] pthread_mutex_lock(&mutex_log); //lock. do{ //初始化日志 if(initlog(loglevel) != 0) { rtv = -1; break; } //打印日志信息 va_start(args,fromat); PrintfLog(fromat,args); va_end(args); //文件刷出 fflush(loging.logfile); //日志关闭 if(loging.logfile!=NULL) fclose(loging.logfile); loging.logfile=NULL; rtv = 0; }while(0); //[为支持多线程需要加锁] pthread_mutex_unlock(&mutex_log); //unlock. return rtv; }

test.c 文件:
/** test.c **/ #include "stdio.h" #include "stdlib.h" #include "log.h" int main(int argv,char**argc){ printf("%s\n",argc[0]); LogWrite(INFO,"%s","Hello World!"); LogWrite(DEBUG,"%s","H.e.l.l.o W.o.r.l.d!"); LogWrite(WARN,"%s","H e l l o W o r l d!"); LogWrite(ERROR,"%s","Hallo World!"); return 0; }

path=./temp level=ALL

该系统需要在类Unix系统下使用。
下面我演示一下在我的Ubuntu云服务器上的使用方法。
1. 按照上面的要求新建代码文件和目录 root@iZ2zeaqy08amcq9fk440kyZ:/code/log1# tree . ├── log.c ├── log.conf ├── log.h ├── temp └── test.c2. 编译 root@iZ2zeaqy08amcq9fk440kyZ:/code/log1# gcc log.c test.c -o out3. 运行 root@iZ2zeaqy08amcq9fk440kyZ:/code/log1# ./out 4. 可打开temp目录下的日志文件看看 root@iZ2zeaqy08amcq9fk440kyZ:/code/log1# tree . ├── log.c ├── log.conf ├── log.h ├── out ├── temp │└── 2021-12-13.log └── test.c5. temp目录下的2021-12-13.log文件既是我们写入的日志文件 文件中的内容如下: [INFO] [2021-12-13 21:18:25] Hello World! [DEBUG] [2021-12-13 21:18:25] H.e.l.l.o W.o.r.l.d! [WARN] [2021-12-13 21:18:25] H e l l o W o r l d! [ERROR] [2021-12-13 21:18:25] Hallo World! [INFO] [2021-12-13 21:18:59] Hello World! 可以看到记录了日志等级,日期时间,打印信息。

可以看到这个日志系统还是很小巧易用的,适合与在有文件系统的类Unix系统中。
2.C语言日志系统2
下面这个日志系统是打印出来的:C语言日志分级设计
/**************************************************************** ***Author: lishuangliang*** ***Email: lishuangliang@outlook.com*** ***Date: 2018-09-24*** ***Festivsl: Mid-autumn*** *****************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include //通过宏来控制是否打开日志输出 #ifdef DISABLE_DEBUG #define real_debug_level 0 #else #define real_debug_level debug_level #endif //定义日志输出级别 #defineFATALEER(1<<0) #defineERROR(1<<1) #defineWRAN(1<<2) #defineINFO(1<<3) #defineDEBUG(1<<4) #define WriteLog(level,mesg) log_mesg_printf(__FILE__, __LINE__, __func__, level, mesg) #define WriteLog2(level,format, arg...) log_mesg_printf2( __FILE__,__FUNCTION__, __LINE__, level, format, ##arg) int debug_level = 0; struct dbg { int level; const char *mesg; }; static struct dbg debug_level_table[] = { {FATALEER, "Config The Log Level as FATALEER"}, {ERROR, "Config The Log Level as ERROR"}, {WRAN, "Config The Log Level as WRAN"}, {INFO, "Config The Log Level as INFO"}, {DEBUG, "Config The Log Level as DEBUG"} }; void print_debug_usage(void) { struct dbg *p; fprintf(stderr, "To calculate the debug level, logically 'or'\n" "some of the following values together to get a debug level:\n"); for (p = debug_level_table; p < debug_level_table + (sizeof (debug_level_table) / sizeof (struct dbg)); p++) { fprintf(stderr, "\t%d:\t%s\n", p->level, p->mesg); } fprintf(stderr, "\n"); } void parse_debug(char *foo) { int i; struct dbg *p; if (!foo) return; fprintf(stderr, "Before parse_debug, debug_level is: %d\n", debug_level); i = atoi(foo); if(i == -1) { /* error */ fprintf(stderr, "Invalid level specified.\n"); exit(0); } for (p = debug_level_table; p < debug_level_table +(sizeof (debug_level_table) / sizeof (struct dbg)); p++) { if (i > 0) { if (i & p->level) { fprintf(stderr, "Enabling %s debug level.\n",p->mesg); debug_level |= p->level; } } } fprintf(stderr, "After parse_debug, debug_level is: %d\n", debug_level); } char *get_commonlog_time(void) { char *p; char sys_time[64]; time_t tnow = time(NULL); struct tm *ptm = localtime(&tnow); memset(sys_time, 0 ,sizeof(sys_time)); sprintf(sys_time, "%04d-%02d-%02d %02d:%02d:%02d",ptm->tm_year+1900 ,ptm->tm_mon+1 ,ptm->tm_mday ,ptm->tm_hour ,ptm->tm_min ,ptm->tm_sec); //return (char *)sys_time; p = sys_time; return p; } void log_mesg_printf(const char *file, int line, const char *func,int level, const char *mesg) { if(real_debug_level & level) { int errno_save = errno; fprintf(stderr, "%s%s:%d (%s) - ", get_commonlog_time(), file, line, func); errno = errno_save; perror(mesg); errno = errno_save; } } void log_mesg_printf2(const char *file,const char *func,const int line, int level, char *fmt,...) { if(real_debug_level & level) { char msg_buf[20*1024]; va_list ap; va_start(ap,fmt); sprintf(msg_buf,"[%s%s:%s:%d] ",get_commonlog_time(),file,func,line); vsprintf(msg_buf+strlen(msg_buf),fmt,ap); fprintf(stderr,"%s\n",msg_buf); va_end(ap); } } int main(int argc, char* argv[]) { #ifdef DISABLE_DEBUG print_debug_usage(); parse_debug(argv[1]); //解析日志打印输出级别 #endif //不使用可变参数解析样例 WriteLog(DEBUG,"I want to DEBUG"); WriteLog(INFO,"I want to INFO"); WriteLog(WRAN,"I want to WARN"); WriteLog(ERROR,"I want to ERROR"); WriteLog(FATALEER,"I want to FATALEER"); //使用可变参数解析样例 WriteLog2(DEBUG,"I want to %s which level is %d","DEBUG",DEBUG); WriteLog2(INFO,"I want to %s which level is %d","INFO",INFO); WriteLog2(WRAN,"I want to %s which level is %d","WRAN",WRAN); WriteLog2(ERROR,"I want to %s which level is %d","ERROR",ERROR); WriteLog2(FATALEER,"I want to %s which level is %d","FATALEER",FATALEER); return 0; }

3.zlog日志系统
Chapter 1 zlog是什么? zlog是一个高可靠性、高性能、线程安全、灵活、概念清晰的纯C日志函数库。
事实上,在C的世界里面没有特别好的日志函数库(就像JAVA里面的的log4j,或者C++的log4cxx)。C程序员都喜欢用自己的轮子。printf就是个挺好的轮子,但没办法通过配置改变日志的格式或者输出文件。syslog是个系统级别的轮子,不过速度慢,而且功能比较单调。
所以我写了zlog。
zlog在效率、功能、安全性上大大超过了log4c,并且是用c写成的,具有比较好的通用性。
zlog有这些特性:
  • syslog分类模型,比log4j模型更加直接了当
  • 日志格式定制,类似于log4j的pattern layout
  • 多种输出,包括动态文件、静态文件、stdout、stderr、syslog、用户自定义输出函数
  • 运行时手动、自动刷新配置文件(同时保证安全)
  • 高性能,在我的笔记本上达到25万条日志每秒, 大概是syslog(3)配合rsyslogd的1000倍速度
  • 用户自定义等级
  • 多线程和多进程环境下保证安全转档
  • 精确到微秒
  • 简单调用包装dzlog(一个程序默认只用一个分类)
  • MDC,线程键-值对的表,可以扩展用户自定义的字段
  • 自诊断,可以在运行时输出zlog自己的日志和配置状态
  • 不依赖其他库,只要是个POSIX系统就成(当然还要一个C99兼容的vsnprintf)
相关链接:
主页:http://hardysimpson.github.com/zlog/
下载:https://github.com/HardySimpson/zlog/releases
其实之前我已经写过一篇博客来讲解zlog的简单使用:【C语言开源库】C语言开源库zlog的使用
Chapter 2 zlog不是什么? zlog的目标是成为一个简而精的日志函数库,不会直接支持网络输出或者写入数据库,不会直接支持日志内容的过滤和解析。
原因很明显,日志库是被应用程序调用的,所有花在日志库上的时间都是应用程序运行时间的一部分,而上面说的这些操作都很费时间,会拖慢应用程序的速度。这些事儿应该在别的进程或者别的机器上做。
如果你需要这些特性,我建议使用rsyslog、zLogFabric、Logstash,这些日志搜集、过滤、存储软件,当然这是单独的进程,不是应用程序的一部分。
目前zlog已经支持7.4,可以自己实现一个输出函数,自由的把日志输出到其他进程或者其他机器。而把日志的分类匹配、日志格式成型的工作交给zlog。
目前我的想法是实现一个zlog-redis客户端,用自定义输出功能,把日志存储到本机或者远程的redis服务器内,然后用其他进程(也使用zlog库)来把日志写到文件里面,不知大家以为这个想法如何?欢迎和我联系探讨。
Chapter 3 Hello World 3.1 编译和安装zlog 下载[zlog-latest-stable.tar.gz](file://https://github.com/HardySimpson/zlog/archive/latest-stable.tar.gz)
$ tar -zxvf zlog-latest-stable.tar.gz $ cd zlog-latest-stable/$ make $ sudo make installor$ sudo make PREFIX=/usr/local/ install

PREFIX指明了安装的路径,安转完之后为了让你的程序能找到zlog动态库
$ sudo vi /etc/ld.so.conf /usr/local/lib$ sudo ldconfig

在你的程序运行之前,保证libzlog.so在系统的动态链接库加载器可以找到的目录下。上面的命令适用于linux,别的系统自己想办法。
  • 除了一般的make以外,还可以
$ make 32bit # 32bit version on 64bit machine, libc6-dev-i386 is needed $ make noopt # without gcc optimization$ make doc# lyx and hevea is needed$ make test# test code, which is also good example for zlog

  • makefile是用GNU make的格式写的,所以在你的平台上需要预装gnu make和gcc。或者,手工修改一个自己平台的makefile也行。
3.2 应用程序调用和链接zlog 应用程序使用zlog很简单,只要在C文件里面加一行。
#include "zlog.h"
链接zlog需要pthread库,命令是:
$ cc -c -o app.o app.c -I/usr/local/include # -I[where zlog.h is put]$ cc -o app app.o -L/usr/local/lib -lzlog -lpthread# -L[where libzlog.so is put]

3.3 Hello World 代码 这些代码在$(top_builddir)/test/test_hello.c, test_hello.conf
  1. 写一个C文件:
    #include#include "zlog.h" int main(int argc, char** argv){int rc; zlog_category_t *c; rc = zlog_init("test_hello.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; }

  2. 写一个配置文件,放在和test_hello.c同样的目录下:
    [formats]simple = "%m%n"[rules]my_cat.DEBUG>stdout; simple

  3. 编译、然后运行!
    $ cc -c -o test_hello.o test_hello.c -I/usr/local/include $ cc -o test_hello test_hello.o -L/usr/local/lib -lzlog$ ./test_hellohello, zlog

3.4 更简单的Hello World 这个例子在$(top_builddir)/test/test_default.c, test_default.conf. 源代码是:
#include #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; }

配置文件是test_default.conf,和test_hello.conf一模一样,最后执行程序的输出也一样。区别在于这里用了dzlog API,内含一个默认的zlog_category_t。详见6.5。
Chapter 4 Syslog 模型 4.1 分类(Category)、规则(Rule)和格式(Format) zlog有3个重要的概念:分类(Category)、规则(Rule)和格式(Format)。
分类(Category)用于区分不同的输入。代码中的分类变量的名字是一个字符串,在一个程序里面可以通过获取不同的分类名的category用来后面输出不同分类的日志,用于不同的目的。
格式(Format)是用来描述输出日志的格式,比如是否有带有时间戳,是否包含文件位置信息等,上面的例子里面的格式simple就是简单的用户输入的信息+换行符。
规则(Rule)则是把分类、级别、输出文件、格式组合起来,决定一条代码中的日志是否输出,输出到哪里,以什么格式输出。
所以,当程序执行下面的语句的时候
zlog_category_t *c; c = zlog_get_category("my_cat"); zlog_info(c, "hello, zlog");

zlog会找到c的名字是"my_cat",对应的配置文件中的规则是
[rules] my_cat.DEBUG>stdout; simple

然后库会检查,目前这条日志的级别是否符合规则中的级别来决定是否输出。因为INFO>=DEBUG,所以这条日志会被输出。并且根据这条规则,会被输出到stdout(标准输出) ,输出的格式是simple,在配置文件中定义是
[formats] simple = "%m%n"

最后在屏幕上打印
hello, zlog

这就是整个过程。用户要做就是写自己的信息。日志往哪里输出,以什么格式输出,都是库和配置文件来完成的。
4.2 syslog模型和log4j模型的区别 好,那么目前这个模型和syslog有什么关系呢?至今为止,这个模型还是比较像log4j。log4j的模型里面有logger, appender和layout。区别在于,在log4j里面,代码中的logger和配置中的logger是一一对应的,并且一个logger有唯一的级别。一对一关系是log4j, log4cxx, log4cpp, log4cplus, log4net的唯一选择。
但这种模型是不灵活的,他们发明了过滤器(filters)来弥补,但这只能把事情弄得更加混乱。所以让我们把目光转回syslog的模型,这是一个设计的很简易正确的模型。
继续上一节的例子,如果在zlog的配置文件中有这么2行规则:
[rules] my_cat.DEBUG>stdout; simplemy_cat.INFO>stdout;

然后,一行代码会产生两行输出:
hello, zlog 2012-05-29 10:41:36 INFO [11288:test_hello.c:41] hello, zlog

现在一个代码中的分类对应配置文件中的两条规则。log4j的用户可能会说:"这很好,但是只要在log4j里面放两个appender也能做的一样。"所以继续看下一个例子:
[rules] my_cat.WARN"/var/log/aa.log"my_cat.DEBUG"/var/log/bb.log"

代码是:
zlog_info(c, "info, zlog"); zlog_debug(c, "debug, zlog");

最后,在aa.log中只有一条日志
2012-05-29 10:41:36 INFO [11288:test_hello.c:41] info, zlog

但在bb.log里面有两条
2012-05-29 10:41:36 INFO [11288:test_hello.c:41] info, zlog 2012-05-29 10:41:36 DEBUG [11288:test_hello.c:42] debug, zlog

从这个例子能看出来区别。log4j无法轻易的做到这一点。在zlog里面,一个分类可以对应多个规则,每个规则有自己的级别、输出和格式。这就让用户能按照需求过滤、多渠道输出自己的所有日志。
Chapter 5 配置文件 大部分的zlog的行为取决于配置文件:把日志打到哪里去,用什么格式,怎么转档。配置文件是zlog的黑话,我尽量把这个黑话设计的简单明了。这是个配置文件例子:
# comments [global]strict init = truebuffer min = 1024buffer max = 2MBrotate lock file = /tmp/zlog.lockdefault format = "%d.%us %-6V (%c:%F:%L) - %m%n"file perms = 600 [levels]TRACE = 10CRIT = 130, LOG_CRIT [formats]simple = "%m%n"normal = "%d %m%n" [rules]default.*>stdout; simple*.*"%12.2E(HOME)/log/%c.log", 1MB*12; simplemy_.INFO>stderr; my_cat.!ERROR"/var/log/aa.log"my_dog.=DEBUG>syslog, LOG_LOCAL0; simplemy_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 byte

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

    推荐阅读