muduo网络库(三):日志输出

muduo网络库(三):日志输出
muduo网络库三日志输出muduo网络库三日志输出总体介绍单例模式的实现为什么不直接构造而用单例利用宏函数输出日志信息时间戳的设计精髓muduo网络库三日志输出总体介绍本项目日志输出涉及 4 个文件Logger.h/Logger.cc定义日志记录器类LoggerTimestamp.h/Timestamp.cc定义时间戳类TimestampLogger是最核心的类负责记录日志信息。它只有一个成员变量logLevel_用于存储当前日志级别。构造函数是私有的只能通过instance()获取唯一实例。对外提供两个接口setLogLevel(int level)设置日志级别func_Log(string msg)输出日志信息输出日志时func_Log()会自动调用Timestamp::now().toString()获取当前时间将时间戳和日志消息一起输出。单例模式的实现classLogger:noncopyable{public:staticLoggerinstance();// 获取日志实例voidsetLogLevel(intlevel);// 设置日志级别voidfunc_Log(string msg);// 输出日志信息private:LogLevel logLevel_;// 当前日志级别Logger(){};// 私有构造禁止外部创建}; cpp LoggerLogger::instance(){staticLogger logger;// 局部静态变量实现单例returnlogger;}instance()中定义了一个局部静态变量logger。局部静态变量的特点是第一次执行到该语句时才初始化之后一直存在直到程序结束。这保证了两个关键特性线程安全C11 起保证局部静态变量初始化是线程安全的多个线程同时调用instance()不会创建多个实例懒加载只有第一次调用时才创建避免启动时不必要的开销使用方式很简单Logger logger Logger::instance()无论在哪里调用拿到的都是同一个对象。为什么不直接构造而用单例如果构造函数是公有的每个人都能Logger logger1;创建自己的实例日志级别等状态就不一致了。把构造函数设为私有后只能通过instance()获取实例而instance()内部的static Logger logger保证了全局唯一。同时Logger继承了noncopyable和私有构造函数形成双重保险确保实例不会被复制。利用宏函数输出日志信息#defineLOG_INFO(Logmessage,...)\do{\LoggerloggerLogger::instance();\Logger::instance().setLogLevel(Logger::kInfo);\charbuf[1024]{0};\snprintf(buf,1024,Logmessage,##__VA_ARGS__);\logger.func_Log(buf);\}while(0) 这个宏函数做的事情很直白1.通过 instance() 获取单例2.2.调用 setLogLevel(kInfo) 设置日志级别3.3.用 snprintf 把格式字符串和可变参数格式化到 buf 中4.4.调用 logger.func_Log(buf) 输出 几个关键点-##__VA_ARGS__ 是 C99 特性当没有可变参数时会自动去掉前面的逗号--do{...}while(0) 保证宏在任何上下文如if-else中都能安全展开为单条语句--日志级别有5个kDebug、kInfo、kWarn、kError、kFatal对应 LOG_DEBUG、LOG_INFO 等宏 使用起来就是一个增强版的 printf cppLOG_INFO(server started on port %d,port);LOG_ERROR(connection failed: %s,strerror(errno));简洁、统一、零额外开销。时间戳的设计精髓在Logger.cc中输出日志时会调用Timestamp::now().toString()来获取当前时间的字符串表示。classTimestamp{public:Timestamp():microSecondsSinceEpoch_(0){}explicitTimestamp(int64_tmicroSecondsSinceEpoch):microSecondsSinceEpoch_(microSecondsSinceEpoch_){}staticTimestampnow();std::stringtoString()const;private:int64_tmicroSecondsSinceEpoch_;// 微秒级时间戳};Timestamp只有一个int64_t成员变量一、explicit修饰单参数构造函数禁止int64_t到Timestamp的隐式转换。否则函数参数为Timestamp类型时传入一个整数会被悄悄转换容易产生难以察觉的 bug。二、now()设计为静态成员函数。获取当前时间是一个与具体实例无关的操作如果设计为非静态就得先Timestamp t1; t1.now();白白创建和析构一个无用的中间对象。设计为static后直接Timestamp::now()即可返回的临时对象会自动析构。三、用int64_t存储微秒级时间而非struct timeval秒微秒两个字段。好处是时间比较和差值计算都变成简单的整数运算内存也更紧凑。注C11 中Timestamp::now()返回的临时对象会优先使用移动构造或 RVO 优化避免了拷贝开销。这也是Timestamp不需要显式声明析构函数的原因——只有一个int64_t没有需要释放的资源。