文章目录
该模块是从sylar服务器框架中学习的,以下将会对其进行总结以加深对该框架的理解;
索引
【C++模块实现】| 【01】日志系统实现
【C++模块实现】| 【02】日志系统优化
【C++模块实现】| 【03】文件管理模块
【C++模块实现】| 【04】配置模块
【C++模块实现】| 【05】日志模块增加配置模块的功能
【C++模块实现】| 【06】日志模块添加循环覆盖写文件功能
【C++模块实现】| 【07】对于互斥、自旋锁、条件变量、信号量简介及封装
【C++模块实现】| 【08】循环覆盖写内存缓冲区(日志中多线程记录)
【C++模块实现】| 【09】线程模块及线程池的实现
【C++模块实现】| 【10】定时器的实现
【C++模块实现】| 【11】封装Ipv4、Ipv6、unix网络地址
1、日志级别LogLevel
class LogLevel{
public:
enum Level {
/// 未知级别
UNKNOW = 0,
/// DEBUG: 记录有关通过系统的流量的详细信息,应用程序记录的大多数为 DEBUG;
DEBUG = 1,
/// INFO:运行时事件(启动/关闭);
INFO = 2,
/// WARN:使用已弃用的 API、API 使用不当、“几乎”错误、其他不合需要或意外但不一定“错误”的运行时情况;
WARN = 3,
/// ERROR:其他运行时错误或意外情况
ERROR = 4,
/// FATAL:导致提前终止的严重错误
FATAL = 5
};
/** Level转字符串 */
static const char* ToString(LogLevel::Level level);
/** 字符串转Level */
static LogLevel::Level FromString(const std::string& str);
};
LogLevel类中还提供了两个静态函数,便于日志级别和字符串之间的相互转换;
1.1 实现
const char* LogLevel::ToString(LogLevel::Level level) {
switch(level) {
#define XX(name) \
case LogLevel::name: \
return #name;\
break;
XX(DEBUG);
XX(INFO);
XX(WARN);
XX(ERROR);
XX(FATAL);
#undef XX
default:
return "UNKNOW";
}
return "UNKNOW";
}
LogLevel::Level LogLevel::FromString(const std::string& str) {
#define XX(level, v) \
if(str == #v) { \
return LogLevel::level; \
}
XX(DEBUG, debug);
XX(INFO, info);
XX(WARN, warn);
XX(ERROR, error);
XX(FATAL, fatal);
XX(DEBUG, DEBUG);
XX(INFO, INFO);
XX(WARN, WARN);
XX(ERROR, ERROR);
XX(FATAL, FATAL);
return LogLevel::UNKNOW;
#undef XX
}
2、日志事件LogEvent
class LogEvent{
public:
void format(const char* fmt, ...);
void format(const char* fmt, va_list al);
private:
/// 文件名
const char* m_file = nullptr;
/// 行号
int32_t m_line = 0;
/// 程序启动开始到现在的毫秒数
uint32_t m_elapse = 0;
/// 线程ID
uint32_t m_threadId = 0;
/// 协程ID
uint32_t m_fiberId = 0;
/// 时间戳
uint64_t m_time = 0;
/// 线程名称
std::string m_threadName;
/// 日志内容流
std::stringstream m_ss;
/// 日志器
std::shared_ptr<Logger> m_logger;
/// 日志等级
LogLevel::Level m_level;
}
获取和设置日志事件(文件名、行号、启动时间、线程、协程、时间戳、线程名称、日志内容、日志器、日志等级)
2.1 实现
LogEvent::LogEvent(std::shared_ptr<Logger> logger, LogLevel::Level level
,const char* file, int32_t line, uint32_t elapse
,uint32_t thread_id, uint32_t fiber_id, uint64_t time
,const std::string& thread_name)
:m_file(file)
,m_line(line)
,m_elapse(elapse)
,m_threadId(thread_id)
,m_fiberId(fiber_id)
,m_time(time)
,m_threadName(thread_name)
,m_logger(logger)
,m_level(level) {
}
void LogEvent::format(const char* fmt, ...) {
//va_start、va_end成对出现
va_list al; //val_list 用于获取不确定个数的参数
va_start(al, fmt);
format(fmt, al);
va_end(al);
}
/** 将内容都添加到m_ss中 */
void LogEvent::format(const char* fmt, va_list al) {
char* buf = nullptr;
int len = vasprintf(&buf, fmt, al);
if(len != -1) {
m_ss << std::string(buf, len);
free(buf);
}
}
3、日志格式化
class LogFormatter {
public:
/**
* @brief 构造函数
* @param[in] pattern 格式模板
* @details
* %m 消息
* %p 日志级别
* %r 累计毫秒数
* %c 日志名称
* %t 线程id
* %n 换行
* %d 时间
* %f 文件名
* %l 行号
* %T 制表符
* %F 协程id
* %N 线程名称
*
* 默认格式 "%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"
*/
LogFormatter(const std::string& pattern);
/**
* 返回格式化日志文本
* @param[in] logger 日志器
* @param[in] level 日志级别
* @param[in] event 日志事件
*/
std::string format(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event);
std::ostream& format(std::ostream& ofs, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event);
public:
/**
* 日志内容项格式化
*/
class FormatItem {
public:
typedef std::shared_ptr<FormatItem> ptr;
virtual ~FormatItem() {}
/**
* @brief 格式化日志到流
* @param[in, out] os 日志输出流
* @param[in] logger 日志器
* @param[in] level 日志等级
* @param[in] event 日志事件
*/
//纯虚函数
virtual void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) = 0;
};
/**
* 初始化,解析日志模板, 做m_pattern的解析
*/
void init();
private:
/// 日志格式模板
std::string m_pattern;
/// 日志格式解析后格式
std::vector<FormatItem::ptr> m_items;
/// 是否有错误
bool m_error = false;
};
该类主要对输入格式的解析并以该格式来输出日志;
【内部属性】:
- m_parttern:日志格式;
- m_iterms:存储以FormatItem为基类的类,即后续继承该类的子类都可存入该vector;
- m_error:用于在init中解析pattern中是否有错误;
【FormatItem类】:该类为虚基类,采用的设计模式为策略模式,可根据自己的需求继承该类从而增加日志的输出格式;
========》策略模式的使用《========
========》纯虚函数《========
3.1 实现
/** [%m] 消息*/
class MessageFormatItem : public LogFormatter::FormatItem {
public:
MessageFormatItem(const std::string& str = "") {}
void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
os << event->getContent();
}
};
/** [%p] 日志级别 */
class LevelFormatItem : public LogFormatter::FormatItem {
public:
LevelFormatItem(const std::string& str = "") {}
void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
os << LogLevel::ToString(level);
}
};
/** [%r] 累计毫秒数 */
class ElapseFormatItem : public LogFormatter::FormatItem {
public:
ElapseFormatItem(const std::string& str = "") {}
void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
os << event->getElapse();
}
};
/** [%c] 日志名称 */
class NameFormatItem : public LogFormatter::FormatItem {
public:
NameFormatItem(const std::string& str = "") {}
void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
os << event->getLogger()->getName();
}
};
/** [%t] 线程id */
class ThreadIdFormatItem : public LogFormatter::FormatItem {
public:
ThreadIdFormatItem(const std::string& str = "") {}
void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
os << event->getThreadId();
}
};
/** [%F] 协程id */
class FiberIdFormatItem : public LogFormatter::FormatItem {
public:
FiberIdFormatItem(const std::string& str = "") {}
void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
os << event->getFiberId();
}
};
/** [%N] 线程名称 */
class ThreadNameFormatItem : public LogFormatter::FormatItem {
public:
ThreadNameFormatItem(const std::string& str = "") {}
void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
os << event->getThreadName();
}
};
/** [%d] 时间 */
class DateTimeFormatItem : public LogFormatter::FormatItem {
public:
DateTimeFormatItem(const std::string& format = "%Y-%m-%d %H:%M:%S")
:m_format(format) {
if(m_format.empty()) {
m_format = "%Y-%m-%d %H:%M:%S";
}
}
void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
struct tm tm;
time_t time = event->getTime();
//localtime_r 用于linux平台下获取系统时间
localtime_r(&time, &tm);
char buf[64];
//strftime 根据format中定义的格式化规则,格式化结构tm表示的时间,并把它存储在buf中。
strftime(buf, sizeof(buf), m_format.c_str(), &tm);
os << buf;
}
private:
std::string m_format;
};
/** [%f] 文件名 */
class FilenameFormatItem : public LogFormatter::FormatItem {
public:
FilenameFormatItem(const std::string& str = "") {}
void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
os << event->getFile();
}
};
/** [%l] 行号 */
class LineFormatItem : public LogFormatter::FormatItem {
public:
LineFormatItem(const std::string& str = "") {}
void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
os << event->getLine();
}
};
/** [%n] 换行 */
class NewLineFormatItem : public LogFormatter::FormatItem {
public:
NewLineFormatItem(const std::string& str = "") {}
void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
os << std::endl;
}
};
class StringFormatItem : public LogFormatter::FormatItem {
public:
StringFormatItem(const std::string& str)
:m_string(str) {}
void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
os << m_string;
}
private:
std::string m_string;
};
/** [%T] 制表符 */
class TabFormatItem : public LogFormatter::FormatItem {
public:
TabFormatItem(const std::string& str = "") {}
void format(std::ostream& os, Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override {
os << "\t";
}
};
/** 构造函数,先初始化解析parttern */
LogFormatter::LogFormatter(const std::string& pattern)
:m_pattern(pattern) {
init();
}
/** 执行每个m_iterms内的子类中的format,让其内容都添加到ss中 */
std::string LogFormatter::format(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) {
std::stringstream ss;
for (auto& i : m_items) {
i->format(ss, logger, level, event);
}
return ss.str();
}
/** 与上一个函数功能类似,只是输出到ostream */
std::ostream& LogFormatter::format(std::ostream& ofs, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) {
for (auto& i : m_items) {
i->format(ofs, logger, level, event);
}
return ofs;
}
// %xxx %xxx{xxx} %%
// "%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"
// 解析日志格式
void LogFormatter::init() {
//str, format, type
std::vector<std::tuple<std::string, std::string, int> > vec;
std::string nstr;
for (size_t i = 0; i < m_pattern.size(); ++i) {
if (m_pattern[i] != '%') {
nstr.append(1, m_pattern[i]);
continue;
}
if ((i + 1) < m_pattern.size()) {
if (m_pattern[i + 1] == '%') {
nstr.append(1, '%');
continue;
}
}
size_t n = i + 1;
int fmt_status = 0;
size_t fmt_begin = 0;
std::string str;
std::string fmt;
while (n < m_pattern.size()) {
if (!fmt_status && (!isalpha(m_pattern[n]) && m_pattern[n] != '{'
&& m_pattern[n] != '}')) {
str = m_pattern.substr(i + 1, n - i - 1);
break;
}
if (fmt_status == 0) {
if (m_pattern[n] == '{') {
str = m_pattern.substr(i + 1, n - i - 1);
fmt_status = 1; //解析格式
fmt_begin = n;
++n;
continue;
}
}
else if (fmt_status == 1) {
if (m_pattern[n] == '}') {
fmt = m_pattern.substr(fmt_begin + 1, n - fmt_begin - 1);
fmt_status = 0;
++n;
break;
}
}
++n;
if (n == m_pattern.size()) {
if (str.empty()) {
str = m_pattern.substr(i + 1);
}
}
}
if (fmt_status == 0) {
if (!nstr.empty()) {
vec.push_back(std::make_tuple(nstr, std::string(), 0));
nstr.clear();
}
vec.push_back(std::make_tuple(str, fmt, 1));
i = n - 1;
}
else if (fmt_status == 1) {
std::cout << "pattern parse error: " << m_pattern << " - " << m_pattern.substr(i) << std::endl;
m_error = true;
vec.push_back(std::make_tuple("<<pattern_error>>", fmt, 0));
}
}
if (!nstr.empty()) {
vec.push_back(std::make_tuple(nstr, "", 0));
}
static std::map<std::string, std::function<FormatItem::ptr(const std::string& str)> > s_format_items = {
#define XX(str, C) \
{#str, [](const std::string& fmt) { return FormatItem::ptr(new C(fmt));}}
XX(m, MessageFormatItem), //m:消息
XX(p, LevelFormatItem), //p:日志级别
XX(r, ElapseFormatItem), //r:累计毫秒数
XX(c, NameFormatItem), //c:日志名称
XX(t, ThreadIdFormatItem), //t:线程id
XX(n, NewLineFormatItem), //n:换行
XX(d, DateTimeFormatItem), //d:时间
XX(f, FilenameFormatItem), //f:文件名
XX(l, LineFormatItem), //l:行号
XX(T, TabFormatItem), //T:Tab
XX(F, FiberIdFormatItem), //F:协程id
XX(N, ThreadNameFormatItem), //N:线程名称
#undef XX
};
for (auto& i : vec) {
if (std::get<2>(i) == 0) {
m_items.push_back(FormatItem::ptr(new StringFormatItem(std::get<0>(i))));
}
else {
auto it = s_format_items.find(std::get<0>(i));
if (it == s_format_items.end()) {
m_items.push_back(FormatItem::ptr(new StringFormatItem("<<error_format %" + std::get<0>(i) + ">>")));
m_error = true;
}
else {
m_items.push_back(it->second(std::get<1>(i)));
}
}
}
}
4、日志输出地
class LogAppender {
friend class Logger;
public:
virtual ~LogAppender() {}
/**
* @brief 写入日志
* @param[in] logger 日志器
* @param[in] level 日志级别
* @param[in] event 日志事件
*/
virtual void log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) = 0;
/**
* @brief 将日志输出目标的配置转成YAML String
*/
virtual std::string toYamlString() = 0;
void setFormatter(LogFormatter::ptr val);
LogFormatter::ptr getFormatter();
protected:
/// 日志级别
LogLevel::Level m_level = LogLevel::DEBUG;
/// 是否有自己的日志格式器
bool m_hasFormatter = false;
/// Mutex
MutexType m_mutex;
/// 日志格式器
LogFormatter::ptr m_formatter;
}
/**
* 输出到控制台的Appender
*/
class StdoutLogAppender : public LogAppender {
public:
typedef std::shared_ptr<StdoutLogAppender> ptr;
void log(Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override;
std::string toYamlString() override;
};
/**
* 输出到文件的Appender
*/
class FileLogAppender : public LogAppender {
public:
typedef std::shared_ptr<FileLogAppender> ptr;
FileLogAppender(const std::string& filename);
void log(Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event) override;
std::string toYamlString() override;
/**
* @brief 重新打开日志文件
* @return 成功返回true
*/
bool reopen();
private:
/// 文件路径
std::string m_filename;
/// 文件流
std::ofstream m_filestream;
/// 上次重新打开时间
uint64_t m_lastTime = 0;
};
该类为基类,指定日志输出地;可将日志输出到控制台、文件等;
m_mutex:为了确保在多线程中的线程安全;
========》线程、线程同步、线程安全《========
========》Linux | Linux中的线程、互斥量、信号量的基本使用《========
4.1 实现
/** 设置formatter */
void LogAppender::setFormatter(LogFormatter::ptr val) {
MutexType::Lock lock(m_mutex);
m_formatter = val;
if(m_formatter) {
m_hasFormatter = true;
} else {
m_hasFormatter = false;
}
}
/** 获取formatter */
LogFormatter::ptr LogAppender::getFormatter() {
MutexType::Lock lock(m_mutex);
return m_formatter;
}
FileLogAppender::FileLogAppender(const std::string& filename)
:m_filename(filename) {
reopen(); // 打开文件
}
void FileLogAppender::log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) {
/* 日志级别是否满足 */
if(level >= m_level) {
uint64_t now = event->getTime();
if(now >= (m_lastTime + 3)) { // 若打开时间太长,则重新打开
reopen();
m_lastTime = now;
}
MutexType::Lock lock(m_mutex);
if(!m_formatter->format(m_filestream, logger, level, event)) {
std::cout << "error" << std::endl;
}
}
}
/** 将yaml格式转换成string */
std::string FileLogAppender::toYamlString() {
MutexType::Lock lock(m_mutex);
YAML::Node node;
node["type"] = "FileLogAppender";
node["file"] = m_filename;
if(m_level != LogLevel::UNKNOW) {
node["level"] = LogLevel::ToString(m_level);
}
if(m_hasFormatter && m_formatter) {
node["formatter"] = m_formatter->getPattern();
}
std::stringstream ss;
ss << node;
return ss.str();
}
/** 重新打开文件 */
bool FileLogAppender::reopen() {
MutexType::Lock lock(m_mutex);
if(m_filestream) {
m_filestream.close();
}
// std::ios_base::openmode app 每次进行写入操作的时候都会重新定位到文件的末尾
m_filestream.open(m_filename.c_str(), std::ios::app);
m_lasttime = time(0);
return m_filestream.is_open();
}
void StdoutLogAppender::log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) {
if(level >= m_level) {
MutexType::Lock lock(m_mutex);
m_formatter->format(std::cout, logger, level, event);
}
}
std::string StdoutLogAppender::toYamlString() {
MutexType::Lock lock(m_mutex);
YAML::Node node;
node["type"] = "StdoutLogAppender";
if(m_level != LogLevel::UNKNOW) {
node["level"] = LogLevel::ToString(m_level);
}
if(m_hasFormatter && m_formatter) {
node["formatter"] = m_formatter->getPattern();
}
std::stringstream ss;
ss << node;
return ss.str();
}
5、日志器
========》智能指针share_ptr的使用《========
class Logger : public std::enable_shared_from_this<Logger> {
friend class LoggerManager;
public:
Logger(const std::string& name = "root");
void log(LogLevel::Level level, LogEvent::ptr event);
/* 日志级别输出 */
void debug(LogEvent::ptr event);
void info(LogEvent::ptr event);
void warn(LogEvent::ptr event);
void error(LogEvent::ptr event);
void fatal(LogEvent::ptr event);
/* 日志器对日志输出地的管理函数 */
void addAppender(LogAppender::ptr appender);
void delAppender(LogAppender::ptr appender);
void clearAppenders();
/* 日志器对日志格式的管理函数 */
void setFormatter(LogFormatter::ptr val);
void setFormatter(const std::string& val);
LogFormatter::ptr getFormatter();
std::string toYamlString();
private:
/// 日志名称
std::string m_name;
/// 日志级别
LogLevel::Level m_level;
/// Mutex
MutexType m_mutex;
/// 日志输出地集合
std::list<LogAppender::ptr> m_appenders;
/// 日志格式器
LogFormatter::ptr m_formatter;
/// 主日志器
Logger::ptr m_root;
};
日志器,可添加删除日志输出的地点,设置日志输出格式;
5.1 实现
/** 初始化名称、级别、格式 */
Logger::Logger(const std::string& name)
:m_name(name)
,m_level(LogLevel::DEBUG) {
m_formatter.reset(new LogFormatter("%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"));
}
/** 根据LogFormatter设置formatter */
void Logger::setFormatter(LogFormatter::ptr val) {
MutexType::Lock lock(m_mutex);
m_formatter = val;
for(auto& i : m_appenders) {
MutexType::Lock ll(i->m_mutex);
if(!i->m_hasFormatter) {
i->m_formatter = m_formatter;
}
}
}
/** 根据val设置formatter */
void Logger::setFormatter(const std::string& val) {
LogFormatter::ptr new_val(new LogFormatter(val));
if(new_val->isError()) {
std::cout << "Logger setFormatter name=" << m_name
<< " value=" << val << " invalid formatter"
<< std::endl;
return;
}
setFormatter(new_val);
}
std::string Logger::toYamlString() {
MutexType::Lock lock(m_mutex);
YAML::Node node;
node["name"] = m_name;
if(m_level != LogLevel::UNKNOW) {
node["level"] = LogLevel::ToString(m_level);
}
if(m_formatter) {
node["formatter"] = m_formatter->getPattern();
}
for(auto& i : m_appenders) {
node["appenders"].push_back(YAML::Load(i->toYamlString()));
}
std::stringstream ss;
ss << node;
return ss.str();
}
/** 获取日志输出地 */
LogFormatter::ptr Logger::getFormatter() {
MutexType::Lock lock(m_mutex);
return m_formatter;
}
/** 添加日志输出地 */
void Logger::addAppender(LogAppender::ptr appender) {
MutexType::Lock lock(m_mutex);
if(!appender->getFormatter()) {
MutexType::Lock ll(appender->m_mutex);
appender->m_formatter = m_formatter;
}
m_appenders.push_back(appender);
}
/** 删除日志输出地 */
void Logger::delAppender(LogAppender::ptr appender) {
MutexType::Lock lock(m_mutex);
for(auto it = m_appenders.begin();
it != m_appenders.end(); ++it) {
if(*it == appender) {
m_appenders.erase(it);
break;
}
}
}
/** 清空 */
void Logger::clearAppenders() {
MutexType::Lock lock(m_mutex);
m_appenders.clear();
}
void Logger::log(LogLevel::Level level, LogEvent::ptr event) {
// 打印<= 给定level的log信息
if(m_level <= level) {
auto self = shared_from_this();
MutexType::Lock lock(m_mutex);
/* 向各个输出地输出 */
if(!m_appenders.empty()) {
for(auto& i : m_appenders) {
i->log(self, level, event);
}
// 如果没有绑定m_appenders,则直接使用logger,即m_root
} else if(m_root) {
m_root->log(level, event);
}
}
}
void Logger::debug(LogEvent::ptr event) {
log(LogLevel::DEBUG, event);
}
void Logger::info(LogEvent::ptr event) {
log(LogLevel::INFO, event);
}
void Logger::warn(LogEvent::ptr event) {
log(LogLevel::WARN, event);
}
void Logger::error(LogEvent::ptr event) {
log(LogLevel::ERROR, event);
}
void Logger::fatal(LogEvent::ptr event) {
log(LogLevel::FATAL, event);
}
6、日志管理器
class LoggerManager {
public:
typedef Spinlock MutexType;
/**
* @brief 构造函数
*/
LoggerManager();
/**
* @brief 获取日志器
* @param[in] name 日志器名称
*/
Logger::ptr getLogger(const std::string& name);
void init();
Logger::ptr getRoot() const { return m_root;}
std::string toYamlString();
private:
/// Mutex
MutexType m_mutex;
/// 日志器容器
std::map<std::string, Logger::ptr> m_loggers;
/// 主日志器
Logger::ptr m_root;
};
/// 日志器管理类单例模式
typedef Singleton<LoggerManager> LoggerMgr;
日志管理类:管理日志器,采用单例的设计模式,使用map内部存储多个日志器并进行管理,提供一个主日志器;
========》C++【设计模式】 | 【08对象性能】Singleton《========
6.1 实现
/** 初始化 */
LoggerManager::LoggerManager() {
m_root.reset(new Logger);
m_root->addAppender(LogAppender::ptr(new StdoutLogAppender));
m_loggers[m_root->m_name] = m_root;
init();
}
Logger::ptr LoggerManager::getLogger(const std::string& name) {
MutexType::Lock lock(m_mutex);
auto it = m_loggers.find(name);
if(it != m_loggers.end()) {
return it->second;
}
Logger::ptr logger(new Logger(name));
logger->m_root = m_root;
m_loggers[name] = logger;
return logger;
}
std::string LoggerManager::toYamlString() {
MutexType::Lock lock(m_mutex);
YAML::Node node;
for(auto& i : m_loggers) {
node.push_back(YAML::Load(i.second->toYamlString()));
}
std::stringstream ss;
ss << node;
return ss.str();
}
void LoggerManager::init() {
}
7、宏定义
/**
* @brief 使用流式方式将日志级别level的日志写入到logger
*/
#define SYLAR_LOG_LEVEL(logger, level) \
if(logger->getLevel() <= level) \
sylar::LogEventWrap(sylar::LogEvent::ptr(new sylar::LogEvent(logger, level, \
__FILE__, __LINE__, 0, sylar::GetThreadId(),\
sylar::GetFiberId(), time(0), sylar::Thread::GetName()))).getSS()
#define SYLAR_LOG_DEBUG(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::DEBUG)
#define SYLAR_LOG_INFO(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::INFO)
#define SYLAR_LOG_WARN(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::WARN)
#define SYLAR_LOG_ERROR(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::ERROR)
#define SYLAR_LOG_FATAL(logger) SYLAR_LOG_LEVEL(logger, sylar::LogLevel::FATAL)
// 规定打印<=指定level的log信息
#define SYLAR_LOG_FMT_LEVEL(logger, level, fmt, ...) \
if(logger->getLevel() <= level) \
sylar::LogEventWrap(sylar::LogEvent::ptr(new sylar::LogEvent(logger, level, \
__FILE__, __LINE__, 0, sylar::GetThreadId(),\
sylar::GetFiberId(), time(0), sylar::Thread::GetName()))).getEvent()->format(fmt, __VA_ARGS__)
#define SYLAR_LOG_FMT_DEBUG(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::DEBUG, fmt, __VA_ARGS__)
#define SYLAR_LOG_FMT_INFO(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::INFO, fmt, __VA_ARGS__)
#define SYLAR_LOG_FMT_WARN(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::WARN, fmt, __VA_ARGS__)
#define SYLAR_LOG_FMT_ERROR(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::ERROR, fmt, __VA_ARGS__)
#define SYLAR_LOG_FMT_FATAL(logger, fmt, ...) SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::FATAL, fmt, __VA_ARGS__)
/**
* 获取主日志器
*/
#define SYLAR_LOG_ROOT() sylar::LoggerMgr::GetInstance()->getRoot()
/**
* 获取name的日志器
*/
#define SYLAR_LOG_NAME(name) sylar::LoggerMgr::GetInstance()->getLogger(name)
8、测试
#include <iostream>
#include "sylar/log.h"
#include "sylar/util.h"
int main(int argc, char** argv) {
// 创建一个日志器
sylar::Logger::ptr logger(new sylar::Logger);
// 输出到控制台
logger->addAppender(sylar::LogAppender::ptr(new sylar::StdoutLogAppender));
// 输出到文件
sylar::FileLogAppender::ptr file_appender(new sylar::FileLogAppender("./log.txt"));
sylar::LogFormatter::ptr fmt(new sylar::LogFormatter("%d%T%p%T%m%n"));
file_appender->setFormatter(fmt);
file_appender->setLevel(sylar::LogLevel::ERROR);
logger->addAppender(file_appender);
// 输出日志
SYLAR_LOG_INFO(logger) << "test macro";
SYLAR_LOG_ERROR(logger) << "test macro error";
SYLAR_LOG_FMT_ERROR(logger, "test macro fmt error %s", "aa");
// 创建新的日志器,并输出日志
auto l = sylar::LoggerMgr::GetInstance()->getLogger("xx");
SYLAR_LOG_INFO(l) << "xxx";
return 0;
}