同步异步日志系统
一、日志器模块设计
功能:对前面所有功能进行整合,向外提供接口完成不同等级日志的输出。
管理的成员:
1.格式化模块对象
2.落地模块对象
3.默认的日志输出限制等级(大于等于限制输出等级的日志才能输出)
4.互斥锁(保证日志输出过程的线程安全,不会出现交叉日志)
5.日志名称(日志器的唯一标识,方便查找)
提供的操作:
debug等级日志的输出操作(分别封装日志消息LogMsg——各个接口日志等级不同)
info等级日志的输出操作
warn等级日志的输出操作
error等级日志的输出操作
fatal等级日志的输出操作
实现:
1.实现Logger基类(派生出同步日志器和异步日志器)
2.因为两种日志器的落地方式不同,需要将落地操作给抽象出来,不同的日志器调用不同的落地操作进行日志落地
3.模块关联过程中使用基类指针对子类日志器对象进行日志管理和操作
当前日志系统支持同步日志&异步日志,它们的不同点在于日志的落地方式上不同:
同步日志器:直接对日志消息进行输出
异步日志器:先将日志消息放到缓冲区,然后异步线程进行输出
因此:日志器类在设计的时候,先要设计一个Logger的基类,在Logger基类的基础上,继承出同步日志器(SyncLogger)和异步日志器(AsyncLoggrr)。
1.1 同步日志器模块设计
- 同步日志器的设计和具体框架
/*日志器模块
1.先设计一个日志器的基类
2.根据基类派生出不同的日志器
*/
#include "util.hpp"
#include "level.hpp"
#include "sink.hpp"
#include "format.hpp"
#include <memory>
#include <mutex>
#include <atomic>
namespace logslearn
{
// 设计日志器基类
class Logger
{
// 公有
public:
// 基类指针,用来控制继承子类的对象
using ptr = std::shared_ptr<Logger>;
// 操作方法
// 构造日志消息对象并进行格式化,得到格式化后的日志消息字符串--然后进行落地输出,5个等级
void debug(const std::string &file, size_t line, const std::string &fmt, ...); // 日志的输出操作
void info(const std::string &file, size_t line, const std::string &fmt, ...); // 日志的输出操作
void warn(const std::string &file, size_t line, const std::string &fmt, ...); // 日志的输出操作
void error(const std::string &file, size_t line, const std::string &fmt, ...); // 日志的输出操作
void fatal(const std::string &file, size_t line, const std::string &fmt, ...); // 日志的输出操作
protected:
// 日志落地,抽象接口完成实际的落地输出——不同的日志器会有不同的实际落地方式
virtual void log(const char *data, size_t len) = 0;
protected:
std::mutex _mutex; // 互斥锁
std::string _logger_name; // 日志器的名字
std::atomic<loglevel::value> _limit_level; // 限制日志等级,atomic原子操作的意思是该操作执行过程中不能被中断,该操作要么不执行,要么全部执行,不存在执行一部分的情况。
Formatter::ptr _formatter; // 控制格式化模块的对象
std::vector<LogSink::ptr> _sliks; // 这是一个数组,数组里存放日志落地方式的对象
};
// 派生出同步日志器
class SyncLogger : public Logger
{
protected:
// 重写虚函数
void log(const char *data, size_t len)
{
}
};
}
- 同步日志器的具体实现
#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__
/*日志器模块
1.先设计一个日志器的基类
2.根据基类派生出不同的日志器
*/
#define _GNU_SOURCE
#include "util.hpp"
#include "level.hpp"
#include "sink.hpp"
#include "format.hpp"
#include <memory>
#include <mutex>
#include <atomic>
#include <cstdarg>
namespace logslearn
{
// 设计日志器基类
class Logger
{
// 公有
public:
// 基类指针,用来控制继承子类的对象
using ptr = std::shared_ptr<Logger>;
// 构造函数
Logger(const std::string &logger_name, loglevel::value level, Formatter::ptr &formatter, std::vector<LogSink::ptr> &sinks) : _logger_name(logger_name), _limit_level(level), _formatter(formatter), _sliks(sinks.begin(), sinks.end()) {
}
// 操作方法
//获取日志器名称
const std::string& name(){
return _logger_name;
}
// 构造日志消息对象并进行格式化,得到格式化后的日志消息字符串--然后进行落地输出,5个等级
void debug(const std::string &file, size_t line, const std::string &fmt, ...)
{
// 日志的输出操作
// 1.判断当前的日志是否达到输出等级
if (loglevel::value::DEBUG < _limit_level)
{
return;
} // 没有达到输出等级
// 2.对fmt格式化字符串和不定参进行字符串组织,得到日志消息的字符串
va_list ap;
va_start(ap, fmt);
char *res;
int ret = vasprintf(&res, fmt.c_str(), ap);
if (ret == -1)
{
std::cout << "vasprintf failed!!\n";
return;
}
va_end(ap); // 将ap指针置空
// 代码一样,可以封装成一个函数
serialize(loglevel::value::DEBUG, file, line, res);
free(res); // 将指针释放掉
}
void info(const std::string &file, size_t line, const std::string &fmt, ...)
{
// 日志的输出操作
// 1.判断当前的日志是否达到输出等级
if (loglevel::value::INFO < _limit_level)
{
return;
} // 没有达到输出等级
// 2.对fmt格式化字符串和不定参进行字符串组织,得到日志消息的字符串
va_list ap;
va_start(ap, fmt);
char *res;
int ret = vasprintf(&res, fmt.c_str(), ap);
if (ret == -1)
{
std::cout << "vasprintf failed!!\n";
return;
}
va_end(ap); // 将ap指针置空
// 代码一样,可以封装成一个函数
serialize(loglevel::value::INFO, file, line, res);
free(res); // 将指针释放掉
}
void warn(const std::string &file, size_t line, const std::string &fmt, ...)
{
// 日志的输出操作
// 1.判断当前的日志是否达到输出等级
if (loglevel::value::WARN < _limit_level)
{
return;
} // 没有达到输出等级
// 2.对fmt格式化字符串和不定参进行字符串组织,得到日志消息的字符串
va_list ap;
va_start(ap, fmt);
char *res;
int ret = vasprintf(&res, fmt.c_str(), ap);
if (ret == -1)
{
std::cout << "vasprintf failed!!\n";
return;
}
va_end(ap); // 将ap指针置空
// 代码一样,可以封装成一个函数
serialize(loglevel::value::WARN, file, line, res);
free(res); // 将指针释放掉
}
void error(const std::string &file, size_t line, const std::string &fmt, ...)
{
// 日志的输出操作
// 1.判断当前的日志是否达到输出等级
if (loglevel::value::ERROR < _limit_level)
{
return;
} // 没有达到输出等级
// 2.对fmt格式化字符串和不定参进行字符串组织,得到日志消息的字符串
va_list ap;
va_start(ap, fmt);
char *res;
int ret = vasprintf(&res, fmt.c_str(), ap);
if (ret == -1)
{
std::cout << "vasprintf failed!!\n";
return;
}
va_end(ap); // 将ap指针置空
// 代码一样,可以封装成一个函数
serialize(loglevel::value::ERROR, file, line, res);
free(res); // 将指针释放掉
}
void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
{
// 日志的输出操作
// 1.判断当前的日志是否达到输出等级
if (loglevel::value::FATAL < _limit_level)
{
return;
} // 没有达到输出等级
// 2.对fmt格式化字符串和不定参进行字符串组织,得到日志消息的字符串
va_list ap;
va_start(ap, fmt);
char *res;
int ret = vasprintf(&res, fmt.c_str(), ap);
if (ret == -1)
{
std::cout << "vasprintf failed!!\n"