您的位置:首页 > 编程语言 > C语言/C++

c++日志记录小程序

2016-03-24 00:00 501 查看
摘要: 很多程序中需要记录详细的日志信息,除了使用比较成熟的日志库之外,有的时候,可以引入简便轻巧的日志小程序,在此,共享下本人自己用到过的日志程序,希望对您有所帮助,也请动脑筋思考去把相应的功能完善。(该代码也是同事共享给本人的,笔者刚参加工作一年不到,水平有限,代码中有什么bug存在,敬请指教)

Linux版的日志程序

该程序支持线程安全的写入,也支持固定大小之后自动切割文件,代码不多,稍微阅读,就能看懂基本用法了,那就直接上代码了哦

log.h 头文件

#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 实现文件

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

使用例子:

//打开落日志功能,is_threadsafe表示是否支持线程安全
bool  is_threadsafe = true;
long long int l_log_rotate_size = 50* 1024 * 1024; // 以50兆切割文件
// 打开日志文件
if(log_open("mytest.log", Logger::LEVEL_INFO, is_threadsafe, l_log_rotate_size) == -1 )
{
fprintf(stderr, "error log open file : %s\n", logger_output.c_str());
return true;
}

std::stringstream ss;
ss<<"pid =  "<<getpid()<<" , thread = "<<pthread_self()
<<", log test."<<std::endl;
// 记录日志
log_write(Logger::LEVEL_ERROR, ss.str().c_str() );

上述代码,需要修改部分代码才能在windows下运行,也就是将锁mutex那部分改成windows下临界资源的相应API即可,即(CRITICAL_SECTION),有需要的,可以自己去修改哦,最好是用条件编译来完善,这样就可以跨平台了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Linux 日志 log