Logger.h和Logger.cc文件分析

一、头文件(Logger.h)完整注释+语法解析

cpp 复制代码
// 1. 预处理指令:防止头文件被重复包含(现代编译器特性,替代传统#ifndef/#define/#endif)
//    语法:#pragma是编译器专属预处理指令,once表示该文件仅被编译一次
//    作用:避免多次包含Logger.h导致类/宏重复定义的编译错误
#pragma once

// 2. 引入C++标准库字符串头文件
//    语法:#include <string> 引入系统标准头文件,尖括号<>用于系统库
//    作用:提供std::string类型的定义,支撑Logger类的log()函数参数、字符串拼接等操作
#include <string>

// 3. 引入自定义的noncopyable.h头文件
//    语法:#include "头文件名.h" 引入项目内自定义头文件,双引号""用于本地文件
//    作用:获取noncopyable类的声明,Logger类继承该类以禁止拷贝
#include "noncopyable.h"

// 4. 宏定义:LOG_INFO 日志宏(普通信息级别)
//    语法:#define 宏名(参数列表) 宏体,...表示可变参数,##__VA_ARGS__处理可变参数(兼容无参数场景)
//    设计思路:用do-while(0)包裹宏体,保证宏在任何场景下语法正确(如if后不加{}也不会出错)
#define LOG_INFO(logmsgFormat, ...)                       \  // \是行连接符,将多行宏体合并为一行
    do                                                    \  // do-while(0)包裹:保证宏体作为一个整体执行
    {                                                     \
        Logger &logger = Logger::instance();              \  // 获取Logger单例对象的引用(避免拷贝)
        logger.setLogLevel(INFO);                         \  // 设置日志级别为INFO
        char buf[1024] = {0};                             \  // 定义1024字节字符数组缓冲区,{0}初始化所有元素为0
        snprintf(buf, 1024, logmsgFormat, ##__VA_ARGS__); \  // 格式化日志内容到buf:snprintf是安全格式化函数,避免缓冲区溢出;##__VA_ARGS__处理可变参数(无参数时自动省略逗号)
        logger.log(buf);                                  \  // 调用log()函数输出日志
    } while (0)  // while(0):让do-while仅执行一次,保证宏体语法完整性

// 5. 宏定义:LOG_ERROR 日志宏(错误信息级别)
//    语法/逻辑与LOG_INFO一致,仅日志级别改为ERROR
#define LOG_ERROR(logmsgFormat, ...)                      \
    do                                                    \
    {                                                     \
        Logger &logger = Logger::instance();              \
        logger.setLogLevel(ERROR);                        \
        char buf[1024] = {0};                             \
        snprintf(buf, 1024, logmsgFormat, ##__VA_ARGS__); \
        logger.log(buf);                                  \
    } while (0)

// 6. 宏定义:LOG_FATAL 日志宏(致命错误级别)
//    语法/逻辑与LOG_INFO一致,新增exit(-1):输出日志后终止程序
#define LOG_FATAL(logmsgFormat, ...)                      \
    do                                                    \
    {                                                     \
        Logger &logger = Logger::instance();              \
        logger.setLogLevel(FATAL);                        \
        char buf[1024] = {0};                             \
        snprintf(buf, 1024, logmsgFormat, ##__VA_ARGS__); \
        logger.log(buf);                                  \
        exit(-1);                                         \  // exit(-1):终止程序,返回非0表示异常退出
    } while (0)

// 7. 条件编译:仅定义MUDEBUG宏时,才启用LOG_DEBUG宏
//    语法:#ifdef 宏名 编译分支1 #else 编译分支2 #endif
//    作用:调试模式下输出DEBUG日志,发布模式下禁用(减少性能开销)
#ifdef MUDEBUG
// 8. 宏定义:LOG_DEBUG 日志宏(调试信息级别)
#define LOG_DEBUG(logmsgFormat, ...)                      \
    do                                                    \
    {                                                     \
        Logger &logger = Logger::instance();              \
        logger.setLogLevel(DEBUG);                        \
        char buf[1024] = {0};                             \
        snprintf(buf, 1024, logmsgFormat, ##__VA_ARGS__); \
        logger.log(buf);                                  \
    } while (0)
#else
// 9. 非调试模式:LOG_DEBUG宏为空(调用时无任何操作)
#define LOG_DEBUG(logmsgFormat, ...)
#endif

// 10. 枚举类型定义:日志级别枚举
//     语法:enum 枚举名 { 枚举值1, 枚举值2, ... }; 枚举默认从0开始赋值(INFO=0, ERROR=1, FATAL=2, DEBUG=3)
//     作用:用枚举替代魔法数字,提高代码可读性和可维护性
enum LogLevel
{
    INFO,  // 普通信息级别
    ERROR, // 错误信息级别
    FATAL, // 致命错误级别(会终止程序)
    DEBUG  // 调试信息级别(仅调试模式启用)
};

// 11. 日志类定义:封装日志的级别设置、日志输出逻辑
//     语法:class 类名 : 继承基类 { 成员声明 }; Logger继承noncopyable以禁止拷贝
//     设计模式:单例模式(instance()返回唯一实例)
class Logger : noncopyable  // 继承noncopyable:禁止Logger对象被拷贝(拷贝构造/赋值运算符被删除)
{
// 12. 公有访问控制符:暴露对外接口
public:
    // 13. 静态成员函数声明:获取日志类的唯一实例(单例模式)
    //     语法:static 返回值类型 函数名(); 静态函数属于类而非对象,无this指针
    //     作用:返回Logger的唯一实例引用,保证全局只有一个Logger对象
    static Logger &instance();

    // 14. 成员函数声明:设置日志级别
    //     语法:void 函数名(参数类型 参数名); 无返回值,接收int类型的级别参数
    //     作用:将日志级别存储到私有成员logLevel_中
    void setLogLevel(int level);

    // 15. 成员函数声明:写日志
    //     语法:void 函数名(参数类型 参数名); 接收std::string类型的日志内容
    //     作用:拼接日志级别、时间戳和日志内容,输出到控制台
    void log(std::string msg);

// 16. 私有访问控制符:隐藏内部数据
private:
    // 17. 私有成员变量:存储当前日志级别
    //     语法:int 变量名_; 下划线结尾是C++工程命名规范,区分参数和成员变量
    //     作用:记录当前设置的日志级别(INFO/ERROR/FATAL/DEBUG)
    int logLevel_;
};

二、源文件(Logger.cpp)完整注释+语法解析

cpp 复制代码
// 1. 引入C++标准库输入输出流头文件
//    语法:#include <iostream> 提供std::cout(控制台输出)、std::endl(换行)等功能
//    作用:支撑log()函数的日志输出操作
#include <iostream>

// 2. 引入自定义Logger头文件
//    语法:#include "Logger.h" 获取Logger类的声明,否则类外定义成员函数会编译报错
#include "Logger.h"

// 3. 引入自定义Timestamp头文件
//    语法:#include "Timestamp.h" 获取Timestamp类的声明,用于生成日志的时间戳
#include "Timestamp.h"

// 4. 静态成员函数instance()类外定义:实现单例模式
//    语法:返回值类型 类名::函数名() { 函数体 } 静态函数类内声明加static,类外定义不加
//    单例实现:局部静态变量(static Logger logger),程序运行期仅初始化一次,保证全局唯一
Logger &Logger::instance()
{
    static Logger logger;  // 局部静态变量:第一次调用时初始化,后续调用返回同一对象;存储在静态存储区,生命周期贯穿程序全程
    return logger;         // 返回Logger对象的引用:避免拷贝(若返回值会调用拷贝构造,但Logger继承noncopyable,拷贝构造已删除)
}

// 5. 成员函数setLogLevel()类外定义:设置日志级别
//    语法:void 类名::函数名(参数类型 参数名) { 函数体 }
//    作用:将传入的级别参数赋值给私有成员logLevel_
void Logger::setLogLevel(int level)
{
    logLevel_ = level;  // 直接修改私有成员变量:成员函数可访问类的私有成员
}

// 6. 成员函数log()类外定义:写日志(核心逻辑)
//    语法:void 类名::函数名(参数类型 参数名) { 函数体 }
//    作用:拼接日志级别前缀、时间戳和日志内容,输出到控制台
void Logger::log(std::string msg)
{
    std::string pre = "";  // 定义字符串变量pre,存储日志级别前缀(如[INFO]),初始化为空字符串
    // 7. 分支判断:根据当前日志级别设置前缀
    //    语法:switch(变量) { case 枚举值: 逻辑; break; default: 逻辑; }
    //    作用:匹配不同日志级别,设置对应的前缀字符串
    switch (logLevel_)
    {
    case INFO:          // 匹配INFO级别(枚举值0)
        pre = "[INFO]"; // 设置前缀为[INFO]
        break;          // break跳出switch,避免穿透到下一个case
    case ERROR:         // 匹配ERROR级别(枚举值1)
        pre = "[ERROR]";
        break;
    case FATAL:         // 匹配FATAL级别(枚举值2)
        pre = "[FATAL]";
        break;
    case DEBUG:         // 匹配DEBUG级别(枚举值3)
        pre = "[DEBUG]";
        break;
    default:            // 匹配未定义的级别(兜底逻辑)
        break;
    }

    // 8. 输出日志到控制台
    //    语法解析:
    //      - std::cout:标准输出流,向控制台打印内容
    //      - +:字符串拼接运算符,将pre(级别前缀)、Timestamp::now().toString()(当前时间戳字符串)拼接
    //      - <<:流插入运算符,依次输出拼接后的字符串、分隔符、日志内容、换行符
    //      - Timestamp::now():调用Timestamp的静态函数获取当前时间戳对象
    //      - toString():调用Timestamp的常成员函数,将时间戳转为字符串
    //      - std::endl:输出换行符并刷新缓冲区
    std::cout << pre + Timestamp::now().toString() << " : " << msg << std::endl;
}

三、核心语法&符号总结

符号/语法 作用 出现位置
#pragma once 预处理指令,防止头文件重复包含 Logger.h开头
#define 宏定义指令,定义日志宏(LOG_INFO/ERROR/FATAL/DEBUG) Logger.h日志宏定义
.../##__VA_ARGS__ 可变参数宏:...接收可变参数,##__VA_ARGS__处理可变参数(无参数时省略逗号) 日志宏的参数列表/格式化函数
\ 行连接符,将多行宏体合并为一行(宏定义必须单行,用\拆分) 日志宏的每行结尾
do-while(0) 包裹宏体,保证宏在任何语法场景下正确(如if后不加{}) 日志宏体
enum 枚举类型定义,封装日志级别(替代魔法数字) LogLevel枚举
class : noncopyable 类继承,Logger继承noncopyable以禁止拷贝 Logger类定义
static 静态成员函数/变量: 1. instance():类级函数,无对象也可调用 2. logger:局部静态变量,实现单例 instance()函数/局部变量
:: 作用域解析符: 1. Logger::instance():定位类的静态函数 2. Timestamp::now():定位Timestamp的静态函数 单例调用/时间戳调用
& 引用: 1. Logger &logger:获取单例对象的引用(避免拷贝) 2. 返回值Logger&:返回对象引用 instance()返回值/logger变量
switch/case/break 分支语句,根据日志级别匹配前缀 log()函数的级别判断
+ 字符串拼接运算符,拼接级别前缀和时间戳 log()函数的输出逻辑
<< 流插入运算符,向std::cout输出内容 log()函数的控制台输出

四、关键设计亮点&注意事项

  1. 单例模式实现
    instance()函数中的局部静态变量static Logger logger是C++单例模式的经典实现,优点是:线程安全(C++11及以后)、无需手动释放、代码简洁。
  2. 宏定义的do-while(0)包裹
    避免宏在if/for等语句后不加{}导致的语法错误(如if(flag) LOG_INFO("test");),保证宏体作为一个整体执行。
  3. noncopyable继承
    禁止Logger对象被拷贝,确保单例的唯一性(若允许拷贝,可能创建多个Logger对象,违背单例设计)。
  4. 条件编译的DEBUG宏
    调试模式下输出DEBUG日志,发布模式下禁用,减少生产环境的性能开销。
  5. 安全格式化函数snprintf
    替代sprintf,指定缓冲区长度(1024),避免缓冲区溢出导致的程序崩溃。

五、测试示例(补充)

可编写main函数测试日志功能:

cpp 复制代码
#include "Logger.h"

int main() {
    LOG_INFO("程序启动成功,进程ID:%d", getpid());  // 输出INFO级别日志
    LOG_ERROR("文件读取失败,路径:%s", "test.txt"); // 输出ERROR级别日志
    LOG_DEBUG("调试信息:变量x的值为%d", 10);        // 仅MUDEBUG定义时输出
    // LOG_FATAL("致命错误:内存分配失败");          // 输出后终止程序
    return 0;
}

编译时若要启用DEBUG日志,需添加编译选项-DMUDEBUG

bash 复制代码
g++ -std=c++11 main.cpp Logger.cpp Timestamp.cpp -o test_log -DMUDEBUG
相关推荐
CRMEB系统商城2 小时前
CRMEB标准版系统(PHP)v6.0公测版发布,商城主题市场上线~
java·开发语言·小程序·php
yangminlei2 小时前
openclaw对接飞书
开发语言·python·飞书
REDcker2 小时前
Linux C++ 内存泄漏排查分析手册
java·linux·c++
临溟夜空的繁星2 小时前
C++ STL-- vector
开发语言·c++
XiYang-DING2 小时前
【Java SE】Java代码块详解
java·开发语言·python
摇滚侠2 小时前
Java SpringBoot 项目,项目启动后执行的方法,有哪些方式实现
java·开发语言·spring boot
艾莉丝努力练剑3 小时前
【Linux进程间通信:共享内存】为什么共享内存的 key 值由用户设置
java·linux·运维·服务器·开发语言·数据库·mysql
Reisentyan3 小时前
GoLang Learn Data Day 0
开发语言·rpc·golang
Chengbei113 小时前
AI 自动逆向 JS 加密!自动抓密钥、出报告,彻底解放双手,解决抓包数据包加密难题
开发语言·javascript·人工智能·安全·网络安全·网络攻击模型