8.5 日志落地(LogSink)类设计(简单工厂模式)
1.⽇志落地类主要负责落地⽇志消息到⽬的地。
它主要包括以下内容:
• Formatter⽇志格式化器:主要是负责格式化⽇志消息,
• mutex互斥锁:保证多线程⽇志落地过程中的线程安全,避免出现交叉输出的情况。
这个类⽀持可扩展,其成员函数log设置为纯虚函数,当我们需要增加⼀个log输出⽬标, 可以增加⼀个类继承⾃该类并重写log⽅法实现具体的落地⽇志逻辑。
2.⽬前实现了三个不同⽅向上的⽇志落地:
• 标准输出:StdoutSink
• 固定⽂件:FileSink
• 滚动⽂件:RollSink
2.1滚动⽇志⽂件输出的必要性:
▪ 由于机器磁盘空间有限, 我们不可能⼀直⽆限地向⼀个⽂件中增加数据
▪ 如果⼀个⽇志⽂件体积太⼤,⼀⽅⾯是不好打开,另⼀⽅⾯是即时打开了由于包含数据巨⼤,也不利于查找我们需要的信息
▪ 所以实际开发中会对单个⽇志⽂件的⼤⼩也会做⼀些控制,即当⼤⼩超过某个⼤⼩时(如1GB),我们就重新创建⼀个新的⽇志⽂件来滚动写⽇志。 对于那些过期的⽇志, ⼤部分企业内部都有专⻔的运维⼈员去定时清理过期的⽇志,或者设置系统定时任务,定时清理过期⽇志。
2.2⽇志⽂件的滚动思想:
⽇志⽂件滚动的条件有两个:⽂件⼤⼩ 和 时间。我们可以选择:
▪ ⽇志⽂件在⼤于 1GB 的时候会更换新的⽂件
▪ 每天定点滚动⼀个⽇志⽂件
本项⽬基于⽂件⼤⼩的判断滚动⽣成新的⽂件
#ifndef __M_SINK_H__
#define __M_SINK_H__
#include "util.hpp"
#include "message.hpp"
#include "formatter.hpp"
#include <memory>
#include <mutex>
namespace bitlog{
class LogSink {
public:
using ptr = std::shared_ptr<LogSink>;
LogSink() {}
virtual ~LogSink() {}
virtual void log(const char *data, size_t len) = 0;
};
class StdoutSink : public LogSink {
public:
using ptr = std::shared_ptr<StdoutSink>;
StdoutSink() = default;
void log(const char *data, size_t len) {
std::cout.write(data, len);
}
};
class FileSink : public LogSink {
public:
using ptr = std::shared_ptr<FileSink>;
FileSink(const std::string &filename):_filename(filename) {
util::file::create_directory(util::file::path(filename));
_ofs.open(_filename, std::ios::binary | std::ios::app);
assert(_ofs.is_open());
}
const std::string &file() {return _filename; }
void log(const char *data, size_t len) {
_ofs.write((const char*)data, len);
if (_ofs.good() == false) {
std::cout << "⽇志输出⽂件失败!\n";
}
}
private:
std::string _filename;
std::ofstream _ofs;
};
class RollSink : public LogSink {
public:
using ptr = std::shared_ptr<RollSink>;
RollSink(const std::string &basename, size_t max_fsize):
_basename(basename), _max_fsize(max_fsize), _cur_fsize(0){
util::file::create_directory(util::file::path(basename));
}
void log(const char *data, size_t len) {
initLogFile();
_ofs.write(data, len);
if (_ofs.good() == false) {
std::cout << "⽇志输出⽂件失败!\n";
}
_cur_fsize += len;
}
private:
void initLogFile() {
if (_ofs.is_open() == false || _cur_fsize >= _max_fsize) {
_ofs.close();
std::string name = createFilename();
_ofs.open(name, std::ios::binary | std::ios::app);
assert(_ofs.is_open());
_cur_fsize = 0;
return;
}
return;
}
std::string createFilename() {
time_t t = time(NULL);
struct tm lt;
localtime_r(&t, <);
std::stringstream ss;
ss << _basename;
ss << lt.tm_year + 1900;
ss << lt.tm_mon + 1;
ss << lt.tm_mday;
ss << lt.tm_hour;
ss << lt.tm_min;
ss << lt.tm_sec;
ss << ".log";
return ss.str();
}
private:
std::string _basename;
std::ofstream _ofs;
size_t _max_fsize;
size_t _cur_fsize;
};
class SinkFactory {
public:
template<typename SinkType, typename ...Args>
static LogSink::ptr create(Args &&...args) {
return std::make_shared<SinkType>(std::forward<Args>(args)...);
}
};
}
#endif