在开始之前,先将用到的一些工具类实现。
Noncopyable类
此类在Muduo源码中,作为不可拷贝构造和赋值的类的基类,通过禁止其拷贝构造和赋值运算符来实现,并被其他的类所默认继承(私有继承)。
/*
noncopyable 被继承以后,派生类对象可以正常构造和析构
但是不可用进行拷贝构造和赋值
*/
class noncopyable{
public:
noncopyable(const noncopyable&) = delete;
noncopyable& operator=(const noncopyable) = delete;
protected: // 可以被派生类访问
noncopyable() = default;
~noncopyable() = default;
};
Timestamp类
Timestamp类封装了多个接口,提供了Muduo库中的时间获取。
头文件:
#pragma once
#include <iostream>
#include <string>
class Timestamp
{
private:
int64_t microSecondsSinceEpoch_;
public:
Timestamp();
explicit Timestamp(int64_t microSecondsSinceEpoch); // explicit 不允许隐式类型转换
static Timestamp now();
std::string toString() const;
~Timestamp();
};
cpp文件
#include "Timestamp.h"
#include <time.h>
#include <sstream>
#include <iomanip>
Timestamp::Timestamp() : microSecondsSinceEpoch_(0)
{
}
Timestamp::Timestamp(int64_t microSecondsSinceEpochArg) : microSecondsSinceEpoch_(microSecondsSinceEpochArg)
{
}
Timestamp Timestamp::now()
{
return Timestamp(ti);
}
// return yyyy/MM/dd hh:mm:ss
std::string Timestamp::toString() const
{
// C++写法,格式化输出太麻烦
std::string time;
tm *tm_time = localtime(µSecondsSinceEpoch_);
std::ostringstream oss;
oss << std::setw(4) << (tm_time->tm_year + 1900) << "-"
<< std::setw(2) << std::setfill('0') << (tm_time->tm_mon + 1) << "-"
<< std::setw(2) << std::setfill('0') << tm_time->tm_mday << " "
<< std::setw(2) << std::setfill('0') << tm_time->tm_hour << ":"
<< std::setw(2) << std::setfill('0') << tm_time->tm_min << ":"
<< std::setw(2) << std::setfill('0') << tm_time->tm_sec;
time = oss.str();
return time;
// C语言写法
char buf[128] = {0};
sprintf(buf, "%4d-%02d-%02d %02d:%2d:%02d",
tm_time->tm_year + 1900,
tm_time->tm_mon + 1,
tm_time->tm_mday,
tm_time->tm_hour,
tm_time->tm_min,
tm_time->tm_sec);
return buf;
}
Timestamp::~Timestamp()
{
}
Logger类
源代码中日志逻辑比较复杂,而课程中老师实现了一个C语言输出风格的日志类,在此,我实现了一个C++风格的日志类LogStream,易于使用,可以通过宏定义实现日志分级,确保了线程间安全。(只是不知道这种频繁的对象创建会不会对性能有所影响,请大佬们指点~)
头文件:
#include "Timestamp.h"
#include <sstream>
#include <string>
#include <mutex>
#include <fstream>
// 定义日志级别 INFO ERROR FATAL DEBUG
enum LogLevel
{
INFO, // 普通信息
ERROR, // 错误信息
FATAL, // core信息
DEBUG, // 调试信息
};
// 直接使用左移运算法<<进行输出,末尾不需要加换行
#define LOG_INFO LogStream(INFO, __FILE__, __LINE__, __FUNCTION__)
// 直接使用左移运算法<<进行输出,末尾不需要加换行
#define LOG_ERROR LogStream(ERROR, __FILE__, __LINE__, __FUNCTION__)
// 直接使用左移运算法<<进行输出,末尾不需要加换行
#define LOG_FATAL LogStream(FATAL, __FILE__, __LINE__, __FUNCTION__)
#ifdef DEBUG_LOGGING_ENABLED
// 直接使用左移运算法<<进行输出,末尾不需要加换行
#define LOG_DEBUG LogStream(DEBUG, __FILE__, __LINE__, __FUNCTION__)
#else
#define LOG_DEBUG if(false) LogStream(DEBUG, __FILE__, __LINE__, __FUNCTION__)
#endif
// 每个LogStream对象都是一行Log日志
// 在对象析构的时候存入文件
class LogStream
{
public:
LogStream(const int logLevel,
const char* file = __FILE__,
const int line = __LINE__,
const char* func = __FUNCTION__);
// 在此处将ss_中所有内容写入文件
~LogStream();
// 代理到std::stringstream的函数
template <typename T>
LogStream &operator<<(const T &value);
private:
std::stringstream ss_; // 通过重载<<接收用户的多种数据的log信息
std::stringstream ssEnd_; // 保存log发生的位置信息
// 静态变量,全局只有一个,在多线程的多个实例化对象中也可保持线程安全
static std::mutex fileMutex;
int logLevel_;
};
// 代理到std::stringstream的函数
template <typename T>
LogStream& LogStream::operator<<(const T &value)
{
ss_ << value;
return *this;
}
CPP文件
#include "LogStream.h"
std::mutex LogStream::fileMutex;
LogStream::LogStream(const int logLevel, const char *file, const int line, const char* func)
: logLevel_(logLevel)
{
ssEnd_ << " --FILE:" << file << " LINE:" << line << " FUNC:" << func;
}
// 在此处将ss_中所有内容写入文件
LogStream::~LogStream()
{
if (ss_.str().empty())
return;
std::string output;
switch (logLevel_)
{
case INFO:
output += "[INFO] ";
break;
case ERROR:
output += "[ERROR]";
break;
case FATAL:
output += "[FATAL]";
break;
case DEBUG:
output += "[DEBUG]";
break;
default:
break;
}
std::string time = Timestamp::now().toString();
std::string filePath = time.substr(0, 10) + "-log.txt";
{
std::unique_lock<std::mutex> ulock(fileMutex);
// C++ 文件流
std::ofstream file(filePath, std::ios::app);
if (file.is_open())
{
file << output << time << " " << ss_.str() << ssEnd_.str() << std::endl;
file.close();
}
else
{
// std::cout << "error : log file not open!" << std::endl;
return;
}
}
if (logLevel_ == FATAL)
{
exit(-1);
}
}
Callbacks.h文件
使用C++的语法来定义一些类型别名,方便其他类使用
#include <functional>
#include <memory>
class Buffer;
class TcpConnection;
class Timestamp;
using TcpConnectionPtr = std::shared_ptr<TcpConnection>;
using TimerCallback = std::function<void()>;
using ConnectionCallback = std::function<void (const TcpConnectionPtr&)>;
using CloseCallback = std::function<void (const TcpConnectionPtr&)>;
using WriteCompleteCallback = std::function<void (const TcpConnectionPtr&)>;
using HighWaterMarkCallback = std::function<void (const TcpConnectionPtr&, size_t)>;
using MessageCallback = std::function<void (const TcpConnectionPtr&,
Buffer*,
Timestamp)>;