日志类相对来说比较简单,在每个项目中 也都能经常用到。
其主要的函数就是三个
1、因为日志对象在整个项目中是唯一的,所以采用单例模式,来获取日志对象
2、设置日志级别,通常包括INFO ERROR FATAL DEBUG这四大类
3、设置日志信息
//定义日志级别 INFO ERROR FATAL DEBUG
enum LogLevel
{
INFO, //普通信息
ERROR, //错误信息
FATAL, //core信息
DEBUG, //调试信息
};
//输出一个日志类
class Logger:noncopyable
{
public:
//获取日志唯一的实例对象
static Logger& instance();
//设置日志级别
void setLogLevel(int level);
//写日志
void log(std::string msg);
private:
int logLevel_;
};
其内部实现如下所示,也是主打一个简单易懂,这里就不多说啥了
#include "Logger.h"
#include "Timestamp.h" //该类是关于时间的类,在下一篇文章中有解释
#include
//获取日志的唯一实例对象
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 //这里和时间有关的函数 放在下一篇文章中实现
std::cout<< Timestamp::now().toString() <<" : "<
其实设置日志级别 或者是获取单例等,对于调用者来说是不重要的,因为调用者只想简单的调用一个函数来完成日志的输出,如果我们直接将上述类交给调用者,他们用起来还是太复杂了。
我们可以在Logger.h文件中 采用宏定义来简化对接口的调用
//LOG_INFO("%s %d",arg1,arg2)
#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)
#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)
#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);\
}while (0)
#ifdef MUDEBUG //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
#define LOG_DEBUG(logmsgFormat, ...)
#endif
这里的宏定义为什么使用do……while(0)呢,可参考下面的博客,总之就是
辅助定义复杂的宏,避免引用的时候出错,提高代码健壮性do while(0)的作用以及原因_康来个程的博客-CSDN博客
可变参数宏...和_ _VA_ARGS_ _
…和_ _VA_ARGS_ _是组合使用的,用在不确定参数个数的场合中。