文章目录
专栏导读
🌸作者简介:花想云,在读本科生一枚,C/C++领域新星创作者,新星计划导师,阿里云专家博主,CSDN内容合伙人...致力于 C/C++、Linux 学习。
🌸专栏简介:本文收录于 C++项目------基于多设计模式下的同步与异步日志系统
日志宏&全局接口设计
本章我们将完成提供全局接口&宏函数,对日志系统接口进行使用便捷性优化(避免用户自己创建单例)。
设计思想:
提供获取指定日志器的全局接口
(避免用户自己操作单例对象);使用宏函数对日志器的接口进行代理
(代理模式);提供宏函数,直接通过默认日志器进行日志的标准输出打印
(省去获取日志器的操作);
cpp
#ifndef __MY_LOG__
#define __MY_LOG__
#include "logger.hpp"
namespace LOG
{
// 1.提供获取指定日志器的全局接口(避免用户自己操作单例对象)
Logger::ptr getLogger(const std::string &name)
{
return LOG::LoggerManager::getInstance().getLogger(name);
}
Logger::ptr rootLogger()
{
return LOG::LoggerManager::getInstance().rootLogger();
}
// 2.实用宏函数对日志器的接口进行代理(代理模式)
#define debug(fmt, ...) debug(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define info(fmt, ...) info(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define warn(fmt, ...) warn(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define error(fmt, ...) error(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define fatal(fmt, ...) fatal(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
// 3.提供宏函数,直接通过默认日志器进行日志的标准输出打印
#define DEBUG(fmt, ...) LOG::rootLogger()->debug(fmt, ##__VA_ARGS__)
#define INFO(fmt, ...) LOG::rootLogger()->info(fmt, ##__VA_ARGS__)
#define WARN(fmt, ...) LOG::rootLogger()->warn(fmt, ##__VA_ARGS__)
#define ERROR(fmt, ...) LOG::rootLogger()->error(fmt, ##__VA_ARGS__)
#define FATAL(fmt, ...) LOG::rootLogger()->fatal(fmt, ##__VA_ARGS__)
}
#endif
全局接口测试
测试代码
cpp
void log_test()
{
DEBUG("%s", "测试日志");
INFO("%s", "测试日志");
WARN("%s", "测试日志");
ERROR("%s", "测试日志");
FATAL("%s", "测试日志");
size_t count = 0;
while(count < 300000)
{
FATAL("测试日志-%d", count++);
}
}
int main()
{
log_test();
return 0;
}
项目目录结构整理
示例代码
cpp
// example/test.cc
#include "../logs/log.h"
#include <unistd.h>
void log_test(const std::string& name)
{
INFO("%s", "测试开始");
LOG::Logger::ptr logger = LOG::LoggerManager::getInstance().getLogger("async_logger");
logger->debug(__FILE__, __LINE__, "%s", "测试日志");
logger->info(__FILE__, __LINE__, "%s", "测试日志");
logger->warn(__FILE__, __LINE__, "%s", "测试日志");
logger->error(__FILE__, __LINE__, "%s", "测试日志");
logger->fatal(__FILE__, __LINE__, "%s", "测试日志");
INFO("%s", "测试完毕");
}
int main()
{
std::unique_ptr<LOG::LoggerBuilder> builder(new LOG::GlobalLoggerBuilder());
builder->buildLoggerName("async_logger");
builder->buildLoggerLevel(LOG::LogLevel::value::WARN);
builder->buildFormatter("[%c][%f:%l]%m%n");
builder->buildLoggerType(LOG::LoggerType::LOGGER_ASYNC);
builder->buildSink<LOG::FileSink>("./logfile/async.log");
builder->buildSink<LOG::StdOutSink>();
builder->buildSink<LOG::RollBySizeSink>("./logfile/roll-async-by-size", 1024 * 1024);
builder->build();
log_test("async_logger");
return 0;
}
拓展示例代码
cpp
// extent/test.cc
#include "../logs/log.h"
#include <unistd.h>
enum class TimeGap
{
GAP_SECOND,
GAP_MINUTE,
GAP_HOUR,
GAP_DAY
};
class RollByTimeSink : public LOG::LogSink
{
public:
// 构造时传入文件名,并打开文件,将操作句柄管理起来
RollByTimeSink(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;
}
_cur_gap = _gap_size == 1? LOG::util::Date::getTime() : LOG::util::Date::getTime() % _gap_size; // 获取当前是第几个时间段
std::string filename = createNewFile();
LOG::util::File::createDirectory(LOG::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 cur = LOG::util::Date::getTime();
if((cur % _gap_size) != _cur_gap)
{
_ofs.close();
std::string filename = createNewFile();
_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 = LOG::util::Date::getTime();
struct tm lt;
localtime_r(&t, <);
std::stringstream filename;
filename << _basename;
filename << lt.tm_year + 1900;
filename << lt.tm_mon + 1;
filename << lt.tm_mday;
filename << lt.tm_hour;
filename << lt.tm_min;
filename << lt.tm_sec;
filename << "-";
filename << ".log";
return filename.str();
}
private:
std::string _basename;
size_t _gap_size; // 时间段的大小
int _cur_gap; // 当前是第几个时间段
std::ofstream _ofs;
};
int main()
{
std::unique_ptr<LOG::LoggerBuilder> builder(new LOG::GlobalLoggerBuilder());
builder->buildLoggerName("async_logger");
builder->buildLoggerLevel(LOG::LogLevel::value::WARN);
builder->buildFormatter("[%c][%f:%l]%m%n");
builder->buildLoggerType(LOG::LoggerType::LOGGER_ASYNC);
builder->buildSink<LOG::FileSink>("./logfile/async.log");
builder->buildSink<LOG::StdOutSink>();
builder->buildSink<RollByTimeSink>("./logfile/roll-async-by-time", TimeGap::GAP_SECOND);
LOG::Logger::ptr logger = builder->build();
size_t cur = LOG::util::Date::getTime();
while(LOG::util::Date::getTime() < cur + 5)
{
logger->fatal("这是一条测试日志");
}
return 0;
}