專欄導(dǎo)讀
??作者簡(jiǎn)介:花想云 ,在讀本科生一枚,C/C++領(lǐng)域新星創(chuàng)作者,新星計(jì)劃導(dǎo)師,阿里云專家博主,CSDN內(nèi)容合伙人…致力于 C/C++、Linux 學(xué)習(xí)。
??專欄簡(jiǎn)介:本文收錄于 C++項(xiàng)目——基于多設(shè)計(jì)模式下的同步與異步日志系統(tǒng)
??相關(guān)專欄推薦:C語(yǔ)言初階系列、C語(yǔ)言進(jìn)階系列 、C++系列、數(shù)據(jù)結(jié)構(gòu)與算法、Linux
日志器主要是用來(lái)與前端交互,當(dāng)我們需要使用日志系統(tǒng)打印日志消息時(shí),只需要?jiǎng)?chuàng)建Logger
對(duì)象,調(diào)用該對(duì)象的debug
、info
、warn
、error
、fatal
等方法輸出自己想要打印的日志消息即可。支持解析可變參數(shù)列表和輸出格式,就可以做到像printf
函數(shù)一樣打印日志。
因?yàn)槿罩酒髂K是對(duì)前邊所有模塊的一個(gè)整合,所以Logger
類管理的成員有:
-
日志器名稱
(日志器的唯一標(biāo)識(shí)); -
格式化模塊對(duì)象
(Formatter); -
落地模塊對(duì)象數(shù)組
(一個(gè)日志器可能會(huì)向多個(gè)位置進(jìn)行日志輸出); -
默認(rèn)的輸出限制等級(jí)
(控制達(dá)到指定等級(jí)的日志才可以輸出); -
互斥鎖
(保證日志輸出過(guò)程是線程安全的,不會(huì)出現(xiàn)交叉日志);
Logger
類提供的操作有:
-
debug
等級(jí)日志的輸出操作; -
info
等級(jí)日志的輸出操作; -
warn
等級(jí)日志的輸出操作; -
error
等級(jí)日志的輸出操作; -
fatal
等級(jí)日志的輸出操作;
當(dāng)前日志系統(tǒng)支持同步日志和異步日志兩種方式,兩個(gè)不同的日志器唯一的區(qū)別是它們?cè)谌罩韭涞胤绞缴嫌兴煌?/p>
-
同步日志器
:直接對(duì)日志消息進(jìn)行輸出; -
異步日志器
:將日志消息放入緩沖區(qū),由異步線程
進(jìn)行輸出。
因此日志器在設(shè)計(jì)的時(shí)候先設(shè)計(jì)一個(gè)Logger基類
,在Logger基類
的基礎(chǔ)上繼承出SyncLogger同步日志器
和AsyncLogger異步日志器
。
Logger類設(shè)計(jì)
-
debug
、info
等接口在設(shè)計(jì)時(shí),需要傳遞參數(shù)有文件名、行號(hào)、參數(shù)包。至于為什么要傳遞文件名與行號(hào),因?yàn)橐苊猥@取文件名和行號(hào)時(shí)是在本函數(shù)內(nèi)部; - 將參數(shù)包進(jìn)行內(nèi)容提取后保存在字符串中,交由
serialize
進(jìn)行處理; -
serialize
函數(shù)的功能是,將字符串中的內(nèi)容進(jìn)行日志消息格式化
,并進(jìn)行落地操作
;
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),
_sinks(sinks.begin(), sinks.end())
{}
// 獲取日志器名稱
const std::string& name(){ return _logger_name; }
void debug(const std::string &file, size_t line, const std::string &fmt, ...)
{
// 通過(guò)傳入的參數(shù)構(gòu)造出一個(gè)日志消息對(duì)象, 進(jìn)行日志格式化,最終落地
// 判斷當(dāng)前的日志是否達(dá)到了輸出等級(jí)
if (LogLevel::value::DEBUG < _limit_level)
{
return;
}
// 對(duì)fmt格式化字符串和不定參數(shù)進(jìn)行字符串組織, 得到的日志消息字符串
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);
serialize(LogLevel::value::DEBUG, file, line, res);
free(res);
}
void info(const std::string &file, size_t line, const std::string &fmt, ...)
{
// 通過(guò)傳入的參數(shù)構(gòu)造出一個(gè)日志消息對(duì)象, 進(jìn)行日志格式化,最終落地
if (LogLevel::value::INFO < _limit_level)
{
return;
}
// 對(duì)fmt格式化字符串和不定參數(shù)進(jìn)行字符串組織, 得到的日志消息字符串
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);
serialize(LogLevel::value::INFO, file, line, res);
free(res);
}
void warn(const std::string &file, size_t line, const std::string &fmt, ...)
{
// 通過(guò)傳入的參數(shù)構(gòu)造出一個(gè)日志消息對(duì)象, 進(jìn)行日志格式化,最終落地
if (LogLevel::value::WARN < _limit_level)
{
return;
}
// 對(duì)fmt格式化字符串和不定參數(shù)進(jìn)行字符串組織, 得到的日志消息字符串
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);
serialize(LogLevel::value::WARN, file, line, res);
free(res);
}
void error(const std::string &file, size_t line, const std::string &fmt, ...)
{
// 通過(guò)傳入的參數(shù)構(gòu)造出一個(gè)日志消息對(duì)象, 進(jìn)行日志格式化,最終落地
if (LogLevel::value::ERROR < _limit_level)
{
return;
}
// 對(duì)fmt格式化字符串和不定參數(shù)進(jìn)行字符串組織, 得到的日志消息字符串
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);
serialize(LogLevel::value::ERROR, file, line, res);
free(res);
}
void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
{
// 通過(guò)傳入的參數(shù)構(gòu)造出一個(gè)日志消息對(duì)象, 進(jìn)行日志格式化,最終落地
if (LogLevel::value::FATAL < _limit_level)
{
return;
}
// 對(duì)fmt格式化字符串和不定參數(shù)進(jìn)行字符串組織, 得到的日志消息字符串
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);
serialize(LogLevel::value::FATAL, file, line, res);
free(res);
}
protected:
void serialize(LogLevel::value level, const std::string &file, size_t line, char *str)
{
// 構(gòu)造LogMsg對(duì)象
LogMsg msg(level, line, file, _logger_name, str);
// 通過(guò)格式化工具對(duì)LogMsg進(jìn)行格式化, 得到格式化后的日志字符串
std::stringstream ss;
_formatter->format(ss, msg);
// 對(duì)日志進(jìn)行落地
log(ss.str().c_str(), ss.str().size());
}
virtual void log(const char *data, size_t len) = 0;
protected:
std::mutex _mutex;
std::string _logger_name; // 日志器名稱
std::atomic<LogLevel::value> _limit_level; // 限制輸出等級(jí)
Formatter::ptr _formatter;
std::vector<LogSink::ptr> _sinks; // 落地方向數(shù)組
};
同步日志器類設(shè)計(jì)
同步日志器設(shè)計(jì)較為簡(jiǎn)單,設(shè)計(jì)思想是:
- 遍歷日志落地?cái)?shù)組,以數(shù)組中的各種落地方式進(jìn)行落地操作;
class SyncLogger : public Logger
{
public:
SyncLogger(const std::string &logger_name,
LogLevel::value level,
LOG::Formatter::ptr &formatter,
std::vector<LogSink::ptr> &sinks)
: Logger(logger_name, level, formatter, sinks)
{
}
protected:
void log(const char *data, size_t len)
{
std::unique_lock<std::mutex> lock(_mutex);
if (_sinks.empty())
return;
for (auto &sink : _sinks)
{
sink->log(data, len);
}
}
};
同步日志器測(cè)試
int main()
{
LOG::LogMsg msg(LOG::LogLevel::value::INFO, 53, "main.cc", "root", "格式化功能測(cè)試...");
LOG::Formatter fmt;
std::string str = fmt.format(msg);
LOG::LogSink::ptr time_lsp = LOG::SinkFactory::create<RollByTimeSink>("./logfile/roll-", TimeGap::GAP_SECOND);
time_t old = LOG::util::Date::getTime();
while(LOG::util::Date::getTime() < old + 5)
{
time_lsp->log(str.c_str(), str.size());
sleep(1);
}
std::string logger_name = "sync_logger";
LOG::LogLevel::value limit = LOG::LogLevel::value::WARN;
LOG::Formatter::ptr fmt(new LOG::Formatter("[%d{%H:%M:%S}][%c][%f:%l][%p]%T%m%n"));
LOG::LogSink::ptr stdout_lsp = LOG::SinkFactory::create<LOG::StdOutSink>();
LOG::LogSink::ptr file_lsp = LOG::SinkFactory::create<LOG::FileSink>("./logfile/test.log");
LOG::LogSink::ptr roll_lsp = LOG::SinkFactory::create<LOG::RollBySizeSink>("./logfile/test.log", 1024*1024);
std::vector<LOG::LogSink> sinks = {stdout_lsp, file_lsp, roll_lsp};
LOG::Logger::ptr logger(new LOG::SyncLogger(logger_name, limit, fmt, sinks));
logger->debug(__FILE__, __LINE__, "%s", "測(cè)試日志");
logger->info(__FILE__, __LINE__, "%s", "測(cè)試日志");
logger->warn(__FILE__, __LINE__, "%s", "測(cè)試日志");
logger->error(__FILE__, __LINE__, "%s", "測(cè)試日志");
logger->fatal(__FILE__, __LINE__, "%s", "測(cè)試日志");
size_t cursize = 0, count = 0;
while(cursize < 1024*1024*10)
{
logger->fatal(__FILE__, __LINE__, "測(cè)試日志-%d", count++);
cursize+=20;
}
return 0;
}
日志器建造者模式設(shè)計(jì)
觀察上一小節(jié)中的日志器測(cè)試代碼,在構(gòu)建一個(gè)同步日志器時(shí),需要先設(shè)置很多的零部件
。這對(duì)于用戶來(lái)說(shuō)未免有些繁瑣
。
我們需要使用建造者模式
來(lái)建造日志器,而不要讓用戶直接去構(gòu)造日志器,以簡(jiǎn)化用戶的使用復(fù)雜度
。
設(shè)計(jì)思想:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-709648.html
-
抽象一個(gè)日志器建造者類
:- 設(shè)置日志器類型;
- 將不同類型(同步&異步)日志器的創(chuàng)建放到同一個(gè)日志器建造者類中完成。
-
派生出具體的建造者類
----局部日志器建造者 & 全局日志器建造者類(后面添加了全局單例管理器之后,將日志器添加全局管理)。
抽象日志器建造者類
- 建造者類中包含成員:
-
logger_type
日志器類型; -
logger_name
日志器名稱; -
limit_level
日志輸出限制等級(jí); -
formatter
格式化對(duì)象; -
sinks
日志落地?cái)?shù)組;
-
- 還有構(gòu)建各個(gè)零件的函數(shù);
enum class LoggerType
{
LOGGER_SYNC,
LOGGER_ASYNC
};
// 1.抽象一個(gè)日志器建造者類(完成日志器所需零部件的構(gòu)建 & 日志器的構(gòu)建)
class LoggerBuilder
{
public:
LoggerBuilder() : _logger_type(LoggerType::LOGGER_SYNC),
_limit_level(LogLevel::value::DEBUG)
{}
void buildLoggerType(LoggerType type) { _logger_type = type; }
void buildLoggerName(const std::string &name) { _logger_name = name; }
void buildLoggerLevel(LogLevel::value level) { _limit_level = level; }
void buildFormatter(const std::string &pattern)
{
_formatter = std::make_shared<Formatter>(pattern);
}
template <typename SinkType, typename... Args>
void buildSink(Args &&...args)
{
LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);
_sinks.push_back(psink);
}
virtual Logger::ptr build() = 0;
protected:
LoggerType _logger_type;
std::string _logger_name;
std::atomic<LogLevel::value> _limit_level;
Formatter::ptr _formatter;
std::vector<LogSink::ptr> _sinks;
};
派生局部日志器建造者
/*2.派生出具體的建造者類---局部日志器的建造者 & 全局日志器的建造者*/
class LocalLoggerBuilder : public LoggerBuilder
{
public:
Logger::ptr build() override
{
assert(_logger_name.empty() == false);
if (_formatter.get() == nullptr)
{
_formatter = std::make_shared<Formatter>();
}
if (_sinks.empty())
{
buildSink<StdOutSink>();
}
if (_logger_type == LoggerType::LOGGER_ASYNC)
{
// 后面實(shí)現(xiàn)異步日志器后再完善...
}
return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
}
};
日志器建造者類測(cè)試
int main()
{
std::unique_ptr<LOG::LoggerBuilder> builder(new LOG::GlobalLoggerBuilder());
builder->buildLoggerName("sync_logger");
builder->buildLoggerLevel(LOG::LogLevel::value::WARN);
builder->buildFormatter("[%c][%f:%l]%m%n");
builder->buildLoggerType(LOG::LoggerType::LOGGER_SYNC);
builder->buildEnableUnSafeAsync();
builder->buildSink<LOG::FileSink>("./logfile/async.log");
builder->buildSink<LOG::StdOutSink>();
LOG::Logger::ptr = builder->build();
logger->debug(__FILE__, __LINE__, "%s", "測(cè)試日志");
logger->info(__FILE__, __LINE__, "%s", "測(cè)試日志");
logger->warn(__FILE__, __LINE__, "%s", "測(cè)試日志");
logger->error(__FILE__, __LINE__, "%s", "測(cè)試日志");
logger->fatal(__FILE__, __LINE__, "%s", "測(cè)試日志");
size_t cursize = 0, count = 0;
while(cursize < 1024*1024*10)
{
logger->fatal(__FILE__, __LINE__, "測(cè)試日志-%d", count++);
cursize+=20;
}
return 0;
}
同步日志器類與日志器建造者類整理
#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__
#include "util.hpp"
#include "level.hpp"
#include "format.hpp"
#include "sink.hpp"
#include "looper.hpp"
#include <cstdarg>
#include <atomic>
#include <thread>
#include <mutex>
#include <unordered_map>
namespace LOG
{
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),
_sinks(sinks.begin(), sinks.end())
{
}
const std::string& name(){ return _logger_name; }
void debug(const std::string &file, size_t line, const std::string &fmt, ...)
{
// 通過(guò)傳入的參數(shù)構(gòu)造出一個(gè)日志消息對(duì)象, 進(jìn)行日志格式化,最終落地
// 判斷當(dāng)前的日志是否達(dá)到了輸出等級(jí)
if (LogLevel::value::DEBUG < _limit_level)
{
return;
}
// 對(duì)fmt格式化字符串和不定參數(shù)進(jìn)行字符串組織, 得到的日志消息字符串
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);
serialize(LogLevel::value::DEBUG, file, line, res);
free(res);
}
void info(const std::string &file, size_t line, const std::string &fmt, ...)
{
// 通過(guò)傳入的參數(shù)構(gòu)造出一個(gè)日志消息對(duì)象, 進(jìn)行日志格式化,最終落地
if (LogLevel::value::INFO < _limit_level)
{
return;
}
// 對(duì)fmt格式化字符串和不定參數(shù)進(jìn)行字符串組織, 得到的日志消息字符串
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);
serialize(LogLevel::value::INFO, file, line, res);
free(res);
}
void warn(const std::string &file, size_t line, const std::string &fmt, ...)
{
// 通過(guò)傳入的參數(shù)構(gòu)造出一個(gè)日志消息對(duì)象, 進(jìn)行日志格式化,最終落地
if (LogLevel::value::WARN < _limit_level)
{
return;
}
// 對(duì)fmt格式化字符串和不定參數(shù)進(jìn)行字符串組織, 得到的日志消息字符串
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);
serialize(LogLevel::value::WARN, file, line, res);
free(res);
}
void error(const std::string &file, size_t line, const std::string &fmt, ...)
{
// 通過(guò)傳入的參數(shù)構(gòu)造出一個(gè)日志消息對(duì)象, 進(jìn)行日志格式化,最終落地
if (LogLevel::value::ERROR < _limit_level)
{
return;
}
// 對(duì)fmt格式化字符串和不定參數(shù)進(jìn)行字符串組織, 得到的日志消息字符串
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);
serialize(LogLevel::value::ERROR, file, line, res);
free(res);
}
void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
{
// 通過(guò)傳入的參數(shù)構(gòu)造出一個(gè)日志消息對(duì)象, 進(jìn)行日志格式化,最終落地
if (LogLevel::value::FATAL < _limit_level)
{
return;
}
// 對(duì)fmt格式化字符串和不定參數(shù)進(jìn)行字符串組織, 得到的日志消息字符串
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);
serialize(LogLevel::value::FATAL, file, line, res);
free(res);
}
protected:
void serialize(LogLevel::value level, const std::string &file, size_t line, char *str)
{
// 構(gòu)造LogMsg對(duì)象
LogMsg msg(level, line, file, _logger_name, str);
// 通過(guò)格式化工具對(duì)LogMsg進(jìn)行格式化, 得到格式化后的日志字符串
std::stringstream ss;
_formatter->format(ss, msg);
// 對(duì)日志進(jìn)行落地
log(ss.str().c_str(), ss.str().size());
}
virtual void log(const char *data, size_t len) = 0;
protected:
std::mutex _mutex;
std::string _logger_name; // 日志器名稱
std::atomic<LogLevel::value> _limit_level; // 限制輸出等級(jí)
Formatter::ptr _formatter;
std::vector<LogSink::ptr> _sinks;
};
class SyncLogger : public Logger
{
public:
SyncLogger(const std::string &logger_name,
LogLevel::value level,
LOG::Formatter::ptr &formatter,
std::vector<LogSink::ptr> &sinks)
: Logger(logger_name, level, formatter, sinks)
{
}
protected:
void log(const char *data, size_t len)
{
std::unique_lock<std::mutex> lock(_mutex);
if (_sinks.empty())
return;
for (auto &sink : _sinks)
{
sink->log(data, len);
}
}
};
// 1.抽象一個(gè)日志器建造者類(完成日志器所需零部件的構(gòu)建 & 日志器的構(gòu)建)
// 1.設(shè)置日志器類型
// 2.將不同類型的日志器的創(chuàng)建放到同一個(gè)日志器建造者類中完成
enum class LoggerType
{
LOGGER_SYNC,
LOGGER_ASYNC
};
class LoggerBuilder
{
public:
LoggerBuilder() : _logger_type(LoggerType::LOGGER_SYNC),
_limit_level(LogLevel::value::DEBUG)
{}
void buildLoggerType(LoggerType type) { _logger_type = type; }
void buildEnableUnSafeAsync() { _looper_type = AsyncType::ASYNC_UNSAFE; }
void buildLoggerName(const std::string &name) { _logger_name = name; }
void buildLoggerLevel(LogLevel::value level) { _limit_level = level; }
void buildFormatter(const std::string &pattern)
{
_formatter = std::make_shared<Formatter>(pattern);
}
template <typename SinkType, typename... Args>
void buildSink(Args &&...args)
{
LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);
_sinks.push_back(psink);
}
virtual Logger::ptr build() = 0;
protected:
LoggerType _logger_type;
std::string _logger_name;
std::atomic<LogLevel::value> _limit_level;
Formatter::ptr _formatter;
std::vector<LogSink::ptr> _sinks;
};
/*2.派生出具體的建造者類---局部日志器的建造者 & 全局日志器的建造者*/
class LocalLoggerBuilder : public LoggerBuilder
{
public:
Logger::ptr build() override
{
assert(_logger_name.empty() == false);
if (_formatter.get() == nullptr)
{
_formatter = std::make_shared<Formatter>();
}
if (_sinks.empty())
{
buildSink<StdOutSink>();
}
if (_logger_type == LoggerType::LOGGER_ASYNC)
{}
return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
}
};
}
#endif
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-709648.html
到了這里,關(guān)于C++項(xiàng)目實(shí)戰(zhàn)——基于多設(shè)計(jì)模式下的同步&異步日志系統(tǒng)-⑨-同步日志器類與日志器建造者類設(shè)計(jì)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!