💡知行合一.🐬|

BaldButStrong

园龄:2年10个月粉丝:2关注:0

🔖C
2024-10-28 11:07阅读: 100评论: 0推荐: 0

Linux C语言实现日志模块demo

有时候在调试编写程序的时候日志打印模块是必不可少的,今天我们在Linux下用C语言实现一个模块化的日志demo小玩具

一、一共有以下几个文件:

image

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 日志文件,里面存放日志,没有会根据日期创建
  1. 模块功能的代码实现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;
}
  1. 可供引用的头文件 -- 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
  1. 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)
  1. 功能测试文件--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;
}
  1. 日志配置模板文件--log.conf.simple

###
### log 模块的配置文件
###
# log文件存放目录
path = ./logout
# 日志打印级别,打印类型小于配置类型方可打印输出
# 选项 级别
# INFO 1
# DEBUG 2
# WARNING 3
# ERROR 4
# ALL 5
level = ALL

二、 编译运行效果图

  1. 编译以动态库方式链接(默认方式)
    image

  2. 编译以静态库方式链接
    image

  3. 运行以及查看结果
    image
    (注:在test.c 中设置了sleep休眠方便观察日志顺序,并不是卡顿,可以取消休眠)

三、 TODO LIST

  1. 此demo未考虑高并发多进程/线程情况,后续需要添加锁
  2. 配置参数较少仅有日志文件目录选择与log显示级别
  3. 性能较为低下

本文作者:BaldButStrong

本文链接:https://www.cnblogs.com/supersimple/p/18504064

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   BaldButStrong  阅读(100)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起