log4cplus 是一个C++ 的日志库,虽然说对于 log4cplus 的使用已经很简单了,但是我们还想更简单,也就是只需要使用一个宏就可以完成不同级别日志的输出。
因为 log4cplus 的日志输出不同级别的日志可以使用不同级别的宏来输出,我觉得将 log4cplus 直接封装为一个类,然后我们可以调用一个宏,然后传入这个日志的级别,后面就是可变参数列表,可以传入任意类型任意个数的参数,最后以空格分隔输出到文件中。
但是并不是说 log4cplus 直接会帮你输出到文件中,而是需要你自己配置的,但是这里我们并不讲配置,也不讲使用,只是简单的封装。
设计:
我们决定将这个类设计为一个单例类,我们也采用最简单的懒汉模式,然后我们只需要获取到日志类的对象,然后我们调用里面的 log 方法及完成日志的输出。
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#define FILE_AND_LINE __FILE__ ":" TOSTRING(__LINE__) // 定义一个文件名和行号
// 定义日志级别宏
#define LOG4C(level, ...) \
Log::getInstance().log(level, FILE_AND_LINE,##__VA_ARGS__)
// 错误级别(log4cplus 中还有一个错误级别,但是因为这里智能用到这几种,另外的一个用不到,所以就不写了,如果有需要的话,可以直接写到枚举中,然后在 log 方法中添加一个关于该错误级别的日志输出即可)
enum Level{
DEBUG,
INFO,
WARNING,
ERROR,
FATAL
};
// 封装的 log4cplus 类(该类为单例模式,所以构造函数不能为 public 的,这里决定以 getInstance 函数来完成日志对象的输出)
class Log
{
public:
~Log(){}
// 获取日志对象
static Log &getInstance() {
static Log instance;
return instance;
}
// 日志对象的主要的函数,完成对日志的输出,因为这里需要的是可变参数列表,所以这里就使用可变模板参数列表
template<typename... Args>
void log(int level, Args... args) {
// 解析可变模板参数列表,将里面的参数都解析为 string 类型
std::string message = format_impl(args...);
switch(level){
case DEBUG:
LOG4CPLUS_DEBUG(logger, message);
break;
case WARNING:
LOG4CPLUS_WARN(logger, message);
break;
case INFO:
LOG4CPLUS_INFO(logger, message);
break;
case ERROR:
LOG4CPLUS_ERROR(logger, message);
break;
case FATAL:
LOG4CPLUS_FATAL(logger, message);
break;
default:
LOG4CPLUS_ERROR(logger, "No such error");
break;
}
}
private:
// 解析函数也是一个可变模板参数列表,这里我就不说明这个为什么可以解析了,如果有想知道的可以问一下ai
template<typename T>
std::string format_impl(const T& value){
std::ostringstream oss;
oss << value;
return oss.str();
}
template<typename T, typename... Args>
std::string format_impl(const T& first, const Args&... args){
std::ostringstream oss;
oss << first << " " << format_impl(args...);
return oss.str();
}
log4cplus::Logger logger;
private:
Log() {
// 这个就是读取 log4cplus 的配置
log4cplus::PropertyConfigurator::doConfigure(LOG4CPLUS_TEXT("/home/nano/log4cplus-2.0.8/build/log4cplusConfig"));
// 获取日志记录器,
logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("myLog"));
}
Log(const Log&);
Log operator=(const Log&);
};
log 函数是用于日志输出的,但是我们不想每次都 getInstance 后调用 log 方法,所以我们定义一个宏,这个宏就会替换为这个函数的调用。