C语言_日志打印功能实现代码

日志打印在大型的项目中是必不可少的,是程序崩溃等等的分析之来源。

一、思路

其实是参考着项目前人已经写好的一个日志打印功能自己照虎画猫写出来的
1、设计一个结构体用于管理日志相关成员
2、分配一块连续的内存空间用于记录日志文件的信息,主要是日志的目录。我做一个循环链表,将日志的信息存放其中,可以设置日志的数量,同时日志数大于设定值时,将最早的日志删除,腾出链表的信息节点给新的日志信息存放。
3、传参,关于传指针和指针的指针。
4、这是一个还比较想多了的日志功能。

二、结构体和函数总览

在代码中做了功能的详细解释

/*结构体*/
typedef struct my_log_file  /*这个是用于控制日志的结构体*/
{
	char file_name[LOG_FILENAME_SIZE]; //存日志文件名
	int Delete_flag;				//保留,没啥用
	int file_num;					//保留,没啥用
	int trace_level;				//日志的打印级别
	FILE *fp;						//对应的日志文件
}Log_File;

typedef struct LOGFILE_LIST {/*这个是循环链表中的节点结构体*/
	struct LOGFILE_LIST	*next;
	int			used_flag;
	char		filename[LOG_FILENAME_SIZE];
}LOGFILE_LIST;

typedef struct LOGFILE_INFO {/*这个是用于控制循环链表的结构体*/
	LOGFILE_LIST	*element;
	LOGFILE_LIST	*addlog_addr;
	LOGFILE_LIST	*delete_addr;
	int		logfile_numb;
}LOGFILE_INFO;
/*函数*/
void LogTime();  											/*用于给日志信息加上当前时间*/
int LogInit(int trace_leavel, int flag);					/*用于初始化日志功能*/
int LogOut(int trace_leavel, const char *format, ...);		/*用于打印日志*/
int AddLogListFile(char *filename);					/*用于将新产生的日志信息加入日志信息的循环列表*/
int LogListCreat(void);								/*用于创建存日志信息的循环链表*/	
int SetLogListValue(void);							/*用于获取已经存在的日志的信息,并加入循环列表*/
int CheckDir(const char *pathname);					/*用于检查此目录是否存在*/
int IsDirExist(const char *pathname);				/*用于检查此路径是否是目录文件*/
int DelLogListValue(void);							/*用于日志文件数量大于限定值是,用于删除最早的日志文件*/
int LogfileCompare(const char *strsrc, const char *strdes); /*用于日志文件比较,找出最早日志文件*/

int FineNextAvailAddr();	/*用于在循环链表中找打下一个可以用的节点,用于存放日志信息*/
int FineNextAvailAddr2(LOGFILE_LIST **addlog_addr);

int LogLock(); 										/*用于为日志操作加锁*/
int LogUnlock();  									 /*用于为日志操作解锁*/

三、函数实现

/*
*	ilename	    :			logfile.c
*	CreatTime	:			2019-09-20
*	Version		:			1.0
*	I hope you will like it. Thank you, thank you,thank you,thank .....
*/
#include "logfile.h"

pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;

Log_File g_log_file;
LOGFILE_INFO	g_logfile_info;

void LogTime()
{
   char buf[64];
   struct tm curData;
   time_t curTime;

   memset(buf,0,sizeof(buf));

   time(&curTime);
   localtime_r(&curTime,&curData);

   sprintf(buf,"[Time]%d-%02d-%02d %02d:%02d:%02d : ",
   				curData.tm_year+1900,
                curData.tm_mon+1,
                curData.tm_mday,
                curData.tm_hour,
                curData.tm_min,
                curData.tm_sec);
    if(g_log_file.fp){
        fwrite(buf,1,strlen(buf),g_log_file.fp);
    }
}
int LogInit(int trace_level, int flag)
{
    char filename[LOG_FILENAME_SIZE];
    struct tm curdata;
    time_t curtime;

    memset(filename,0,sizeof(filename));

    /*这里表示是首次创建还是日志达到规定字节后的再次创建*/
    /*因为只有首次创建才需要进行链表创建,本地已经存在的日志文件的检查和每个日志信息的获取*/
    if(flag == 0){
		
        LogListCreat();
		SetLogListValue();
		
        g_log_file.file_num = 0; 	
        g_log_file.Delete_flag = 0;	
        g_log_file.trace_level = trace_level;
        g_log_file.fp = NULL;
        memset(g_log_file.file_name,0,LOG_FILENAME_SIZE);
    }
    
    time(&curtime);
    localtime_r(&curtime,&curdata);
	
	CheckDir(LOG_FILE_DIR);
	sprintf(filename,"%s/%s_%d%02d%02d_%02d_%02d_%02d.log ",LOG_FILE_DIR,
				LOF_FILE_NAME,
                curdata.tm_year+1900,
                curdata.tm_mon+1,
                curdata.tm_mday,
                curdata.tm_hour,
                curdata.tm_min,
                curdata.tm_sec);
	if(g_log_file.fp){
		fprintf(stderr,"g_log_file.fp is NULL\n");
		fclose(g_log_file.fp);
		g_log_file.fp = NULL;
    }
	g_log_file.fp = fopen(filename,"w+b");
	if(NULL == g_log_file.fp){
		return -1;
	}
	AddLogListFile(filename);
	g_log_file.file_num++;	

	return 0;	
}

