文章内容和代码为作者原创,转载请说明 ^_^
之前的文章把Log的大体设计和框架展示出来了,还有根据时间和大小进行分割,目前实现还不支持direct_io模式。但是接口以留出,之后还会对Log进行一次升级,因为有的业务可能存在日志量很大的问题,所以提供一个ub_log_set_buffer(char* buf,size_t size)的函数,来使用用户的buffer,这样可以类似fread/fwrite那样在用户态有个缓冲区,减少系统调用。
代码 : ub_log.h
#ifndef __LOG_4_C_H__ #define __LOG_4_C_H__ #include <stdlib.h> #include <stdio.h> #include <string.h> #include <time.h> #include <sys/stat.h> #define __USE_GNU 1 #include <fcntl.h> #include <sys/types.h> #include <errno.h> #include <unistd.h> #define FILE_NAME_MAX 1024 #ifdef __cplusplus extern "C" { #endif typedef enum _log_level { DEBUG = 1, TRACE, WARNNING, FATAL }log_level; typedef struct __log_t { //unsigned int level; /* level of log to record : ERROR , WARNING , DBUG , ALL */ char file_name[FILE_NAME_MAX]; unsigned int mask; int direct_io; int normal_fd; /* output log file */ int error_fd; /* output wf file */ size_t normal_file_size; /* file size record */ size_t error_file_size; /* file size record */ size_t split_size; /* one piece(file) max size with split */ unsigned long long time_used; /* total time */ unsigned long long split_time; /* one piece(file) time with split */ const char* last_error; } log_t; /** * @brief : initial log handler with path , indicate use direct io or not * * @params : [in] filepath : which file to be written . if NULL "stdout" will be set * @params : [in] flag : file mask set * * @return : pointer to the log_t structure . if failed , it's NULL */ log_t* ub_log_init(const char* filepath, int direct_io); /** * @brief : initial log handler with path , direct io , and split strategy * * @params : [in] filepath : which file to be written . if NULL "stdout" will be set * @params : [in] flag : file mask set * @params : [in] ts : span time of split file , 0 means no split * @params : [in] ss : content size of split file , 0 means no split * * @return : pointer to the log_t structure . if failed , it's NULL */ log_t* ub_log_split_init(const char* filepath, int direct_io,unsigned long long ts, size_t ss); /** * @brief : destroy log handle */ void ub_log_destroy(); void log4c_write(log_level level, const char* file, const char* func_name,unsigned int line, const char* __format,...); /** * @brief : write log with LEVEL * * @params : [in] log : log handle * @params : [in] buf : buffer to be written */ #ifdef USE_UB_LOG #define UB_LOG_DEBUG(buf,...) do{\ log4c_write(DEBUG,__FILE__,__FUNCTION__,__LINE__,buf,##__VA_ARGS__);\ }while(0) #else #define UB_LOG_DEBUG(buf,...) do{}while(0) #endif #ifdef USE_UB_LOG #define UB_LOG_TRACE(buf,...) do{\ log4c_write(TRACE,__FILE__,__FUNCTION__,__LINE__,buf,##__VA_ARGS__);\ }while(0) #else #define UB_LOG_TRACE(buf,...) do{}while(0) #endif #ifdef USE_UB_LOG #define UB_LOG_WARNNING(buf,...) do{\ log4c_write(WARNNING,__FILE__,__FUNCTION__,__LINE__,buf,##__VA_ARGS__);\ }while(0) #else #define UB_LOG_WARNNING(buf,...) do{}while(0) #endif #ifdef USE_UB_LOG #define UB_LOG_FATAL(buf,...) do{\ log4c_write(FATAL,__FILE__,__FUNCTION__,__LINE__,buf,##__VA_ARGS__);\ }while(0) #else #define UB_LOG_FATAL(buf,...) do{}while(0) #endif void ub_log_flush(); /** * @brief : get last function called error * * @return : error message */ const char* ub_log_error(); #ifdef __cplusplus } #endif #endif
#include "ub_log.h" #define DEFAULT_FILE_FLAG (O_CREAT|O_RDWR|O_APPEND) #define DEFAULT_FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) //#define DEFAULT_DIR_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH|S_IXUSR|S_IXGRP|S_IXOTH) #define DEFAULT_DIR_MODE (S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH) #define OUTPUT_STRING_MAX 1024 #define LOG_PATH "./log/" const char* g_last_error = NULL; struct __log_t* my_log = NULL; const char* ub_log_error() { return g_last_error; } log_t* ub_log_init(const char* filepath, int direct_io) { if (my_log) return my_log; char file_name[2][FILE_NAME_MAX] = {"",""}; my_log = (log_t*)calloc(1,sizeof(log_t)); log_t* feedback = my_log; if (NULL == feedback) return NULL; // TODO : O_DIRECT direct_io = 0; feedback->direct_io = direct_io; int flag = direct_io ? (DEFAULT_FILE_FLAG|O_DIRECT) : DEFAULT_FILE_FLAG; if (filepath) { feedback->mask = flag; struct stat buf = {0}; // LOG_PATH is not exist if (0 != stat(LOG_PATH,&buf) && ENOENT == errno) if (-1==mkdir(LOG_PATH,DEFAULT_DIR_MODE) && errno!=EEXIST) goto exception; snprintf(feedback->file_name,FILE_NAME_MAX,"%s",filepath); snprintf(file_name[0],FILE_NAME_MAX,LOG_PATH"%s.log",filepath); snprintf(file_name[1],FILE_NAME_MAX,LOG_PATH"%s.log.wf",filepath); feedback->normal_fd = open(file_name[0],flag,DEFAULT_FILE_MODE); feedback->error_fd = open(file_name[1],flag,DEFAULT_FILE_MODE); if (-1 == feedback->normal_fd || -1 == feedback->error_fd) { goto exception; } } else { feedback->normal_fd = feedback->error_fd = 1; // stdout } return feedback; exception: g_last_error = strerror(errno); free(feedback); return NULL; } log_t* ub_log_split_init(const char* filepath,int direct_io, unsigned long long ts, size_t ss) { my_log= ub_log_init(filepath,direct_io); if (NULL == my_log) return NULL; struct stat filestat = {0}; // get initial size fstat(my_log->normal_fd,&filestat); my_log->normal_file_size = filestat.st_size; fstat(my_log->error_fd,&filestat); my_log->error_file_size = filestat.st_size; my_log->split_size = (0==ss)?0:(ss-1)*1024*1024; my_log->split_time = ts; my_log->time_used = (unsigned long long)time(NULL); return my_log; } void ub_log_destroy() { if (!my_log) { close(my_log->normal_fd); close(my_log->error_fd); free(my_log); my_log = NULL; } } void __check_file_stat(log_t* log) { char new_file[FILE_NAME_MAX] = {0}; unsigned long t = (unsigned long)time(NULL); int changed = 0; if (my_log->split_size && my_log->normal_file_size>my_log->split_size) { // too big , or time up snprintf(new_file,FILE_NAME_MAX,LOG_PATH"%s.log.%lu",my_log->file_name,t); close(my_log->normal_fd); my_log->normal_fd = open(new_file,my_log->mask,DEFAULT_FILE_MODE); my_log->normal_file_size = 0; changed = 1; } if (my_log->split_size && my_log->error_file_size>my_log->split_size) { // too big , or time up snprintf(new_file,FILE_NAME_MAX,LOG_PATH"%s.log.wf.%lu",my_log->file_name,t); close(my_log->error_fd); my_log->error_fd = open(new_file,my_log->mask,DEFAULT_FILE_MODE); my_log->error_file_size = 0; changed = 1; } // if split_size has effected , we ignore the split_time if (my_log->split_time && !changed && my_log->time_used>my_log->split_time) { snprintf(new_file,FILE_NAME_MAX,LOG_PATH"%s.log.%lu",my_log->file_name,t); close(my_log->normal_fd); my_log->normal_fd = open(new_file,my_log->mask,DEFAULT_FILE_MODE); my_log->normal_file_size = 0; snprintf(new_file,FILE_NAME_MAX,LOG_PATH"%s.log.wf.%lu",my_log->file_name,t); close(my_log->error_fd); my_log->error_fd = open(new_file,my_log->mask,DEFAULT_FILE_MODE); my_log->error_file_size = 0; // reset my_log->time_used = 0; } } void log4c_write(log_level level, const char* file, const char* func_name,unsigned int line, const char* __format, ...) { if (!my_log || !__format) return; // stdout ignore if ((my_log->normal_fd!=1) && (my_log->split_size!=0 || my_log->split_time!=0)) __check_file_stat(my_log); int fd = -1; switch(level) { case DEBUG: case TRACE: fd = my_log->normal_fd; break; case WARNNING: case FATAL: fd = my_log->error_fd; break; default : fd = my_log->error_fd; break; } char tmp[OUTPUT_STRING_MAX*2] = {0}; char params_string[OUTPUT_STRING_MAX] = {0}; char *level_string = NULL; if (my_log->direct_io) { if (-1 == posix_memalign((void**)&level_string,getpagesize(),1024)) return; } else level_string = tmp; int level_string_len = 0; int params_string_len = 0; // get ms time_t timep; struct tm *p; time(&timep); p=gmtime(&timep); const char* print_level = NULL; switch(level) { case DEBUG: print_level = "DEBUG"; break; case TRACE: print_level = "TRACE"; break; case WARNNING: print_level = "WARNNING"; break; case FATAL: print_level = "FATAL"; break; default : print_level = "UNKNOWN"; break; } __builtin_va_list __local_argv; __builtin_va_start(__local_argv, __format); level_string_len = snprintf(level_string,OUTPUT_STRING_MAX,"[%s][%s:%d][%s][%d/%d/%d:%d:%d] ",print_level,file,line,func_name,(1+p->tm_mon),p->tm_ params_string_len = vsnprintf(params_string,OUTPUT_STRING_MAX,__format,__local_argv); strncat(level_string,params_string,OUTPUT_STRING_MAX); // append a "\n" at the end level_string[level_string_len+params_string_len] = '\n'; int written = write(fd,level_string,level_string_len+params_string_len+1); if (-1 != written) { size_t *file_size = (fd==my_log->error_fd) ? &my_log->error_file_size: &my_log->normal_file_size; *file_size += written; my_log->time_used += (unsigned long long)time(NULL); } if (my_log->direct_io) free(level_string); } void ub_log_flush() { if (my_log) sync(); }