流程
- 写日志的信息是个磁盘I/O,肯定不能把磁盘IO的花销算在RPC请求的业务过程中,那么这样的话,Rpc请求业务太慢了,无形中在业务中增加了磁盘IO操作。
- 所以一般都会引入一个队列,把日志信息添加到队尾。然后专门有一个写日志线程,来操作磁盘IO操作。这样磁盘IO操作就不会把时间花费在rpc业务处理过程中。
- RpcProvider是一个epoll+多线程的处理过程,也就是说多个线程都会写日志信息,所以说多个线程都会在缓冲区队列中添加数据,所以这个队列必须保证线程安全,所以得线程互斥来保证线程安全。
- 如果队列空了,就没必要抢锁了,一直等待就行。这也就是线程通信。

异步写日志的日志队列
template<typename T>
class LockQueue
{
public:
// 多个worker线程都会写日志queue
void Push(const T &data)
{
std::lock_guard<std::mutex> lock(m_mutex);
m_queue.push(data);
m_condvariable.notify_one();
}
// 一个线程读日志queue,写日志文件
T Pop()
{
std::unique_lock<std::mutex> lock(m_mutex);
while (m_queue.empty())
{
// 日志队列为空,线程进入wait状态
m_condvariable.wait(lock);
}
T data = m_queue.front();
m_queue.pop();
return data;
}
private:
std::queue<T> m_queue;
std::mutex m_mutex;
std::condition_variable m_condvariable;
};
日志系统
// 定义宏 LOG_INFO("xxx %d %s", 20, "xxxx")
#define LOG_INFO(logmsgformat, ...) \
do \
{ \
Logger &logger = Logger::GetInstance(); \
logger.SetLogLevel(INFO); \
char c[1024] = {0}; \
snprintf(c, 1024, logmsgformat, ##__VA_ARGS__); \
logger.Log(c); \
} while(0) \
#define LOG_ERR(logmsgformat, ...) \
do \
{ \
Logger &logger = Logger::GetInstance(); \
logger.SetLogLevel(ERROR); \
char c[1024] = {0}; \
snprintf(c, 1024, logmsgformat, ##__VA_ARGS__); \
logger.Log(c); \
} while(0) \
// 定义日志级别
enum LogLevel
{
INFO, // 普通信息
ERROR, // 错误信息
};
// Mprpc框架提供的日志系统
class Logger
{
public:
// 获取日志的单例
static Logger& GetInstance();
// 设置日志级别
void SetLogLevel(LogLevel level);
// 写日志
void Log(std::string msg);
private:
int m_loglevel; // 记录日志级别
LockQueue<std::string> m_lckQue; // 日志缓冲队列
Logger();
Logger(const Logger&) = delete;
Logger(Logger&&) = delete;
};
// 获取日志的单例
Logger& Logger::GetInstance()
{
static Logger logger;
return logger;
}
Logger::Logger()
{
// 启动专门的写日志线程
std::thread writeLogTask([&](){
for (;;)
{
// 获取当前的日期,然后取日志信息,写入相应的日志文件当中 a+
time_t now = time(nullptr);
tm *nowtm = localtime(&now);
char file_name[128];
sprintf(file_name, "%d-%d-%d-log.txt", nowtm->tm_year+1900, nowtm->tm_mon+1, nowtm->tm_mday);
FILE *pf = fopen(file_name, "a+");
if (pf == nullptr)
{
std::cout << "logger file : " << file_name << " open error!" << std::endl;
exit(EXIT_FAILURE);
}
std::string msg = m_lckQue.Pop();
char time_buf[128] = {0};
sprintf(time_buf, "%d:%d:%d =>[%s] ",
nowtm->tm_hour,
nowtm->tm_min,
nowtm->tm_sec,
(m_loglevel == INFO ? "info" : "error"));
msg.insert(0, time_buf);
msg.append("\n");
fputs(msg.c_str(), pf);
fclose(pf);
}
});
// 设置分离线程,守护线程
writeLogTask.detach();
}
// 设置日志级别
void Logger::SetLogLevel(LogLevel level)
{
m_loglevel = level;
}
// 写日志, 把日志信息写入lockqueue缓冲区当中
void Logger::Log(std::string msg)
{
m_lckQue.Push(msg);
}