以下代码摘自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;
}