简洁高效的C++终端日志工具类
纯头文件实现。
log.h:
#ifndef _DEBUG_H_
#define _DEBUG_H_#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstring>
#include <ctime>
#include <sys/time.h>// 获取文件名
#define __FILENAME__ (strrchr(__FILE__, '/') ? (strrchr(__FILE__, '/') + 1) : __FILE__)// ANSI 颜色定义
#define LOG_COLOR_RESET "\033[0m"
#define LOG_COLOR_RED "\033[31m"
#define LOG_COLOR_YELLOW "\033[33m"
#define LOG_COLOR_GREEN "\033[32m"
#define LOG_COLOR_CYAN "\033[36m"// 日志等级
enum class LogLevel
{Error = 1,Warn,Info,Debug
};#ifndef LOG_LEVEL
#define LOG_LEVEL LogLevel::Info
#endif#define LOG_LEVEL_VALUE(lvl) static_cast<int>(lvl)// Logger类
class Logger
{
public:Logger(LogLevel level, const char *file, int line, const char *func): enabled_(LOG_LEVEL_VALUE(LOG_LEVEL) >= LOG_LEVEL_VALUE(level)),level_(level){if (enabled_){stream_ << "[" << getTimestamp() << "] "<< getLogColor(level_)<< "[" << std::left << std::setw(5) << getLogTag(level_) << "] "<< LOG_COLOR_RESET<< "[" << file << ":" << line << " " << func << "()] - ";}}~Logger(){if (enabled_){stream_ << std::endl;// 在 C++11 及其后续的标准中,std::cout << xxx 单次调用是线程安全的,但是链式调用不是std::cout << stream_.str();}}template <typename T>Logger &operator<<(const T &val){if (enabled_){stream_ << val;}return *this;}Logger &operator<<(std::ostream &(*manip)(std::ostream &)){if (enabled_){manip(stream_);}return *this;}private:bool enabled_;LogLevel level_;std::ostringstream stream_;// 获取颜色字符串static const char *getLogColor(LogLevel level){switch (level){case LogLevel::Error:return LOG_COLOR_RED;case LogLevel::Warn:return LOG_COLOR_YELLOW;case LogLevel::Info:return LOG_COLOR_GREEN;case LogLevel::Debug:return LOG_COLOR_CYAN;default:return LOG_COLOR_RESET;}}// 获取标签名static const char *getLogTag(LogLevel level){switch (level){case LogLevel::Error:return "ERROR";case LogLevel::Warn:return "WARN";case LogLevel::Info:return "INFO";case LogLevel::Debug:return "DEBUG";default:return "LOG";}}static std::string getTimestamp(){struct timeval tv;gettimeofday(&tv, nullptr);struct tm tm_info;localtime_r(&tv.tv_sec, &tm_info);char buffer[20]; // "%Y-%m-%d %H:%M:%S" + '\0'strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm_info);char timestamp[25]; // "yyyy-mm-dd hh:mm:ss.mmm" + '\0'snprintf(timestamp, sizeof(timestamp), "%s.%03d", buffer, (int)(tv.tv_usec / 1000));return std::string(timestamp);}
};// 简化调用的宏
#define LOG_DEBUG Logger(LogLevel::Debug, __FILENAME__, __LINE__, __func__)
#define LOG_INFO Logger(LogLevel::Info, __FILENAME__, __LINE__, __func__)
#define LOG_WARN Logger(LogLevel::Warn, __FILENAME__, __LINE__, __func__)
#define LOG_ERROR Logger(LogLevel::Error, __FILENAME__, __LINE__, __func__)#define LOG_NOOP ((void)0) ///< 什么也不做
#define unlikely(x) __builtin_expect(!!(x), 0)/*** @brief 调用函数并检查其返回int值,若非0则报错并执行action。* @param func_call 要调用的函数(需返回int类型),返回0表示成功,非0表示失败。* @param action 失败时执行的操作*/
#define CHECK_RET_INT(func_call, action) \do \{ \int _ret = (func_call); \if (unlikely(_ret)) \{ \LOG_ERROR << #func_call << " error: " << _ret; \action; \} \} while (0)/*** @brief 调用函数并检查其返回指针,若为NULL则报错并执行action。* @param func_call 要调用的函数(需返回指针类型),返回NULL表示失败。* @param action 失败时执行的操作*/
#define CHECK_RET_PTR(func_call, action) \do \{ \void *_ret = (void *)(func_call); \if (unlikely(_ret == NULL)) \{ \LOG_ERROR << #func_call << " error: NULL"; \action; \} \} while (0)#endif // _DEBUG_H_