C++ log

以下代码摘自SSDB。
最近项目使用boost log 发现有些问题,于是在
SSDB源码里找到了下面的log类,使用起来不错。大家可以学习一下文件操作和变长参数函数实现。

log.h

/*
Copyright (c) 2012-2014 The SSDB Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
*/
#ifndef UTIL_LOG_H
#define UTIL_LOG_H

#include <inttypes.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <string.h>
#include <math.h>
#include <fcntl.h>
#include <assert.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>

class Logger{
    public:
        static const int LEVEL_NONE     = (-1);
        static const int LEVEL_MIN      = 0;
        static const int LEVEL_FATAL    = 0;
        static const int LEVEL_ERROR    = 1;
        static const int LEVEL_WARN     = 2;
        static const int LEVEL_INFO     = 3;
        static const int LEVEL_DEBUG    = 4;
        static const int LEVEL_TRACE    = 5;
        static const int LEVEL_MAX      = 5;

        static int get_level(const char *levelname);
    private:
        FILE *fp;
        char filename[PATH_MAX];
        int level_;
        pthread_mutex_t *mutex;

        uint64_t rotate_size;
        struct{
            uint64_t w_curr;
            uint64_t w_total;
        }stats;

        void rotate();
        void threadsafe();
    public:
        Logger();
        ~Logger();

        int level(){
            return level_;
        }

        void set_level(int level){
            this->level_ = level;
        }

        int open(FILE *fp, int level=LEVEL_DEBUG, bool is_threadsafe=false);
        int open(const char *filename, int level=LEVEL_DEBUG,
            bool is_threadsafe=false, uint64_t rotate_size=0);
        void close();

        int logv(int level, const char *fmt, va_list ap);

        int trace(const char *fmt, ...);
        int debug(const char *fmt, ...);
        int info(const char *fmt, ...);
        int warn(const char *fmt, ...);
        int error(const char *fmt, ...);
        int fatal(const char *fmt, ...);
};


int log_open(FILE *fp, int level=Logger::LEVEL_DEBUG, bool is_threadsafe=false);
int log_open(const char *filename, int level=Logger::LEVEL_DEBUG,
    bool is_threadsafe=false, uint64_t rotate_size=0);
int log_level();
void set_log_level(int level);
int log_write(int level, const char *fmt, ...);


#ifdef NDEBUG
    #define log_trace(fmt, args...) do{}while(0)
