文章目录
-
- 一、日志系统模块基础
-
- 收集日志
- 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是普通日志的记录,大部分时候是无需关注的。
运营过大型系统的人都知道,除了数据库存储外,日志、图片、附件是存储的三大债主,他们是会占用非常非常大的空间,所有记录info的日志,要简洁易懂,避免空间浪费。 而对于error级别的错误,记录一定要详实,因为error的所有问题,是后期都要去解决的。
- 请求的地址
- 请求的参数
- 请求的ip
- 请求的用户
- error具体信息
- 输出的内容
- …
3. error日志一定是全局统一收集的
前文说过,error的日志,不仅是我们需要关注的,还是我需要解决掉的问题,所有error日志非常重要。错误日志的收集,必须是全局统一收集的,AOP是你最好的伙伴,如果你发现你的errorr日志收集是在每个类中,到处是
try
{
......
}
catch()
{
log.error("......")
}
这个一定要避免,不管你用那种语言,错误的处理,都是可以通过全局进行统一的处理,错误日志也要通过全局统一收集。
管理日志
每个开发人员对日志的收集,都是非常熟悉的,基本都是将日志按照日期的方式进行保存,日常使用日志的时候,也是有一些要求:
1. 单个文件的大小要控制
因为大家都是通过日期方式保存的,但是因为有的人不重视日志,经常会看到有的系统单个日志文件上百M,有的甚至是几G,而实际大家处理问题关注的都是最近的日志,所以控制单个日志文件的大小,对日志的性能以及后期的运维都是非常便利的。
2. 日志要便于浏览
日志文件小才便于浏览,日志最好能通过网址直接访问到,而不需要一波三折登录服务器,花10分钟下载下来,再来分析。
3. 日志的安全性要得到保障
日志内容有时会包含敏感信息,特别是error日志,直接把系统的具体错误抛出来,所以日志除了查看方便,还需要确保日志文件的安全。如果是日志文件是html或者txt,请一定记得把你的日志文件权限修改下,特定用户才能访问,不要随便开放,所有人都能访问。
4. 日志要定期清理
日志是非常占用存储的空间,日志太大对存储的性能也有一定的影响,所有日志要定期进行清理。
- 空间充足可以保留半年
- 空间不足最少也要保留3个月
如果大家是小型网站,一个系统一台服务器,日志管理就简单了。如果系统是做了高可用,后端用了均衡负载,那么,日志存在当前服务器是不太明智的做法,日志一定要统一存储,因为均衡负载随时都可能会切换服务器,当出现故障,你需要去找日志究竟存在哪个服务器,也是件很浪费时间的事情。日志文件也可以通过:
- 共享虚拟目录来存储
- 定时进行文件同步来存储
日志存储也是对性能有一定影响的,文件同步虽然看起来麻烦一定,但是比共享虚拟目录的方式来说,性能会好,推荐使用这种方式。
Logstash支持文件同步,也可以结合rsyslog进行文件同步,当然,也支持通过tcp协议,与第三方对接,好伙伴当然是Elasticsearch。Elasticsearch下文也会做简单的介绍。
Logstash中文手册:点击这里
分析日志
日志的分析也是一个很大的概念,可能对于运维和安全人员关注的是系统的所有日志,包括访问日志、系统监测的日志等,但是开发人员对于日志更多的是:
- 监控系统运行错误,并获取错误时的相关数据包
- 记录重要的信息,某些时候便于后期检查
所以,如果我们对日志系统模块的要求不是很高的话,我们可以考虑自己写一个。
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也行。
#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
- 写一个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; }
- 写一个配置文件,放在和test_hello.c同样的目录下:
[formats]simple = "%m%n"[rules]my_cat.DEBUG>stdout; simple
- 编译、然后运行!
$ 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
#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是等效的。
推荐阅读
- C语言|推荐一些嵌入式、C/C++的开源库和项目
- 杂记|整数的不同进制
- c++ 文件输出及cin.setf()和cin.precision()学习
- cout.setf() 与 cout.precision()
- 十万个为什么|口诀快速记住C语言中的运算符优先级
- bug|一次由于传递局部变量指针产生的bug
- linux|linux进程、线程常用的几个基础知识点
- C|C语言——知识点汇总
- c/c++技术|小白 C++ 入门到大神发疯学习路线