C++ 自定义项目日志库,支持log分级标签耗时统计显示文件名方法名行号信息等,Windows&Linux通用
Posted 阿飞__
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ 自定义项目日志库,支持log分级标签耗时统计显示文件名方法名行号信息等,Windows&Linux通用相关的知识,希望对你有一定的参考价值。
一、效果展示
功能点:
- log输出时间,精确到毫秒
- log等级,支持 V(verbose),D(debug),I(info),W(warning),E(error)
- log标签,可以根据标签筛选不同的log
- log信息,自动添加文件名、方法名、行号输出,帮忙快速定位到log在工程中的位置
- 耗时统计功能,精确到微秒
附上测试代码:
在linux下编译的话需要链接pthread,即编译命令加上 -lpthread
。
#include "LogUtils.h"
#include <thread>
void foo()
for (int i = 0; i < 5; i++) LOGD("i: %d", i);
void bar()
for (char c = 'a'; c < 'f'; c++) LOGD("c: %c", c);
int main(int argc, char* argv[])
set_log_level(LOG_LEVEL_DEBUG);
LOGV("test log v"); // won't print, because debug level > verbose level
__TIC__(task);
std::thread t1(foo);
std::thread t2(bar);
t1.join();
t2.join();
__TOC__(task); // print time elapse
return 0;
二、实现
1. LogInner类
负责实际的打印工作,定义日志级别。
头文件:
#ifndef LOG_INNER_HPP_
#define LOG_INNER_HPP_
#include <stdarg.h>
#include <stdio.h>
/// @brief 日志级别
/// 级别排序 ERROR > WARNING > INFO > DEBUG > VERBOSE
typedef enum
LOG_LEVEL_VERBOSE, ///< 打印所有级别日志
LOG_LEVEL_DEBUG, ///< 打印高于DEBUG(包含)级别日志
LOG_LEVEL_INFO, ///< 打印高于INFO(包含)级别日志
LOG_LEVEL_WARN, ///< 打印高于WARNING(包含)级别日志
LOG_LEVEL_ERR, ///< 打印高于ERROR(包含)级别日志
LOG_LEVEL_CLOSE ///< 关闭
log_level_t;
class LogInner
public:
// @brief 获取LogInner单例对象
static LogInner& getInstance();
/// @brief 设置log级别
/// @param [in] level 级别
void setLogLevel(log_level_t level);
/// @brief 自定义tag的log
/// @param [in] tag 自定义的tag
/// @param [in] format 字符串格式
/// @param [in] list 待输出的可变参数
void v(const char* tag, const char* format, va_list list) const;
/// @brief 自定义tag的log
/// @param [in] tag 自定义的tag
/// @param [in] format 字符串格式
/// @param [in] list 待输出的可变参数
void d(const char* tag, const char* format, va_list list) const;
/// @brief 自定义tag的log
/// @param [in] tag 自定义的tag
/// @param [in] format 字符串格式
/// @param [in] list 待输出的可变参数
void i(const char* tag, const char* format, va_list list) const;
/// @brief 自定义tag的log
/// @param [in] tag 自定义的tag
/// @param [in] format 字符串格式
/// @param [in] list 待输出的可变参数
void w(const char* tag, const char* format, va_list list) const;
/// @brief 自定义tag的log
/// @param [in] tag 自定义的tag
/// @param [in] format 字符串格式
/// @param [in] list 待输出的可变参数
void e(const char* tag, const char* format, va_list list) const;
private:
LogInner();
~LogInner();
LogInner(const LogInner& rhs);
const LogInner& operator= (const LogInner& rhs);
void output(const FILE* f, const char& l, const char* tag, const char* format, va_list list) const;
log_level_t mLevel;
;
实现:
#include "LogInner.hpp"
#include <time.h>
#include <chrono>
#include <mutex>
using namespace std::chrono;
std::mutex mtx; // 多线程场景下打印使用,不是多线程的话关掉可以节约点性能
void LogInner::output(const FILE* f, const char& level, const char* tag, const char* format, va_list list) const
FILE* fp = const_cast<FILE*>(f);
auto now = system_clock::now();
time_t tt = system_clock::to_time_t(now);
struct tm* timeinfo = localtime(&tt);
mtx.lock();
fprintf(fp, "[%02d-%02d %02d:%02d:%02d.%03d %c/%s] ",
timeinfo->tm_mon + 1,
timeinfo->tm_mday,
timeinfo->tm_hour,
timeinfo->tm_min,
timeinfo->tm_sec,
(int)(duration_cast<milliseconds>(now.time_since_epoch()).count() % 1000),
level, tag);
vfprintf(fp, format, list);
fprintf(fp, "\\n");
mtx.unlock();
void LogInner::v(const char* tag, const char* format, va_list list) const
if(mLevel > LOG_LEVEL_VERBOSE)
return;
output(stdout, 'V', tag, format, list);
void LogInner::d(const char* tag, const char* format, va_list list) const
if(mLevel > LOG_LEVEL_DEBUG)
return;
output(stdout, 'D', tag, format, list);
void LogInner::i(const char* tag, const char* format, va_list list) const
if(mLevel > LOG_LEVEL_INFO)
return;
output(stdout, 'I', tag, format, list);
void LogInner::w(const char* tag, const char* format, va_list list) const
if(mLevel > LOG_LEVEL_WARN)
return;
output(stdout, 'W', tag, format, list);
void LogInner::e(const char* tag, const char* format, va_list list) const
if(mLevel > LOG_LEVEL_ERR)
return;
output(stderr, 'E', tag, format, list);
LogInner& LogInner::getInstance()
static LogInner instance;
return instance;
LogInner::LogInner(): mLevel(LOG_LEVEL_VERBOSE)
LogInner::~LogInner()
void LogInner::setLogLevel(log_level_t level)
mLevel = level;
2. LogOuter类
负责包装 LogInner 类,可变参数的转换。
头文件:
#ifndef LOG_OUTER_H_
#define LOG_OUTER_H_
#include "LogInner.hpp"
/// @brief 打开log开关,默认LOG_LEVEL_VERBOSE
void set_log_level(log_level_t level);
/// @brief 带tag的verbose级别log
/// @param [in] tag log标识
/// @param [in] format 输出消息格式化字符串
/// @param [in] ...(可变参数)
void logv_tag(const char* tag, const char* format, ...);
/// @brief 带tag的debug级别log
/// @param [in] tag log标识
/// @param [in] format 输出消息格式化字符串
/// @param [in] ...(可变参数)
void logd_tag(const char* tag, const char* format, ...);
/// @brief 带tag的info级别log
/// @param [in] tag log标识
/// @param [in] format 输出消息格式化字符串
/// @param [in] ...(可变参数)
void logi_tag(const char* tag, const char* format, ...);
/// @brief 带tag的warning级别log
/// @param [in] tag log标识
/// @param [in] format 输出消息格式化字符串
/// @param [in] ...(可变参数)
void logw_tag(const char* tag, const char* format, ...);
/// @brief 带tag的error级别log
/// @param [in] tag log标识
/// @param [in] format 输出消息格式化字符串
/// @param [in] ...(可变参数)
void loge_tag(const char* tag, const char* format, ...);
#endif //LOG_OUTER_H_
实现:
#include "LogOuter.h"
void set_log_level(log_level_t level)
LogInner::getInstance().setLogLevel(level);
void logv_tag(const char* tag, const char* format, ...)
va_list list;
va_start(list, format);
LogInner::getInstance().v(tag, format, list);
va_end(list);
void logd_tag(const char* tag, const char* format, ...)
va_list list;
va_start(list, format);
LogInner::getInstance().d(tag, format, list);
va_end(list);
void logi_tag(const char* tag, const char* format, ...)
va_list list;
va_start(list, format);
LogInner::getInstance().i(tag, format, list);
va_end(list);
void logw_tag(const char* tag, const char* format, ...)
va_list list;
va_start(list, format);
LogInner::getInstance().w(tag, format, list);
va_end(list);
void loge_tag(const char* tag, const char* format, ...)
va_list list;
va_start(list, format);
LogInner::getInstance().e(tag, format, list);
va_end(list);
3. LogUtils类
定义宏定义,方便调用(也可以直接调用LogOuter里的方法)。
#ifndef _LOG_UTILS_H_
#define _LOG_UTILS_H_
#include "LogOuter.h"
#include <string.h>
#include <chrono>
// Windows文件路径分隔符是\\\\,Linux文件路径分隔符是/
#ifdef _MSC_VER
#define __FILENAME__ (strrchr(__FILE__, '\\\\') + 1)
#else
#define __FILENAME__ (strrchr(__FILE__, '/') + 1)
#endif
// 可自定义标签,在cpp中自己定义这个宏就行,如若没有则使用默认标签
#ifndef LOG_TAG
#define LOG_TAG "default_tag"
#endif
#define LOG_TRACE(func, format, ...) \\
func(LOG_TAG, "[%s][%s][%d]: " format, __FILENAME__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define LOGV(format, ...) LOG_TRACE(logv_tag, format, ##__VA_ARGS__)
#define LOGD(format, ...) LOG_TRACE(logd_tag, format, ##__VA_ARGS__)
#define LOGI(format, ...) LOG_TRACE(logi_tag, format, ##__VA_ARGS__)
#define LOGW(format, ...) LOG_TRACE(logw_tag, format, ##__VA_ARGS__)
#define LOGE(format, ...) LOG_TRACE(loge_tag, format, ##__VA_ARGS__)
#define __TIC__(V) auto time_##V##_start = std::chrono::high_resolution_clock::now()
#define __TOC__(V) auto time_##V##_end = std::chrono::high_resolution_clock::now(); \\
auto time_##V##_span = std::chrono::duration_cast<std::chrono::duration<double>> \\
(time_##V##_end - time_##V##_start); \\
LOGD("%s elapse time: %.3f ms", #V, time_##V##_span.count() * 1000)
#endif // _LOG_UTILS_H_
三、后记
目前支持的功能还是有限的,但日常项目开发也足够使用了。
这里没有兼容 android 系统了,也为了避免篇幅太长了,要做的话可以将 LogInner 类分两个实现,一个是使用 vfprintf 打印,一个是使用 Android 的 __android_log_vprint 打印,另外 Android 的 log 是自带时间信息和换行的。
关于 Android 日志,或者宏开关控制是否打印log,可以参考链接:NDK/C++ 耗时统计类TimeUtils
以上是关于C++ 自定义项目日志库,支持log分级标签耗时统计显示文件名方法名行号信息等,Windows&Linux通用的主要内容,如果未能解决你的问题,请参考以下文章