Linux C语言实现日志模块demo
有时候在调试编写程序的时候日志打印模块是必不可少的,今天我们在Linux下用C语言实现一个模块化的日志demo小玩具
一、一共有以下几个文件:
1). 模块功能的代码实现c文件 --log.c 2). 可供引用的头文件 -- log.h 自行创建include文件夹放下面即可 3). makefile文件--Makefile 4). 功能测试文件--test.c 5). 日志配置模板文件--log.conf.simple 6). 日志配置文件--log.conf 拷贝的log.conf.simple 7). lib文件夹 存放编译出的lib库,静态动态两种链接方式都有 8). logout文件夹,默认存放日志输出文件位置 9). 2024-xx-xx.log 日志文件,里面存放日志,没有会根据日期创建
- 模块功能的代码实现c文件 --log.c
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <time.h> #include <string.h> #include "include/log.h" /* 存储日志信息记录的结构体变量 */ LOGSET logsetting; LOG loging; /* 日志级别对应的字符串 */ const static char LogLevelText[4][10]={" INFO "," DEBUG ","WARNING"," ERROR "}; /* 获取日期 */ static char * getdate(char *date); /* 获取时间 */ static void settime(void); /* 获取日志级别*/ static unsigned char getcode(char *path); /* 读取日志配置文件内容 log.conf */ static unsigned char ReadConfig(char *path); /* 获取日志配置 */ static LOGSET *getlogset(void); /* 打印日志 */ static void PrintfLog(char * fromat,va_list args); /* 日志信息初始化*/ static int initlog(unsigned char loglevel); static unsigned char getcode(char *path){ unsigned char code=255; if(strcmp("INFO",path)==0) code = INFO_LEVEL; else if(strcmp("WARNING",path)==0) code = WARNING_LEVEL; else if(strcmp("ERROR",path)==0) code = ERROR_LEVEL; else if(strcmp("ALL",path)==0) code = ALL_LEVEL; else if(strcmp("DEBUG",path)==0) code = DEBUG_LEVEL; else code = DEFAULT_LEVEL; return code; } static unsigned char ReadConfig(char *path){ char value[MAXFILEPATH] = {0x0}; char data[MAXFILENAME] = {0x0}; char file_line[MAXFILENAME] = {0x0}; FILE *fpath=fopen(path,"r"); if(fpath==NULL) { perror("LOG conf file read error! "); return -1; } getdate(data); strcat(data,".log"); while (fgets(file_line, sizeof(file_line), fpath) != NULL) { memset(value,0,sizeof(value)); if (sscanf(file_line, "path = %s\n", value) == 1) { strcat(value,"/"); strcat(value,data); memcpy(logsetting.filepath,value,strlen(value)); } else if (sscanf(file_line, "level = %s\n",value) == 1) { logsetting.loglevel=getcode(value); } } fclose(fpath); return 0; } /* *日志设置信息 * */ static LOGSET *getlogset(){ char path[MAXFILEPATH]={0x0}; getcwd(path,sizeof(path)); strcat(path, CONFNAME); if(access(path,F_OK)==0) { ReadConfig(path); } else { logsetting.loglevel = DEFAULT_LEVEL; logsetting.maxfilelen = MAXFILELEN; } 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, DATE_LEN, "%Y-%m-%d %H:%M:%S", localtime(&timer)); } /* *不定参打印 * */ static void PrintfLog(char * fromat,va_list args){ int d; char c, *s; //写入日志级别,日志时间 fprintf(loging.logfile,"[%s][%s]: ",LogLevelText[loging.loglevel-1],loging.logtime); 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) { return -1; } memset(&loging,0,sizeof(LOG)); //获取日志时间 settime(); if(strlen(logsetting->filepath)==0){ char *path=getenv("PWD"); memcpy(logsetting->filepath,path,strlen(path)); getdate(strdate); strcat(strdate,".log"); strcat(logsetting->filepath,"/"); strcat(logsetting->filepath,strdate); } memcpy(loging.filepath,logsetting->filepath,MAXFILEPATH); loging.loglevel = loglevel; //打开日志文件 if(loging.logfile==NULL) loging.logfile=fopen(loging.filepath, "a+"); if(loging.logfile==NULL){ perror("Open Log File Fail!"); return -1; } return 0; } /* *日志写入 * */ int LogWrite(unsigned char loglevel,char *fromat,...) { int rtv = -1; va_list args; //初始化日志 if(initlog(loglevel) != 0) { return rtv; } //打印日志信息 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; return rtv; }
- 可供引用的头文件 -- log.h
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <time.h> #include <stdarg.h> #ifndef LOG_H #define LOG_H #define INFO_LEVEL 0x01 #define DEBUG_LEVEL 0x02 #define WARNING_LEVEL 0x03 #define ERROR_LEVEL 0x04 #define ALL_LEVEL 0x05 #define DEFAULT_LEVEL INFO_LEVEL #define INFO INFO_LEVEL #define DEBUG DEBUG_LEVEL #define WARNING WARNING_LEVEL #define ERROR ERROR_LEVEL #define ALL ALL_LEVEL #define DEFAULT DEFAULT_LEVEL #define MAXFILELEN 4096 #define MAXFILEPATH 512 #define MAXFILENAME 50 #define DATE_LEN 20 #define CONFNAME "/log.conf" typedef struct log{ char logtime[20]; unsigned char loglevel; 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
- makefile
# # MakeFile # # 编译器 gcc CC=gcc # log库目录 LOGLIBDIR=$(PWD)/lib # 编译参数 CFLAGS=-Wall -std=c99 -g -O0 LIBFLAGS=-L $(LOGLIBDIR) -llog # 日志模块文件 LOGSRC=log.c LOGOBJ=$(LOGSRC:.c=.o) # 编出动态库,默认使用动态库编译 LIB=liblog.so # 编出静态库 SLIB=liblog-s.a # 测试程序 OBJS=test # 测试程序文件 SOURCEFILE=test.c .PHONY:all all:$(OBJS) $(OBJS):$(LIB) $(SOURCEFILE) $(CC) $(SOURCEFILE) $(LIBFLAGS) $(CFLAGS) -o $@ @cp log.conf.simple log.conf @echo "compile finish" $(LIB):$(LOGOBJ) $(CC) -shared $(CFLAGS) -o $@ $^ @cp $(LIB) $(LOGLIBDIR) -R $(SLIB):$(LOGOBJ) ar -crv $(SLIB) $(LOGOBJ) @cp $(SLIB) $(LOGLIBDIR) -R $(LOGOBJ):$(LOGSRC) $(CC) -g -O0 -c -fPIC $^ .PHONY:static static:$(SLIB) $(CC) $(SOURCEFILE) -L $(LOGLIBDIR) -llog-s $(CFLAGS) -o $(OBJS) .PHONY:clean clean: rm -fr $(LOGOBJ) $(LIB) $(SLIB) $(OBJS)
- 功能测试文件--test.c
/* * 日志模块小玩具测试 **/ #include "include/log.h" int main(int argv,char**argc) { LogWrite(DEBUG,"%s","H.e.l.l.o W.o.r.l.d!"); sleep(1); LogWrite(INFO,"%s","Hello World!"); sleep(3); LogWrite(WARNING,"%s","H e l l o W o r l d!"); sleep(5); LogWrite(ERROR,"%s","Hallo World!"); return 0; }
- 日志配置模板文件--log.conf.simple
### ### log 模块的配置文件 ### # log文件存放目录 path = ./logout # 日志打印级别,打印类型小于配置类型方可打印输出 # 选项 级别 # INFO 1 # DEBUG 2 # WARNING 3 # ERROR 4 # ALL 5 level = ALL
二、 编译运行效果图
-
编译以动态库方式链接(默认方式)
-
编译以静态库方式链接
-
运行以及查看结果
(注:在test.c 中设置了sleep休眠方便观察日志顺序,并不是卡顿,可以取消休眠)
三、 TODO LIST
- 此demo未考虑高并发多进程/线程情况,后续需要添加锁
- 配置参数较少仅有日志文件目录选择与log显示级别
- 性能较为低下
本文作者:BaldButStrong
本文链接:https://www.cnblogs.com/supersimple/p/18504064
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
分类:
,
标签:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步