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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章