#else
    #define log_trace(fmt, args...) \
        log_write(Logger::LEVEL_TRACE, "%s(%d): " fmt, __FILE__, __LINE__, ##args)
#endif

#define log_debug(fmt, args...) \
    log_write(Logger::LEVEL_DEBUG, "%s(%d): " fmt, __FILE__, __LINE__, ##args)
#define log_info(fmt, args...)  \
    log_write(Logger::LEVEL_INFO,  "%s(%d): " fmt, __FILE__, __LINE__, ##args)
#define log_warn(fmt, args...)  \
    log_write(Logger::LEVEL_WARN,  "%s(%d): " fmt, __FILE__, __LINE__, ##args)
#define log_error(fmt, args...) \
    log_write(Logger::LEVEL_ERROR, "%s(%d): " fmt, __FILE__, __LINE__, ##args)
#define log_fatal(fmt, args...) \
    log_write(Logger::LEVEL_FATAL, "%s(%d): " fmt, __FILE__, __LINE__, ##args)


#endif

log.cpp

/*
Copyright (c) 2012-2014 The SSDB Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
*/
#include "log.h"

static Logger logger;

int log_open(FILE *fp, int level, bool is_threadsafe){
    return logger.open(fp, level, is_threadsafe);
}

int log_open(const char *filename, int level, bool is_threadsafe, uint64_t rotate_size){
    return logger.open(filename, level, is_threadsafe, rotate_size);
}

int log_level(){
    return logger.level();
}

void set_log_level(int level){
    logger.set_level(level);
}

int log_write(int level, const char *fmt, ...){
    va_list ap;
    va_start(ap, fmt);
    int ret = logger.logv(level, fmt, ap);
    va_end(ap);
    return ret;
}

/*****/

Logger::Logger(){
    fp = stdout;
    level_ = LEVEL_DEBUG;
    mutex = NULL;

    filename[0] = '\0';
    rotate_size = 0;
    stats.w_curr = 0;
    stats.w_total = 0;
}

Logger::~Logger(){
    if(mutex){
        pthread_mutex_destroy(mutex);
        free(mutex);
    }
    this->close();
}

void Logger::threadsafe(){
    if(mutex){
        pthread_mutex_destroy(mutex);
        free(mutex);
        mutex = NULL;
    }
    mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
    pthread_mutex_init(mutex, NULL);
}

int Logger::open(FILE *fp, int level, bool is_threadsafe){
    this->fp = fp;
    this->level_ = level;
    if(is_threadsafe){
        this->threadsafe();
    }
    return 0;
}

int Logger::open(const char *filename, int level, bool is_threadsafe, uint64_t rotate_size){
    if(strlen(filename) > PATH_MAX - 20){
        fprintf(stderr, "log filename too long!");
        return -1;
    }
    strcpy(this->filename, filename);

    FILE *fp;
    if(strcmp(filename, "stdout") == 0){
        fp = stdout;
    }else if(strcmp(filename, "stderr") == 0){
        fp = stderr;
    }else{
        fp = fopen(filename, "a");
        if(fp == NULL){
            return -1;
        }

        struct stat st;
        int ret = fstat(fileno(fp), &st);
        if(ret == -1){
            fprintf(stderr, "fstat log file %s error!", filename);
            return -1;
        }else{
            this->rotate_size = rotate_size;
            stats.w_curr = st.st_size;
        }
    }
    return this->open(fp, level, is_threadsafe);
}

void Logger::close(){
    if(fp != stdin && fp != stdout){
        fclose(fp);
    }
}

void Logger::rotate(){
    fclose(fp);
    char newpath[PATH_MAX];
    time_t time;
    struct timeval tv;
    struct tm *tm;
    gettimeofday(&tv, NULL);
    time = tv.tv_sec;
    tm = localtime(&time);
    sprintf(newpath, "%s.%04d%02d%02d-%02d%02d%02d",
        this->filename,
        tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
        tm->tm_hour, tm->tm_min, tm->tm_sec);

    //printf("rename %s => %s\n", this->filename, newpath);
    int ret = rename(this->filename, newpath);
    if(ret == -1){
        return;
    }
    fp = fopen(this->filename, "a");
    if(fp == NULL){
        return;
    }
    stats.w_curr = 0;
}

int Logger::get_level(const char *levelname){
    if(strcmp("trace", levelname) == 0){
        return LEVEL_TRACE;
    }
    if(strcmp("debug", levelname) == 0){
        return LEVEL_DEBUG;
    }
    if(strcmp("info", levelname) == 0){
        return LEVEL_INFO;
    }
    if(strcmp("warn", levelname) == 0){
        return LEVEL_WARN;
    }
    if(strcmp("error", levelname) == 0){
        return LEVEL_ERROR;
    }
    if(strcmp("fatal", levelname) == 0){
        return LEVEL_FATAL;
    }
    if(strcmp("none", levelname) == 0){
        return LEVEL_NONE;
    }
    return LEVEL_DEBUG;
}

inline static const char* level_name(int level){
    switch(level){
        case Logger::LEVEL_FATAL:
            return "[FATAL] ";
        case Logger::LEVEL_ERROR:
            return "[ERROR] ";
        case Logger::LEVEL_WARN:
            return "[WARN ] ";
        case Logger::LEVEL_INFO:
            return "[INFO ] ";
        case Logger::LEVEL_DEBUG:
            return "[DEBUG] ";
        case Logger::LEVEL_TRACE:
            return "[TRACE] ";
    }
    return "";
}

#define LEVEL_NAME_LEN  8
#define LOG_BUF_LEN     4096

int Logger::logv(int level, const char *fmt, va_list ap){
    if(logger.level_ < level){
        return 0;
    }

    char buf[LOG_BUF_LEN];
    int len;
    char *ptr = buf;

    time_t time;
    struct timeval tv;
    struct tm *tm;
    gettimeofday(&tv, NULL);
    time = tv.tv_sec;
    tm = localtime(&time);
    /* %3ld 在数值位数超过3位的时候不起作用, 所以这里转成int */
    len = sprintf(ptr, "%04d-%02d-%02d %02d:%02d:%02d.%03d ",
        tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
        tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(tv.tv_usec/1000));
    if(len < 0){
        return -1;
    }
    ptr += len;

    memcpy(ptr, level_name(level), LEVEL_NAME_LEN);
    ptr += LEVEL_NAME_LEN;

    int space = sizeof(buf) - (ptr - buf) - 10;
    len = vsnprintf(ptr, space, fmt, ap);
    if(len < 0){
        return -1;
    }
    ptr += len > space? space : len;
    *ptr++ = '\n';
    *ptr = '\0';

    len = ptr - buf;
    // change to write(), without locking?
    if(this->mutex){
        pthread_mutex_lock(this->mutex);
    }
    fwrite(buf, len, 1, this->fp);
    fflush(this->fp);

    stats.w_curr += len;
    stats.w_total += len;
    if(rotate_size > 0 && stats.w_curr > rotate_size){
        this->rotate();
    }
    if(this->mutex){
        pthread_mutex_unlock(this->mutex);
    }

    return len;
}

int Logger::trace(const char *fmt, ...){
    va_list ap;
    va_start(ap, fmt);
    int ret = logger.logv(Logger::LEVEL_TRACE, fmt, ap);
    va_end(ap);
    return ret;
}

int Logger::debug(const char *fmt, ...){
    va_list ap;
    va_start(ap, fmt);
    int ret = logger.logv(Logger::LEVEL_DEBUG, fmt, ap);
    va_end(ap);
    return ret;
}

int Logger::info(const char *fmt, ...){
    va_list ap;
    va_start(ap, fmt);
    int ret = logger.logv(Logger::LEVEL_INFO, fmt, ap);
    va_end(ap);
    return ret;
}

int Logger::warn(const char *fmt, ...){
    va_list ap;
    va_start(ap, fmt);
    int ret = logger.logv(Logger::LEVEL_WARN, fmt, ap);
    va_end(ap);
    return ret;
}

int Logger::error(const char *fmt, ...){
    va_list ap;
    va_start(ap, fmt);
    int ret = logger.logv(Logger::LEVEL_ERROR, fmt, ap);
    va_end(ap);
    return ret;
}

int Logger::fatal(const char *fmt, ...){
    va_list ap;
    va_start(ap, fmt);
    int ret = logger.logv(Logger::LEVEL_FATAL, fmt, ap);
    va_end(ap);
    return ret;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值