int LogLock()
{
	return pthread_mutex_lock(&log_mutex);
}

int LogUnlock()
{
	return pthread_mutex_unlock(&log_mutex);
}

int LogSizeCheck(FILE *fp)
{
	int filelen = 0;

	if(fp == NULL){
		return -1;
	}
	fseek(fp,0,SEEK_END); //指到结尾,用于帮助获取文件大小
	filelen = ftell(fp);

	if(filelen > LOG_FILE_SIZE){
		return -2;
	}
	return 0;
}


int LogOut(int trace_leavel, const char *format, ...)
{
	va_list args;
	char buffer[2048] = {0};
	char prinBuffer[2112] = {0};
	int ret = -1;

	if(g_log_file.fp == NULL){
		return -1;
	}
	
	LogLock();

	if(g_log_file.trace_level >= trace_leavel)
	{
		va_start(args,format);
		//ret = vfprintf(g_log_file.fp,format,args);
		vsnprintf(buffer, sizeof(buffer), format, args);
		va_end(args);
		sprintf(prinBuffer, "%s",buffer);
		
		//fflush(g_log_file.fp);
		LogTime();
		fwrite(prinBuffer, 1, strlen(prinBuffer), g_log_file.fp);
		
		if(LogSizeCheck(g_log_file.fp) != 0){
			LogInit(g_log_file.trace_level, 1);
		}
	}
	else{
		//va_start(args,format);
		//ret = vfprintf(g_log_file.fp,format,args);
		//va_end(args);
	}
	
	LogUnlock();

	return 0;
	
}

int AddLogListFile(char *filename)
{
	sprintf(g_logfile_info.addlog_addr->filename,"%s",filename);
	g_logfile_info.addlog_addr->used_flag = 1;
	g_logfile_info.logfile_numb++;
	if(g_logfile_info.logfile_numb > MAX_LOG_NUM){
		DelLogListValue();
	}
	FineNextAvailAddr(g_logfile_info.addlog_addr);

	return 0;
}

int LogListCreat(void)
{
	int i  = 0;
	memset(&g_logfile_info,0,sizeof(LOGFILE_INFO));
	g_logfile_info.element = NULL;

	g_logfile_info.element = (LOGFILE_LIST *)malloc(MAX_LOG_NUM * sizeof(LOGFILE_LIST));
	if(g_logfile_info.element == NULL){
		return -1;
	}

	memset(g_logfile_info.element,0,MAX_LOG_NUM*sizeof(LOGFILE_LIST));
	g_logfile_info.addlog_addr = g_logfile_info.element;
	g_logfile_info.delete_addr = g_logfile_info.element;

	for(i = 1;i<MAX_LOG_NUM;i++){
		g_logfile_info.element->next = g_logfile_info.addlog_addr+i;
		g_logfile_info.element = g_logfile_info.element->next;
	}

	g_logfile_info.element->next = g_logfile_info.addlog_addr;
	g_logfile_info.element = g_logfile_info.element->next;

	return 0;
	
}

int IsDirExist(const char *pathname)
{
	int fd;
	struct stat buf;

	if((fd = open(pathname,O_RDONLY)) < 0){
		return -1; 
	}
	if(fstat(fd,&buf) < 0){
		close(fd);
		return -2;
	}
	close(fd);

	if(S_ISDIR(buf.st_mode)){
		return 1;
	}
		
	return 0;
}


