⽇志系统模块基础、C语⾔实现⼀个⽇志模块、zlog⽇志模块基础
⽂章⽬录
⼀、⽇志系统模块基础
收集⽇志
⽇志管理的第⼀件事,就是⽇志的收集。⽇志收集是开发者必备的技巧,不管是哪个开发语⾔,哪个开发平台,⽇志收集的插件都是有很多选择的。例如:
平台⼤家钟爱的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是你最好的伙伴,如果你发现你的收集是在每个类中,到处是
try
{
......
}
catch()
{
<("......")
}
这个⼀定要避免,不管你⽤那种语⾔,错误的处理,都是可以通过全局进⾏统⼀的处理,错误⽇志也要通过全局统⼀收集。
管理⽇志
每个开发⼈员对⽇志的收集,都是⾮常熟悉的,基本都是将⽇志按照⽇期的⽅式进⾏保存,⽇常使⽤⽇志的时候,也是有⼀些要求:
1. 单个⽂件的⼤⼩要控制
因为⼤家都是通过⽇期⽅式保存的,但是因为有的⼈不重视⽇志,经常会看到有的系统单个⽇志⽂件
上百M,有的甚⾄是⼏G,⽽实际⼤家处理问题关注的都是最近的⽇志,所以控制单个⽇志⽂件的⼤⼩,对⽇志的性能以及后期的运维都是⾮常便利的。
2. ⽇志要便于浏览
⼩才便于浏览,⽇志最好能通过⽹址直接访问到,⽽不需要⼀波三折登录服务器,花10分钟下载下来,再来分析。
3. ⽇志的安全性要得到保障
⽇志内容有时会包含敏感信息,特别是error⽇志,直接把系统的具体错误抛出来,所以⽇志除了查看⽅便,还需要确保⽇志⽂件的安全。如果是⽇志⽂件是html或者txt,请⼀定记得把你的⽇志⽂件权限修改下,特定⽤户才能访问,不要随便开放,所有⼈都能访问。
4. ⽇志要定期清理
⽇志是⾮常占⽤存储的空间,⽇志太⼤对存储的性能也有⼀定的影响,所有⽇志要定期进⾏清理。
空间充⾜可以保留半年
空间不⾜最少也要保留3个⽉
当然,这个也不是⼀定的,根据每个系统的情况去制定清理计划就可以了。
如果⼤家是⼩型⽹站,⼀个系统⼀台服务器,⽇志管理就简单了。如果系统是做了⾼可⽤,后端⽤了均衡负载,那么,⽇志存在当前服务器是不太明智的做法,⽇志⼀定要统⼀存储,因为均衡负载随时都可能会切换服务器,当出现故障,你需要去⽇志究竟存在哪个服务器,也是件很浪费时间的事情。⽇志⽂件也可以通过:
来存储
定时进⾏⽂件同步来存储
⽇志存储也是对性能有⼀定影响的,⽂件同步虽然看起来⿇烦⼀定,但是⽐共享的⽅式来说,性能会好,推荐使⽤这种⽅式。
说到⽇志的同步,就不得不提Logstash这个。Logstash是现在应⽤最⼴的⽇志收集组件,基于java平台。其实很多java平台的组件,是不⽤去了解java开发的,只要简单的配置就能使⽤。
Logstash⽀持⽂件同步,也可以结合rsyslog进⾏⽂件同步,当然,也⽀持通过,与第三⽅对接,好伙伴当然是Elasticsearch。Elasticsearch下⽂也会做简单的介绍。
Logstash中⽂⼿册:
分析⽇志
⽇志的分析也是⼀个很⼤的概念,可能对于运维和安全⼈员关注的是系统的所有⽇志,包括访问⽇志、系统监测的⽇志等,但是开发⼈员对于⽇志更多的是:
监控系统运⾏错误,并获取错误时的相关数据包
记录重要的信息,某些时候便于后期检查
⼆、C语⾔实现⼀个⽇志系统模块
事实上,在C的世界⾥⾯没有特别好的⽇志函数库(就像JAVA⾥⾯的的log4j,或者C++的log4cxx)。C程序员都喜欢⽤⾃⼰的轮⼦。printf就是个挺好的轮⼦,但没办法通过配置改变⽇志的格式或者输出⽂件。syslog是个系统级别的轮⼦,不过速度慢,⽽且功能⽐较单调。log4c异常坑爹(有内存泄漏、效率低等等),⽽且已经停⽌开发。
所以,如果我们对⽇志系统模块的要求不是很⾼的话,我们可以考虑⾃⼰写⼀个。
1.C语⾔⽇志系统1
参考博客:
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);fprintf格式
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);
return0;
}
/*
*⽇志设置信息
* */
static LOGSET *getlogset(){
char path[512]={0x0};
getcwd(path,sizeof(path));
strcat(path,"/f");
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,"/");
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论