UtilBox(ub)基础组件 -- Log日志(2)

        文章内容和代码为作者原创,转载请说明 ^_^

        之前的文章把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



       代码 : ub_log.c

#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();
}






你可能感兴趣的:(String,IO,File,null,Path,output)