int CheckDir(const char *pathname)
{
	int ret = 0;
	ret = IsDirExist(pathname);
	if(ret != 1){
		ret = mkdir(pathname,0755);
		if(!ret)
			return 0;
		else
			return -1;
	}
	
	return 0;
}

int LogfileCompare(const char *strsrc, const char *strdes)
{
	int filelen = strlen(strsrc);
	//printf("filelen: %d\t",filelen);
	return(strncmp(strsrc,strdes,filelen));
}


int DelLogListValue(void)
{
	int ret;
	int file_count = 1;
	LOGFILE_LIST *del_temp = NULL;

	while(!(g_logfile_info.delete_addr->used_flag))
	{
		//printf("2\n");
		g_logfile_info.delete_addr = g_logfile_info.delete_addr->next;
	}
	del_temp = g_logfile_info.delete_addr;

	while(file_count <= MAX_LOG_NUM)
	{
		del_temp = del_temp->next;
		//printf("3\n");
		if(del_temp->used_flag){
			ret = LogfileCompare(g_logfile_info.delete_addr->filename,del_temp->filename);
			//printf("ret  : %d\n",ret);
			if(ret > 0){
				g_logfile_info.delete_addr = del_temp;
			}
		}
		file_count++;
		
	}
	//strcat(g_logfile_info.delete_addr->filename,"\\");
	//printf("%s\n",g_logfile_info.delete_addr->filename);
	unlink(g_logfile_info.delete_addr->filename);
	g_logfile_info.delete_addr->used_flag = 0;
	g_logfile_info.logfile_numb--;

	return 0;
}

/*
这里找到下一个可用的链表中的节点的函数有两个,特别要说明的是第二个,传参数的时候传入二级指针
一开始传的直接是指针,发现链表的建立出了问题(花了不少时间去查),
后来我改成直接 不传参数,反正有一个全局变量可以用。
后来想明白了,为什么这里传二级指针才行,这么想:
你要改变内存中的内容,你要传指向该内存的地址才行,因为函数会对参数进行拷贝,也即是实参和形参的概念
那么,同样的道理,你要是想对指针指向的内容(指针的内容也就是指针所指向的内存空间地址)进行改变,你就要传指针的指针(也就是二级指针咯)才可以。(不禁感叹一句了不起的C语言)
*/
int FineNextAvailAddr()
{
	if(g_logfile_info.logfile_numb < MAX_LOG_NUM){
		while(g_logfile_info.addlog_addr->used_flag){
			g_logfile_info.addlog_addr = g_logfile_info.addlog_addr->next;
		}
	}
	else{
		DelLogListValue();
		FineNextAvailAddr();
	}
	return 0;
}

int FineNextAvailAddr2(LOGFILE_LIST ** addlog_addr)
{
	if(g_logfile_info.logfile_numb < MAX_LOG_NUM){
		while((*addlog_addr)->used_flag){
			(*addlog_addr) = (*addlog_addr)->next;
		}
	}
	else{
		DelLogListValue();
		FineNextAvailAddr(&g_logfile_info.addlog_addr);
	}
	return 0;
}


int SetLogListValue(void)
{
	DIR *dir = NULL;
	struct dirent *dir_info = NULL;


	CheckDir(LOG_FILE_DIR);
	if((dir = opendir(LOG_FILE_DIR)) == NULL){
		printf("**********opendir error***********\n");
		return -1;
	}
	while((dir_info = readdir(dir)) != NULL)
	{
		if(strstr(dir_info->d_name,"LOG") && strstr(dir_info->d_name,".log")){
			sprintf(g_logfile_info.addlog_addr->filename,"%s/%s",LOG_FILE_DIR,dir_info->d_name);
			g_logfile_info.addlog_addr->used_flag = 1;
			g_logfile_info.logfile_numb++;
			//printf("%d: %s\n",g_logfile_info.logfile_numb,g_logfile_info.addlog_addr->filename);
			if(g_logfile_info.logfile_numb > MAX_LOG_NUM){
				//printf("111\n");
				DelLogListValue();
			}
			//FineNextAvailAddr();
			FineNextAvailAddr2(&g_logfile_info.addlog_addr);
		}
	}
	
	closedir(dir);
	dir = NULL;
	return 0;
	
}


