日志系统的设计
1、背景及概述
1.1、背景
日志系统作为各个产品平台的必要功能,具有普遍性和必要性,车载项目中需要设计一种日志系统实现分应用采集日志。
1.2、概述
该日志系统主要具备五个主要功能:①分应用存储日志;②区分日志级别;③定位日志信息产生位置;④显示日志产生时间;⑤日志到达特定大小后转储。
2、日志系统部署使用
2.1、目录结构
本日志系统的目录结构如下所示。
2.2、使用方法
①将Makefile里面的编译器改为自己需要的编译器,编译将生成的liblog.so拷贝到自己的lib文件下,声明指定该动态库的路径。
②将log.conf拷贝到使用日志处理接口的当前目录下。
③使用接口LogWrite(“app1”,INFO,FILE,LINE,“%s”,“Hello World!”);完成日志的书写。
2.3、结果验证
①运行./build.sh进行编译运行日志采集。
②查看分应用生成日志。
③日志转储功能校验
④日志内容格式校验
主要文件
1、logtest.c
/** test.c **/
#include "stdio.h"
#include "stdlib.h"
#include "log.h"
int main(int argv,char**argc){
printf("%s\n",argc[0]);
LogWrite("app1",INFO,__FILE__,__LINE__,"%s","Hello World!");
LogWrite("app3",DEBUG,__FILE__,__LINE__,"%s","H.e.l.l.o W.o.r.l.d!");
LogWrite("app4",WARN,__FILE__,__LINE__,"%s","H e l l o W o r l d!");
LogWrite("app2",ERROR,__FILE__,__LINE__,"%s","Hallo World!");
return 0;
}
2、log.conf
path=/carLog
level=ALL
# path=./temp
上述文件都存放在logtest文件夹下
4、log.c
/** log.c **/
#include "log.h"
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 *app,int seq){
char value[128]={0x0};
char data[50]={0x0};
char seqnum[50];
sprintf(seqnum, "%d", seq);
FILE *fpath=fopen(path,"r");
if(fpath==NULL)
return -1;
fscanf(fpath,"path=%s\n",value);
strcat(value,"/");
strcat(value,app);
if (access(value,F_OK)!=0){
mkdir(value,0755);
}
getdate(data);
strcat(data,"-");
strcat(data,seqnum);
strcat(data,".log");
strcat(value,"/");
strcat(value,app);
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 void my_system(const char *cmd,char *ret)
{
char result[1024] = {0};
char buf[1024] = {0};
FILE *fp = NULL;
if( (fp = popen(cmd, "r")) == NULL ) {
printf("popen error!\n");
return;
}
while (fgets(buf, sizeof(buf), fp)) {
strcat(result, buf);
}
pclose(fp);
strcpy(ret, result);
return;
}
/*
*获取当前正在写入的日志名称
* */
static void getCurLog(char *app,char maxLog[])
{
char str[1024] = {0};
char cmd[64] = "ls -r /carLog/";
char path[32]="/carLog/";
int n=0;
char lognum[100][100];
strcat(path,app);
if (access(path,F_OK)!=0){
mkdir(path,0755);
}
strcat(cmd,app);
my_system(cmd,str);
if(strlen(str)==0)
{
printf("no log\n");
return;
}
strcat(cmd," > /tmp/tmp.log");
my_system(cmd,str);
char cmd1[64] ="awk 'BEGIN{ORS=\",\"}{print $0}' /tmp/tmp.log";
my_system(cmd1,str);
char s[4] = ",";
char *token;
char *next_token = NULL;
/* 获取第一个子字符串 */
token = strtok_r(str, s, &next_token);
/* 继续获取其他的子字符串 */
while (token != NULL) {
strcpy(lognum[n++],token);
token = strtok_r(NULL, s, &next_token);
}
char max[64];
char tmp[64];
strcpy(max,lognum[0]);
for(int i=0;i<n;i++)
{
strcpy(tmp,lognum[i]);
if(((strlen(max)==strlen(tmp))&&strcmp(max,tmp)>0)||strlen(max)>strlen(tmp)){
continue;
}else{
strcpy(max,tmp);
}
}
strcpy(maxLog,max);
}
/*
*获取当前写入文件的序列号
* */
static int getCurSeq(char maxLog[])
{
int curSeq;
char str[64];
char s[4] = ".log";
char *token;
char *next_token = NULL;
token = strtok_r(maxLog, s, &next_token);
strncat(str,&token[16],strlen(token)-16);
sscanf(str, "%d", &curSeq);
return curSeq;
}
/*
*获取待写入文件的序列号
* */
static void getSeq(char *app,char maxLog[],int *seq)
{
int curSeq;
int logSize;
char str[128];
char cmd_str[64]="du -k /carLog/";
if(strlen(maxLog)==0)
{
curSeq=1;
}
else
{
strcat(cmd_str,app);
strcat(cmd_str,"/");
strcat(cmd_str,maxLog);
strcat(cmd_str,"|awk '{print $1}'");
my_system(cmd_str,str);
sscanf(str,"%d",&logSize);
curSeq=getCurSeq(maxLog);
if(MAXLOGSIZE<logSize)
{
curSeq++;
}
}
*seq=curSeq;
}
/*
*日志设置信息
* */
static LOGSET *getlogset(char *app,int seq){
char path[128]={0x0};
getcwd(path,sizeof(path));
strcat(path,"/log.conf");
if(access(path,F_OK)==0){
if(ReadConfig(path,app,seq)!=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(char *app,unsigned char loglevel,char *file,int line){
char strdate[32]={0x0};
LOGSET *logsetting;
char maxLog[64]={};
int seq;
int ret;
getCurLog(app,maxLog);
getSeq(app,maxLog,&seq);
//获取日志配置信息
if((logsetting=getlogset(app,seq))==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));
strcat(logsetting->filepath,"/");
strcat(logsetting->filepath,app);
if (access(logsetting->filepath,F_OK)!=0){
mkdir(logsetting->filepath,0755);
}
char seqnum[50];
sprintf(seqnum, "%d", seq);
getdate(strdate);
strcat(strdate,"-");
strcat(strdate,seqnum);
strcat(strdate,".log");
strcat(logsetting->filepath,"/");
strcat(logsetting->filepath,app);
strcat(logsetting->filepath,"-");
strcat(logsetting->filepath,strdate);
}
memcpy(loging.filepath,logsetting->filepath,MAXFILEPATH);
memset(logsetting->filepath, 0, sizeof(logsetting->filepath));;
//打开日志文件
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] [%s] [%s] [%d] ",loging.logtime,LogLevelText[loglevel-1],app,file,line);
return 0;
}
/*
*日志写入
* */
int LogWrite(char *app,unsigned char loglevel,char *file,int line,char *fromat,...)
{
int rtv = -1;
va_list args;
//[为支持多线程需要加锁] pthread_mutex_lock(&mutex_log); //lock.
do{
//初始化日志
if(initlog(app,loglevel,file,line) != 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;
}
5、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 MAXLEVELNUM (3)
#define MAXLOGSIZE (2)
#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(char *app,unsigned char loglevel,char *file,int line,char *fromat,...);
#endif /* __LOG_H__ */
6、Makefile
#CROSS_COMPILE = aarch64-none-linux-gnu-
CROSS_COMPILE =
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
SOURCE_ROOT = $(shell pwd)
CFLAGS := -Wall -O2 -fno-builtin
CPPFLAGS := -lm
INCLUDE_DIR := -I $(SOURCE_ROOT)/ \
-I$(SOURCE_ROOT)/include
APP_NAME=liblog.so
all: $(APP_NAME)
APP_OBJECTC += log.c
STATIC_OBJ_O = $(patsubst %.c, %.o, $(APP_OBJECTC))
STATIC_OBJ_C = $(foreach file, $(STATIC_OBJ_O), $(file) )
$(STATIC_OBJ_C) : %.o:%.c
$(CC) $(INCLUDE_DIR) $(CPPFLAGS) -c -fPIC $(APP_OBJECTC)
$(APP_NAME): $(STATIC_OBJ_C)
$(CC) -shared $(CPPFLAGS) -o $(APP_NAME) ./*.o
clean:
@rm -f *.o *.so
.PHONY: clean
以上是存放在loglib里面。
注:本文档参考网上其他大神的文档二次加工增加了一下自己的东西进去,仅供参考。