接着之前我们[muduo网络库]——muduo库noncopyable(剖析muduo网络库核心部分、设计思想),我们接下来继续看muduo库中的logger类。
logger类
日志类相对来说比较简单,它主要是为了帮助我们查看程序的运行,其实在之前的每一类,我们都能看见它的影子,而且在每个项目中也都能经常用到。
重要的成员变量
- 日志级别
int logLevel_; // 类的成员变量
logLevel_
就是一个枚举类型,包括以下四类
- INFO:打印重要的流程信息,跟踪核心流程
- ERROE:一些错误,但是不影响系统的正常运行
- FATAL: 出现这种问题以后,系统不能正常的运行
- DEBUG:调试信息,一般有很多,正常情况下会选择关掉,需要的时候在打开
enum LogLevel
{
INFO,
ERROR,
FATAL,
DEBUG,
};
重要成员函数
- 注意 因为日志对象在整个项目中是唯一的,所以采用单例模式(指在整个系统生命周期里,保证一个类只能产生一个实例,确保该类的唯一性),来获取日志对象,构造析构私有化,禁止外部构造和析构,这里也不需要在自定义了。并且logger类继承了noncopyable,保证了禁止外部拷贝和赋值,确保实例的唯一性。
Logger& Logger ::instance()
{
static Logger logger;
return logger;
}
- 设置日志级别
void Logger ::setLogLevel(int level)
{
logLevel_=level;
}
- 写日志 模式----[级别信息] time :msg
void Logger ::log(std::string msg)
{
switch (logLevel_)
{
case INFO:
std::cout<<"[INFO]";
break;
case ERROR:
std::cout<<"[ERROR]";
break;
case FATAL:
std::cout<<"[FATAL]";
break;
case DEBUG:
std::cout<<"[DEBUG]";
break;
default:
break;
}
//打印时间和msg TimeStamp在下一篇会讲到
std::cout<< TimeStamp::now().toString() <<" : "<< msg <<std::endl;
}
注意 对于调用者来说只想简单的调用一个函数来完成日志的输出,所以我们采用宏定义来简化对接口的调用,例如INFO
#define LOG_INFO(logmsgFormat, ...) \
do \
{ \
Logger &logger = Logger::instance(); \
logger.setLogLevel(INFO); \
char buf[1024] = {0}; \
snprintf(buf, 1024, logmsgFormat, ##__VA_ARGS__); \
logger.log(buf); \
} while(0)
首先获取日志唯一的实例对象,然后设置日志级别,把用户要打印的写入buf中,然后打印出来。其余三个级别都是类似的。注意DEBUG级别,我们需要用一个宏来控制打印开关,否则日志会很多。
// 一般关闭 通过宏打开
#ifdef MUDEBUG
#define LOG_DEBUG(logmsgFormat, ...) \
do \
{ \
//略 \
} while (0)
#else
#define LOG_DEBUG(logmsgFormat, ...)
#endif
再提一句用do{...}while(0);
,c语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参,所以如果宏写得长或者有逻辑的时候,我们在调用的时候,可能会出错或者编译不通过。所以在我们写得宏定义长的时候,我们就需要使用do{…}while(0);包裹住,这样就不会影响#define操作。注意 :每一行后面都要加\
,并且\
后面不能有任何东西了。
补充一下单例模式
线程安全
在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。一般通过以下两种方式:
- 给共享的资源加把锁,保证每个资源变量每时每刻至多被一个线程占用。
- 让线程也拥有资源,不用去共享进程中的资源。如: 使用threadlocal可以为每个线程的维护一个私有的本地变量。
单例模式概念
单例模式指在整个系统生命周期里,保证一个类只能产生一个实例,确保该类的唯一性。
分类
单例模式可以分为懒汉式和饿汉式,两者之间的区别在于创建实例的时间不同:
懒汉式:指系统运行中,实例并不存在,只有当需要使用该实例时,才会去创建并使用实例。(这种方式要考虑线程安全)
饿汉式:指系统一运行,就初始化创建实例,当需要时,直接调用即可。(本身就线程安全,没有多线程的问题)
特点
- 构造函数和析构函数为private类型,目的禁止外部构造和析构
- 拷贝构造和赋值构造函数为private类型,目的是禁止外部拷贝和赋值,确保实例的唯一性
- 类里有个获取实例的静态函数,可以全局访问
logger类中
在C++11内部静态变量的方式里是线程安全的,只创建一次实例,这个方式非常推荐,实现的代码最少!也就是我们上面提到的
static Logger &instance(); //.h中
Logger& Logger ::instance() //.cc中
{
static Logger logger;
return logger;
}
代码地址:https://github.com/Cheeron955/mymuduo/tree/master