文章内容和代码为作者原创,转载请说明 ^_^
之前的文章把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();
}