四、测试函数和头文件

/*
*	ilename	    :			logfile.h
*	CreatTime	:			2019-09-20
*	Version		:			1.0
*	I hope you like it. Thank you, thank you,thank you,thank .....
*/
#ifndef __LOGFILE_H__
#define __LOGFILE_H__

#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <stdarg.h>
#include <time.h>
#include <unistd.h>
#include <netinet/in.h>
#include<fcntl.h>
#include <dirent.h>
#include <malloc.h>



#define	MAX_LOG_NUM 31
#define LOG_FILENAME_SIZE	128
#define	LOG_FILE_SIZE 8388608	 /*	8M	*/
#define LOF_FILE_NAME 	"LOG"
#define	LOG_FILE_DIR	"Logfile"

/*level*/
#define EERROR			1
#define EWARN			2
#define EINFO			3
#define EDEBUG			4


typedef struct my_log_file
{
	char file_name[LOG_FILENAME_SIZE];
	int Delete_flag;
	int file_num;
	int trace_level;
	FILE *fp;
}Log_File;

typedef struct LOGFILE_LIST {
	struct LOGFILE_LIST	*next;
	int			used_flag;
	char		filename[LOG_FILENAME_SIZE];
}LOGFILE_LIST;


typedef struct LOGFILE_INFO {
	LOGFILE_LIST	*element;
	LOGFILE_LIST	*addlog_addr;
	LOGFILE_LIST	*delete_addr;
	int		logfile_numb;
}LOGFILE_INFO;

void LogTime();
int LogInit(int trace_leavel, int flag);
int LogOut(int trace_leavel, const char *format, ...);
int AddLogListFile(char *filename);
int LogListCreat(void);
int SetLogListValue(void);
int CheckDir(const char *pathname);
int IsDirExist(const char *pathname);
int DelLogListValue(void);
int LogfileCompare(const char *strsrc, const char *strdes);
int FineNextAvailAddr();
int FineNextAvailAddr2(LOGFILE_LIST **addlog_addr);
int LogLock();
int LogUnlock();



#endif 



#include "logfile.h"

/*在初始化时设置日志级别,则可以选择日志打印错误,警告,信息,调试等等,错误级别最高,以此排列*/
int main()
{
#if 0
	char temp[1024] = "nihaohasd";
	int temp2 = 666;
	if(LogInit(4,0) < 0){
		printf("Death is at the beginning\n");
	}
	LogOut(EDEBUG,"debug **%s%d\n",temp,temp2);
	LogOut(EERROR,"error **%s%d\n",temp,temp2);
	LogOut(EINFO,"info **%s%d\n",temp,temp2);
	LogOut(EWARN,"warn **%s%d\n",temp,temp2);
	
#endif

#if 1
	char temp[1024] = "nihaohasd";
	int temp2 = 666;
	if(LogInit(3,0) < 0){
		printf("Death is at the beginning\n");
	}
	LogOut(EDEBUG,"debug **%s%d\n",temp,temp2);
	LogOut(EERROR,"error **%s%d\n",temp,temp2);
	LogOut(EINFO,"info **%s%d\n",temp,temp2);
	LogOut(EWARN,"warn **%s%d\n",temp,temp2);
#endif

#if 0
	char temp[1024] = "nihaohasd";
	int temp2 = 666;
	if(LogInit(2,0) < 0){
		printf("Death is at the beginning\n");
	}
	LogOut(EDEBUG,"debug **%s%d\n",temp,temp2);
	LogOut(EERROR,"error **%s%d\n",temp,temp2);
	LogOut(EINFO,"info **%s%d\n",temp,temp2);
	LogOut(EWARN,"warn **%s%d\n",temp,temp2);
#endif

#if 0
	char temp[1024] = "nihaohasd";
	int temp2 = 666;
	if(LogInit(1,0) < 0){
		printf("Death is at the beginning\n");
	}
	LogOut(EDEBUG,"debug **%s%d\n",temp,temp2);
	LogOut(EERROR,"error **%s%d\n",temp,temp2);
	LogOut(EINFO,"info **%s%d\n",temp,temp2);
	LogOut(EWARN,"warn **%s%d\n",temp,temp2);
#endif

	printf("over\n");
	return 0;
}

886

发布了46 篇原创文章 · 获赞 13 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_42718004/article/details/101069589