作为一个宏大的、功能健全的muduo库,考虑的肯定是众多情况是否可以高效满足;而作为学习者,我们需要抽取其中的精华进行简要实现,这要求我们足够了解muduo库。
做项目 = 模仿 + 修改,不要担心自己学了也不会写怎么办,重要的是积累,学到了这些方法,如果下次在遇到通用需求的时候你能够回想起之前的解决方法就够了。送上一段话!
日志是用来记录重大事件的工具。
日志文件是重要的系统信息文件,其中记录了很多重要的系统事件。包括用户的登录信息,系统的启动信息,系统的安全信息,邮件相关信息,各种服务相关信息。
日志对于安全来说也很重要,它记录了每天系统发生的各种事情,通过日志来检查错误发生的原因,或者受到攻击时攻击者留下的痕迹。
上面是muduo库的主要日志级别。
上面是我搜索到的日志级别解释。
日志级别划分
我们这里主要用到INFO、DEBUG、WARN、ERROR、FATAL作为我们小规模的日志库日志级别划分。
日志级别设置
static修饰类成员函数则该成员函数属于整个类,而非类对象,调用需要加类域,初始化要在类外。
日志实例创建——单例模式
我们通过返回静态变量能够获得唯一的日志实例,使用的是懒汉式单例模式。有关单例模式,想要了解复习的uu可以跳转到此。
写日志
关于写日志,我们需要确定统一的写的格式,muduo库选择使用宏定义的方式来统一我们写单条日志的格式。
这里使用到了是将宏定义做成流对象进行传输,类似于c++中的cout。我们同样可以使用类似于printf的格式类型来实现我们的日志输出格式化。比如:
#define LOG_INFO(logmsgformat, ...) \
do { \
Log& lp = Log::getInstance(); \
lp.setLogLevel(INFO); \
char buf[64] = {0}; \
snprintf(buf, sizeof buf, logmsgformat, ##__VA_ARGS__); \
lp.writeLog(buf); \
} while (0)
#define LOG_DEBUG(logmsgformat, ...) \
do { \
Log& lp = Log::getInstance(); \
lp.setLogLevel(DEBUG); \
char buf[64] = {0}; \
snprintf(buf, sizeof buf, logmsgformat, ##__VA_ARGS__); \
lp.writeLog(buf); \
} while (0)
#define LOG_WARN(logmsgformat, ...) \
do { \
Log& lp = Log::getInstance(); \
lp.setLogLevel(WARN); \
char buf[64] = {0}; \
snprintf(buf, sizeof buf, logmsgformat, ##__VA_ARGS__); \
lp.writeLog(buf); \
} while (0)
#define LOG_ERROR(logmsgformat, ...) \
do { \
Log& lp = Log::getInstance(); \
lp.setLogLevel(ERROR); \
char buf[64] = {0}; \
snprintf(buf, sizeof buf, logmsgformat, ##__VA_ARGS__); \
lp.writeLog(buf); \
} while (0)
#define LOG_FATAL(logmsgformat, ...) \
do { \
Log& lp = Log::getInstance(); \
lp.setLogLevel(FATAL); \
char buf[64] = {0}; \
snprintf(buf, sizeof buf, logmsgformat, ##__VA_ARGS__); \
lp.writeLog(buf); \
exit(-1); \
} while (0)
VA_ARGS 是一个预处理宏,用于可变参数宏中。当你在一个宏定义中使用可变参数时,VA_ARGS 允许你捕获那些参数。它常用于宏定义的可变参数列表。
Log& lp = Log::getInstance();
注意这行细节,我们使用引用可以避免赋值运算符。另外,我们也可以修改getInstance返回值,返回一个指针,这样也是可以实现功能的。
因为FATAL定义为出现不可逆转的错误,所以采用程序结束的方式,exit(-1);
对于writeLog函数的实现,我进行了规范的定义,[] : time, msg
。输出结果应该如下:
后面将对时间功能进行重写。
关于日志msg的格式输出,应该统一标准,至少需要达到这样的标准,才可以更好地在后期进行调试。
在此还要注意,buf的长度要大于我们输出的长度!!!
//Log.h
#pragma once
#include <string>
#include <iostream>
#include <unistd.h>
#include "noncopyable.h"
enum LogLevel {
INFO,
ERROR,
WARN,
FATAL,
DEBUG,
};
#define LOG_INFO(logmsgformat, ...) \
do { \
Log& lp = Log::getInstance(); \
lp.setLogLevel(INFO); \
char buf[128] = {0}; \
snprintf(buf, sizeof buf, logmsgformat, ##__VA_ARGS__); \
lp.writeLog(buf); \
} while (0)
#define LOG_DEBUG(logmsgformat, ...) \
do { \
Log& lp = Log::getInstance(); \
lp.setLogLevel(DEBUG); \
char buf[128] = {0}; \
snprintf(buf, sizeof buf, logmsgformat, ##__VA_ARGS__); \
lp.writeLog(buf); \
} while (0)
#define LOG_WARN(logmsgformat, ...) \
do { \
Log& lp = Log::getInstance(); \
lp.setLogLevel(WARN); \
char buf[128] = {0}; \
snprintf(buf, sizeof buf, logmsgformat, ##__VA_ARGS__); \
lp.writeLog(buf); \
} while (0)
#define LOG_ERROR(logmsgformat, ...) \
do { \
Log& lp = Log::getInstance(); \
lp.setLogLevel(ERROR); \
char buf[128] = {0}; \
snprintf(buf, sizeof buf, logmsgformat, ##__VA_ARGS__); \
lp.writeLog(buf); \
} while (0)
#define LOG_FATAL(logmsgformat, ...) \
do { \
Log& lp = Log::getInstance(); \
lp.setLogLevel(FATAL); \
char buf[128] = {0}; \
snprintf(buf, sizeof buf, logmsgformat, ##__VA_ARGS__); \
lp.writeLog(buf); \
exit(-1); \
} while (0)
class Log : noncopyable {
public:
static Log& getInstance();
void writeLog(const char* s);
void setLogLevel(LogLevel logLevel) {
logLevel_ = logLevel;
}
private:
//Log() {};
LogLevel logLevel_;
};
//Log.cc
#include "Log.h"
Log& Log::getInstance() {
static Log log;
return log;
}
// [] : time, msg
void Log::writeLog(const char* s) {
std::string ss;
switch (logLevel_)
{
case INFO:
ss += "[INFO] : ";
break;
case DEBUG:
ss += "[DEBUG] : ";
break;
case ERROR:
ss += "[ERROR] : ";
break;
case FATAL:
ss += "[FATAL] : ";
break;
case WARN:
ss += "[WARN] : ";
break;
default:
break;
}
std::cout << ss << "time, " << s << std::endl;
}