c++日志记录小程序
2016-03-24 00:00
501 查看
摘要: 很多程序中需要记录详细的日志信息,除了使用比较成熟的日志库之外,有的时候,可以引入简便轻巧的日志小程序,在此,共享下本人自己用到过的日志程序,希望对您有所帮助,也请动脑筋思考去把相应的功能完善。(该代码也是同事共享给本人的,笔者刚参加工作一年不到,水平有限,代码中有什么bug存在,敬请指教)
log.h 头文件
log.cpp 实现文件
使用例子:
上述代码,需要修改部分代码才能在windows下运行,也就是将锁mutex那部分改成windows下临界资源的相应API即可,即(CRITICAL_SECTION),有需要的,可以自己去修改哦,最好是用条件编译来完善,这样就可以跨平台了。
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 socket 初步
- Linux Kernel 4.0 RC5 发布!
- linux lsof详解
- linux 文件权限
- Linux 执行数学运算
- 10 篇对初学者和专家都有用的 Linux 命令教程
- Linux 与 Windows 对UNICODE 的处理方式
- Ubuntu12.04下QQ完美走起啊!走起啊!有木有啊!
- 解決Linux下Android开发真机调试设备不被识别问题
- 运维入门
- 运维提升
- Linux 自检和 SystemTap
- Ubuntu Linux使用体验
- c语言实现hashmap(转载)
- Linux 信号signal处理机制
- linux下mysql添加用户
- Scientific Linux 5.5 图形安装教程
- Linux 下无损图片压缩小工具介绍