文章目录
- 日志器模块
- 设计
- 同步日志器实现
- 测试
日志器模块
设计
日志器模块的主要功能就是将前面的所有模块进行整合,向外提供接口,完成不同等级日志的输出
需要管理的成员有
- 格式化模块的对象
- 落地模块的对象组
- 当前默认的输出等级
- 互斥锁
- 日志器名称
- 各个等级的日志输出操作
对于同步日志和异步日志,实现的方式是不同的,因此需要抽象出一个基类,再派生出两个子类
同步日志器实现
/*日志器模块的实现1. 抽象日志器基类2. 派生同步异步日志器子类
*/
#pragma once
// #ifndef _GUN_SOURCE
// #define _GNU_SOURCE
// #endif
#include "util.hpp"
#include "level.hpp"
#include "format.hpp"
#include "sink.hpp"
#include <atomic>
#include <mutex>
#include <cstdarg>namespace Xulog
{class Logger{public:using ptr = std::shared_ptr<Logger>;Logger(const std::string &loggername, LogLevel::value level, Formatter::ptr &formatter, std::vector<LogSink::ptr> sinks): _logger_name(loggername), _limit_level(level), _formatter(formatter), _sinks(sinks.begin(), sinks.end()){}// 构造日志消息,格式化,交给输出接口void debug(const std::string &file, size_t line, const std::string &fmt, ...){// 是否达到输出等级if (LogLevel::value::DEBUG < _limit_level)return;// 将不定参进行格式化,将其转为字符串va_list ap;va_start(ap, fmt);char *res;int ret = vasprintf(&res, fmt.c_str(), ap);if (ret == -1){std::cout << "vasprintf fail\n";return;}va_end(ap);serialize(LogLevel::value::DEBUG, file, line, res);free(res);}void info(const std::string &file, size_t line, const std::string &fmt, ...){// 是否达到输出等级if (LogLevel::value::INFO < _limit_level)return;// 将不定参进行格式化,将其转为字符串va_list ap;va_start(ap, fmt);char *res;int ret = vasprintf(&res, fmt.c_str(), ap);if (ret == -1){std::cout << "vasprintf fail\n";return;}va_end(ap);serialize(LogLevel::value::INFO, file, line, res);free(res);}void warn(const std::string &file, size_t line, const std::string &fmt, ...){// 是否达到输出等级if (LogLevel::value::WARN < _limit_level)return;// 将不定参进行格式化,将其转为字符串va_list ap;va_start(ap, fmt);char *res;int ret = vasprintf(&res, fmt.c_str(), ap);if (ret == -1){std::cout << "vasprintf fail\n";return;}va_end(ap);serialize(LogLevel::value::WARN, file, line, res);free(res);}void error(const std::string &file, size_t line, const std::string &fmt, ...){// 是否达到输出等级if (LogLevel::value::ERROR < _limit_level)return;// 将不定参进行格式化,将其转为字符串va_list ap;va_start(ap, fmt);char *res;int ret = vasprintf(&res, fmt.c_str(), ap);if (ret == -1){std::cout << "vasprintf fail\n";return;}va_end(ap);serialize(LogLevel::value::ERROR, file, line, res);free(res);}void fatal(const std::string &file, size_t line, const std::string &fmt, ...){// 是否达到输出等级if (LogLevel::value::FATAL < _limit_level)return;// 将不定参进行格式化,将其转为字符串va_list ap;va_start(ap, fmt);char *res;int ret = vasprintf(&res, fmt.c_str(), ap);if (ret == -1){std::cout << "vasprintf fail\n";return;}va_end(ap);serialize(LogLevel::value::FATAL, file, line, res);free(res);}protected:// 抽象接口完成实际的落地输出,同步异步的落地方式不同virtual void log(const char *data, size_t len) = 0;void serialize(LogLevel::value level, const std::string &file, size_t line, char *str){// 日志内容格式化LogMsg msg(level, line, file, _logger_name, str);std::stringstream ss;_formatter->Format(ss, msg);// 日志落地log(ss.str().c_str(), ss.str().size());}std::mutex _mutex;std::string _logger_name;std::atomic<LogLevel::value> _limit_level;Formatter::ptr _formatter;std::vector<LogSink::ptr> _sinks;};// 同步日志器class SyncLogger : public Logger{public:SyncLogger(const std::string &loggername, LogLevel::value level, Formatter::ptr &formatter, std::vector<LogSink::ptr> sinks): Logger(loggername, level, formatter, sinks){}protected:// 直接通过落地模块句柄进行日志输出void log(const char *data, size_t len) override{std::unique_lock<std::mutex> lock(_mutex);if (_sinks.empty())return;elsefor (auto &sink : _sinks){sink->log(data, len);}}};// TODO 异步日志器
}
测试
#include "util.hpp"
#include "level.hpp"
#include "format.hpp"
#include "sink.hpp"
#include "logger.hpp"// 扩展测试: 滚动文件(时间)
// 1. 以时间段滚动
// 2. time(nullptr)%gap;
enum class TimeGap
{GAP_SECOND,GAP_MINUTE,GAP_HOUR,GAP_DAY
};
class RollSinkByTime : public Xulog::LogSink
{
public:// 传入文件名时,构造并打开文件,将操作句柄管理起来RollSinkByTime(const std::string &basename, TimeGap gap_type): _basename(basename){switch (gap_type){case TimeGap::GAP_SECOND:_gap_size = 1;break;case TimeGap::GAP_MINUTE:_gap_size = 60;break;case TimeGap::GAP_HOUR:_gap_size = 3600;break;case TimeGap::GAP_DAY:_gap_size = 3600 * 24;break;}_current_gap = _gap_size == 1 ? Xulog::Util::Date::getTime() : (Xulog::Util::Date::getTime() % _gap_size);std::string filename = createNewFile();Xulog::Util::File::createDirectory(Xulog::Util::File::path(filename)); // 创建目录_ofs.open(filename, std::ios::binary | std::ios::app);assert(_ofs.is_open());}void log(const char *data, size_t len){time_t current = Xulog::Util::Date::getTime();if (current % _gap_size != _current_gap){std::string filename = createNewFile();_ofs.close();_ofs.open(filename, std::ios::binary | std::ios::app);assert(_ofs.is_open());}_ofs.write(data, len);assert(_ofs.good());}private:std::string createNewFile(){time_t t = Xulog::Util::Date::getTime();struct tm lt;localtime_r(&t, <);std::stringstream filename;filename << _basename << lt.tm_year + 1900 << lt.tm_mon + 1 << lt.tm_mday << lt.tm_hour << lt.tm_min << lt.tm_sec << ".log";return filename.str();}private:std::string _basename;std::ofstream _ofs;size_t _current_gap; // 当前时间段的个数size_t _gap_size; // 间隔大小
};int main()
{// 测试同步日志器std::string logger_name = "synclog";Xulog::LogLevel::value limit = Xulog::LogLevel::value::WARN;Xulog::Formatter::ptr fmt(new Xulog::Formatter());Xulog::LogSink::ptr std_lsp = Xulog::SinkFactory::create<Xulog::StdoutSink>();Xulog::LogSink::ptr file_lsp = Xulog::SinkFactory::create<Xulog::FileSink>("./log/test.log");Xulog::LogSink::ptr roll_lsp = Xulog::SinkFactory::create<Xulog::RollSinkBySize>("./log/roll-", 1024 * 1024); // 每个文件1MBXulog::LogSink::ptr time_lsp = Xulog::SinkFactory::create<RollSinkByTime>("./log/roll-", TimeGap::GAP_SECOND); // 每个文件1sstd::vector<Xulog::LogSink::ptr> sinks = {std_lsp, file_lsp, roll_lsp, time_lsp};Xulog::Logger::ptr logger(new Xulog::SyncLogger(logger_name, limit, fmt, sinks));std::string str = "测试同步日志器-";logger->debug(__FILE__, __LINE__, "%s", str.c_str());logger->error(__FILE__, __LINE__, "%s", str.c_str());logger->fatal(__FILE__, __LINE__, "%s", str.c_str());logger->info(__FILE__, __LINE__, "%s", str.c_str());logger->warn(__FILE__, __LINE__, "%s", str.c_str());size_t size = 0;int cnt = 1;while (size < 1024 * 1024 * 10) // 10 个{logger->fatal(__FILE__, __LINE__, "%s-%d", str.c_str(), cnt++);size += 20;}return 0;
}