模块概述
模仿log4j日志框架实现一个的日志模块
- 难点:Logformatter的init(),引入了一个简单的状态机机制,实现了将一个字符串解析为模板
- 设计点:
- LogEventWrap:一个封装LogEvent和Logger的RAII类
- LoggerManager:一个管理Logger的单例类
- LogAppender:一个抽象基类,可以方便地扩展日志的输出地
- 一系列辅助函数:
- 优化用户使用方式,用户可使用流式方式或格式化方式进行日志内容的写入
- 包括一系列宏函数和inline,辅助函数创建LogEventWrap来实现日志的输出
模块UML类图

Logger:日志器
- 负责进行日志输出
- 一个日志器可指定一个日志级别(LogLevel)和多个日志输出地(LogAppender)
- 提供log方法,传入日志事件,如果该日志事件的级别高于日志器本身的级别则将其输出,否则将该日志抛弃。
LogAppender:日志输出器
- 用于将一个日志事件输出到对应的输出地。
- 类内部包含一个日志格式器(LogFormatter)成员和一个log方法,日志事件经过格式化后输出到对应的输出地。
- 由于有多种输出地,所以该类成为一个抽象基类,其派生类必须重写它的log方法
- 比如StdoutLogAppender和FileLogAppender为LogAppender的派生类,分别表示输出到终端和文件。
- 与log4cpp的PatternLayout对应,用于格式化一个日志事件(输出log4j日志格式)。
- 该类构建时可以指定pattern,表示如何进行格式化。提供format方法,用于将日志事件格式化成字符串。
- pattern中每个模板参数或字符串对应一个FormatItem派生类
LogEvent:日志事件
- 用于记录日志现场,比如该日志的级别,文件名/行号,日志消息,线程/协程号,所属日志器名称等。
- 可以理解为一个日志事件对应一条日志
LogEventWrap:日志事件包装类
- LogEventWrap是一个RAII类
- LogEventWrap在构造时指定日志事件和日志器
- 在析构时调用日志器的log方法将日志事件进行输出
- LogEventWrap将日志事件和日志器包装到一起
- 一条日志只会在一个日志器上进行输出
- 将日志事件和日志器包装到一起后,方便通过宏定义来简化日志模块的使用。
- 为什么需要LogEventWrap?
- 由于LogEvent::ptr是智能指针,所以如果直接创建LogEvent::ptr,智能指针将在主函数结束时才释放。
- LogEventWrap的getSS()方法支持以流式方式将日志写入logger
- 使用RAII类管理LogEvent,可以在LogEventWrap析构时自动提交日志事件,并将其释放
LogManager:日志器管理类
- 单例模式,用于统一管理所有的日志器,提供日志器的创建与获取方法
- LogManager应成为创建Logger的唯一方式,所有的Logger都应该经由LogManager获取
- 通过singleton.h中定义的单例类GetInstance方法返回LoggerManager的单例对象
- getRoot方法:LogManager自带一个root Logger,用于为日志模块提供一个初始可用的日志器。
- getLogger方法:根据一个名字搜索容器中已有的logger或新建一个logger
调用栈
~LogEventWrap()
→ Logger::log()
→ LogAppender::log()
→ LogFormatter::format()
代码解析
LogLevel
LogLevel表示日志级别
- 类中定义了7种日志级别,实现了日志的分级输出
- 提供了
ToString
和FromString
两个静态方法,支持将日志级别名称转换为字符串,或将字符串转换为日志级别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
class LogLevel{ public: enum Level{ UNKNOW = 0, DEBUG = 1, INFO = 2, WARN = 3, ERROR = 4, FATAL = 5, NOTSET = 100 };
static const char* ToString(Level level);
static Level FromString(const std::string& str); };
|
通过宏函数简化了ToString
和FromString
的书写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
const char* LogLevel::ToString(Level level){ switch (level){ #define XX(name) case LogLevel::name: return #name; XX(DEBUG); XX(INFO); XX(WARN); XX(ERROR); XX(FATAL); #undef XX default: return "UNKNOW"; } }
LogLevel::Level LogLevel::FromString(const std::string& str){ std::string strlower(str.size(), '\0'); std::transform(str.begin(), str.end(), strlower.begin(), ::tolower); #define XX(level, v) if(strlower == #v){ return LogLevel::level; } XX(DEBUG, debug); XX(INFO, info); XX(WARN, warn); XX(ERROR, error); XX(FATAL, fatal); #undef XX return UNKNOW; }
|
LogEvent
LogEvent用于记录日志现场
一个日志事件具体包括以下内容:
- 日志内容
- 日志器名称
- 日志级别
- 文件名,对应__FILE__宏
- 行号,对应__LINE__宏
- 程序运行时间,通过sylar::GetElapsedMS()获取
- 线程ID
- 协程ID
- UTC时间戳,对应time(0)
- 线程名称
LogEvent提供了format
方法,将以上信息格式化写入日志内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
|
class LogEvent{ public: using ptr = std::shared_ptr<LogEvent>;
LogEvent(std::string loggerName, LogLevel::Level level, const char *file, int32_t line , int64_t elapse, uint32_t thread_id, uint64_t fiber_id, time_t time , const std::string &thread_name);
std::string getFile() const { return m_file; }
int32_t getLine() const { return m_line; }
uint32_t getElapse() const { return m_elapse; }
uint32_t getThreadId() const { return m_threadId; }
uint32_t getFiberId() const { return m_fiberId; }
uint64_t getTime() const { return m_time; }
const std::string& getThreadName() const { return m_threadName; }
std::string getContent() const { return m_ss.str(); }
std::stringstream& getSS() { return m_ss; }
std::string getLoggerName() const { return m_loggerName; }
LogLevel::Level getLevel() const { return m_level; }
template <typename... Args> void format(const char* fmt, Args&&... args){ int len = snprintf(nullptr, 0, fmt, args...); if(len >= 0){ char* buf = new char[len + 1]; snprintf(buf, len + 1, fmt, args...); m_ss << std::string(buf, len); delete[] buf; } }
private: std::string m_loggerName; LogLevel::Level m_level; const char* m_file = nullptr; int32_t m_line = 0; uint32_t m_elapse = 0; uint32_t m_threadId = 0; uint32_t m_fiberId = 0; uint64_t m_time = 0; std::string m_threadName; std::stringstream m_ss; };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
LogEvent::LogEvent(std::string loggerName, LogLevel::Level level, const char *file, int32_t line , int64_t elapse, uint32_t thread_id, uint64_t fiber_id, time_t time , const std::string &thread_name) : m_loggerName(loggerName) , m_level(level) , m_file(file) , m_line(line) , m_elapse(elapse) , m_threadId(thread_id) , m_fiberId(fiber_id) , m_time(time) , m_threadName(thread_name) { }
|
LogFormatter用于将日志事件LogEvent格式化(模仿log4j日志框架)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
class LogFormatter{ public: using ptr = std::shared_ptr<LogFormatter>;
LogFormatter(const std::string& pattern = "%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n");
std::ostream& format(std::ostream& os, LogEvent::ptr event);
bool isError() const { return m_error; }
std::string getPattern() const { return m_pattern; }
private: void init();
public: class FormatItem{ public: using ptr = std::shared_ptr<FormatItem>;
virtual ~FormatItem(){};
virtual void format(std::ostream& os, LogEvent::ptr event) = 0; };
private: std::string m_pattern; std::vector<FormatItem::ptr> m_items; bool m_error; };
|
pattern
由于一个日志事件包括了很多的内容(参考LogEvent),但实际上用户并不希望每次输出日志时都将这些信息全部进行输出,而是希望可以自由地选择要输出的信息。并且,用户还可能需要在每条日志里增加一些指定的字符,比如在文件名和行号之间加上一个冒号的情况。
为了实现这项功能,LogFormatter使用了一个模板字符串来指定格式化的方式。模板字符串由普通字符和转义字符构成,转义字符以%开头,比如%m,%p等。除了转义字符,剩下的全部都是普通字符,包括空格,当前支持以下转义字符:
text1 2 3 4 5 6 7 8 9 10 11 12
| %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"
- 描述:年-月-日 时:分:秒 [累计运行毫秒数] \t 线程id \t 线程名称 \t 协程id \t [日志级别] \t [日志器名称] \t 文件名:行号 \t 日志消息 换行符
format使用多态,逐个调用FormatItem派生类的format方法,将日志信息写入os
1 2 3 4 5 6
| std::ostream& LogFormatter::format(std::ostream& os, LogEvent::ptr event){ for(auto& formatItem : m_items){ formatItem->format(os, event); } return os; }
|
init
init()
是LogFormatter的实现重点,其作用是将m_pattern解析为一个FormatItem::ptr数组,并存入m_item
init的逻辑大致只有以下两步,但实现方式需要多琢磨
使用一个简单的基于状态机的解析方案,解析模板字符串与常规字符
- 支持解析上面列举的转义字符,并且支持将连续的普通字符合并成一个字符串
text1 2 3 4 5 6 7 8 9 10 11 12 13
| | 当前字符是否是%? -->是 -->状态机状态为正在解析模板参数 - 当前字符为转义%,将模板参数记录到数组 -->状态机状态为正在解析常规字符串 - 当前常规字符串解析完成,保存已解析的常规字符串 - 准备开始解析模板参数,状态机状态变为正在解析模板参数 -->否 -->状态机状态为正在解析模板参数 - 将模板参数记录到数组(对于日期格式(模板参数为d),还需要进一步进行处理) - 解析完模板参数,状态机状态变为正在解析常规字符串 -->状态机状态为正在解析常规字符串 - 记录字符到字符串
|
创建模板参数对应的FormatItem
对象(使用宏函数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
| void LogFormatter::init(){ std::vector<std::pair<int, std::string>> vec;
std::string dateFormat; std::string normalString; bool parsing_string = true;
for(size_t i = 0; i < m_pattern.size(); ++i){ std::string c = std::string(1, m_pattern[i]); if(c == "%"){ if(parsing_string){ if(!normalString.empty()){ vec.push_back(std::make_pair(0, normalString)); normalString.clear(); } parsing_string = false; continue; } else { vec.push_back(std::make_pair(1, c)); parsing_string = true; } } else { if(parsing_string){ normalString += c; continue; } else { vec.push_back(std::make_pair(1, c));
if(c == "d"){ ++i; if(m_pattern[i] != '{'){ continue; } else { ++i; while(i < m_pattern.size() && m_pattern[i] != '}'){ dateFormat.push_back(m_pattern[i]); ++i; } if(i == m_pattern.size() && m_pattern[i - 1] != '}'){ vec.push_back(std::make_pair(0, "<<Pattern Error>>")); dateFormat.clear(); m_error = true; } } } parsing_string = true; } } } if(!normalString.empty()){ vec.push_back(std::pair(0, normalString)); normalString.clear(); }
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), XX(p, LevelFormatItem), XX(c, LoggerNameFormatItem), XX(d, DateTimeFormatItem), XX(r, ElapseFormatItem), XX(f, FilenameFormatItem), XX(l, LineFormatItem), XX(t, ThreadIdFormatItem), XX(F, FiberIdFormatItem), XX(N, ThreadNameFormatItem), XX(%, PercentSignFormatItem), XX(T, TabFormatItem), XX(n, NewLineFormatItem),
#undef XX };
for(auto& item : vec){ if(item.first == 0){ m_items.push_back(FormatItem::ptr(new StringFormatItem(item.second))); } else { if(item.second == "d"){ m_items.push_back(FormatItem::ptr(new DateTimeFormatItem(dateFormat))); } else { auto it = s_format_items.find(item.second); if(it != s_format_items.end()){ m_items.push_back(FormatItem::ptr(it->second(item.second))); } else { m_items.push_back(FormatItem::ptr(new StringFormatItem("<<error_format %" + item.second + ">>"))); } } } } }
|
LogAppender
LogAppender用于将一个日志事件输出到对应的输出地
LogAppender是一个虚类,可以派生出不同的具体实现。不同类型的Appender通过重载log方法来实现往不同的目的地进行输出
基类指定了格式化器(LogFormatter),用户可以自定义格式化器,如果用户没有定义,则使用默认格式化器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
class LogAppender{ public: using ptr = std::shared_ptr<LogAppender>; using MutexType = SpinLock;
LogAppender(LogFormatter::ptr defaultFormatter) : m_defaultFormatter(defaultFormatter){};
virtual ~LogAppender(){};
virtual void log(LogEvent::ptr event) = 0;
void setFormatter(LogFormatter::ptr formatter);
LogFormatter::ptr getFormatter() const ;
virtual std::string toYamlString() const = 0;
protected: LogFormatter::ptr m_formatter; LogFormatter::ptr m_defaultFormatter; mutable MutexType m_mutex; };
|
基类的get/set方法
1 2 3 4 5 6 7 8 9 10 11
| void LogAppender::setFormatter(LogFormatter::ptr formatter) { MutexType::Lock lock(m_mutex); m_formatter = formatter; }
LogFormatter::ptr LogAppender::getFormatter() const { MutexType::Lock lock(m_mutex); return m_formatter; }
|
StdoutLogAppender——输出到终端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class StdoutLogAppender: public LogAppender{ public: StdoutLogAppender();
using ptr = std::shared_ptr<StdoutLogAppender>;
void log(LogEvent::ptr event) override;
std::string toYamlString() const override; };
StdoutLogAppender::StdoutLogAppender() : LogAppender(LogFormatter::ptr(new LogFormatter)){ };
void StdoutLogAppender::log(LogEvent::ptr event){ if(m_formatter){ MutexType::Lock lock(m_mutex); m_formatter->format(std::cout, event); } else { MutexType::Lock lock(m_mutex); m_defaultFormatter->format(std::cout, event); } }
|
FileLogAppender——输出到文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| class FileLogAppender: public LogAppender{ public: using ptr = std::shared_ptr<FileLogAppender>;
FileLogAppender(const std::string& filename);
void log(LogEvent::ptr event) override;
std::string toYamlString() const override;
private: bool reopen();
private: std::string m_filename; std::ofstream m_filestream; uint64_t m_lastTime; };
FileLogAppender::FileLogAppender(const std::string& filename) : LogAppender(LogFormatter::ptr(new LogFormatter)) , m_filename(filename){ }
void FileLogAppender::log(LogEvent::ptr event){ uint64_t now = event->getTime(); if(now >= (m_lastTime + 3)){ if(!reopen()){ std::cerr << "reopen fail, file name=" << m_filename << std::endl; } m_lastTime = now; } MutexType::Lock lock(m_mutex); if(m_filestream){ if(m_formatter){ m_formatter->format(m_filestream, event); } else { m_defaultFormatter->format(m_filestream, event); } } }
|
Logger
Logger负责进行日志输出
- Logger的多线程安全:因为log操作的阻塞时间较短,所以使用自旋锁来保证多线程安全
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| class Logger{ public: friend LogManager;
using ptr = std::shared_ptr<Logger>; using MutexType = SpinLock;
Logger(const std::string& name = "root");
void log(LogEvent::ptr event);
void addAppender(LogAppender::ptr appender); void delAppender(LogAppender::ptr appender); void clearAppenders();
LogLevel::Level getLevel() const { return m_level; }; void setLevel(LogLevel::Level level);
std::string getName() const { return m_name; }
std::string toYamlString() const;
private: std::string m_name; LogLevel::Level m_level; std::list<LogAppender::ptr> m_appenders; Logger::ptr m_root; mutable MutexType m_mutex; };
|
log
log接受一个日志事件,先判断该日志事件的级别是否大于本日志器的日志级别。如果日志级别满足条件,则调用appender的log方法将日志事件进行输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| void Logger::log(LogEvent::ptr event){ if(event->getLevel() >= m_level){ MutexType::Lock lock(m_mutex); if(!m_appenders.empty()){ for(auto& appender: m_appenders){ appender->log(event); } } else if(m_root) { for(auto& appender : m_root->m_appenders){ appender->log(event); } } } }
|
appender操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| void Logger::addAppender(LogAppender::ptr appender){ MutexType::Lock lock(m_mutex); 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(); }
|
toYamlString
将Logger信息输出为Yaml格式的字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| std::string Logger::toYamlString() const { YAML::Node node(YAML::NodeType::Map); node["name"] = m_name; node["level"] = LogLevel::ToString(m_level); MutexType::Lock lock(m_mutex);
for(const auto& a : m_appenders){ node["appenders"].push_back(YAML::Load(a->toYamlString())); } std::stringstream ss; ss << node; return ss.str(); }
|
LogEventWrap
LogEventWrap是一个RAII类,它封装了一个Logger和LogEvent,在其析构时调用Logger的log方法提交日志事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
class LogEventWrap{ public: LogEventWrap(Logger::ptr logger, LogEvent::ptr event);
~LogEventWrap();
LogEvent::ptr getEvent() const { return m_event; }
std::stringstream& getSS();
private: Logger::ptr m_logger; LogEvent::ptr m_event; };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
LogEventWrap::LogEventWrap(Logger::ptr logger, LogEvent::ptr event) : m_logger(logger) , m_event(event){ }
LogEventWrap::~LogEventWrap(){ m_logger->log(m_event); }
std::stringstream& LogEventWrap::getSS(){ return m_event->getSS(); }
|
LogManager
LogManager用于统一管理所有的日志器,提供日志器的创建与获取方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
class LogManager{ public: friend Singleton<LogManager>;
using MutexType = SpinLock;
Logger::ptr getLogger(std::string loggerName);
void init();
Logger::ptr getRoot() const { return m_root; }
std::string toYamlString() const;
private: LogManager();
private: Logger::ptr m_root; std::map<std::string, Logger::ptr> m_loggers; mutable MutexType m_mutex; };
using LoggerMgr = Singleton<LogManager>;
|
Singleton单例类
基于单例模式的单例模板类,通过静态函数创建指定类的静态对象
1 2 3 4 5 6 7 8 9
| template<typename T, typename X = void, int N = 0> class Singleton{ public: static T* GetInstance(){ static T v; return &v; } };
|
通过类型别名为用户提供创建LogManager单例的方法:
1 2
| using LoggerMgr = Singleton<LogManager>;
|
构造函数
构造时默认创建一个root日志器,将日志信息输出到终端
1 2 3 4 5 6
| LogManager::LogManager(){ m_root.reset(new Logger("root")); m_root->addAppender(std::make_shared<StdoutLogAppender>()); m_loggers[m_root->getName()] = m_root; init(); }
|
getLogger
getLogger根据日志器名从管理的日志器中返回一个Logger,否则以该名字创建一个Logger
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Logger::ptr LogManager::getLogger(std::string loggerName){ MutexType::Lock lock(m_mutex);
auto it = m_loggers.find(loggerName); if(it != m_loggers.end()){ return it->second; } Logger::ptr newLogger(std::make_shared<Logger>(loggerName)); newLogger->m_root = m_root; m_loggers[loggerName] = newLogger; return newLogger; }
|
toYamlString
将LoggerManager
的信息输出为Yaml格式的字符串
1 2 3 4 5 6 7 8 9 10
| std::string LogManager::toYamlString() const { MutexType::Lock lock(m_mutex); YAML::Node node(YAML::NodeType::Map); for(const auto& l : m_loggers){ node["logs"].push_back(YAML::Load(l.second->toYamlString())); } std::stringstream ss; ss << node; return ss.str(); }
|
实用辅助函数
使用流式方式写入日志
使用流式方式将日志级别level的日志写入logger
基于宏函数实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
#define SYLAR_LOG_LEVEL(logger, level) \ if(logger->getLevel() <= level) \ sylar::LogEventWrap(logger, std::make_shared<sylar::LogEvent>(logger->getName(), level, __FILE__, __LINE__\ , 0, sylar::GetThreadId(), sylar::GetFiberId(), time(0), sylar::GetThreadName())).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)
|
使用格式化方式写入日志
基于inline实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
template<typename... Arg> inline void SYLAR_LOG_FMT_LEVEL(sylar::Logger::ptr logger, sylar::LogLevel::Level level, const char* fmt, Arg&&... args){ if(logger->getLevel() <= level){ sylar::LogEventWrap(logger, std::make_shared<sylar::LogEvent>(logger->getName(), level, __FILE__, __LINE__ , 0, sylar::GetThreadId(), 42, time(0), "Thread")).getEvent()->format(fmt, args...); } }
template<typename... Arg> inline void SYLAR_LOG_FMT_DEBUG(sylar::Logger::ptr logger, const char* fmt, Arg&&... args){ SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::DEBUG, fmt, args...); }
template<typename... Arg> inline void SYLAR_LOG_FMT_INFO(sylar::Logger::ptr logger, const char* fmt, Arg&&... args){ SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::INFO, fmt, args...); }
template<typename... Arg> inline void SYLAR_LOG_FMT_WARN(sylar::Logger::ptr logger, const char* fmt, Arg&&... args){ SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::WARN, fmt, args...); }
template<typename... Arg> inline void SYLAR_LOG_FMT_ERROR(sylar::Logger::ptr logger, const char* fmt, Arg&&... args){ SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::ERROR, fmt, args...); }
template<typename... Arg> inline void SYLAR_LOG_FMT_FATAL(sylar::Logger::ptr logger, const char* fmt, Arg&&... args){ SYLAR_LOG_FMT_LEVEL(logger, sylar::LogLevel::FATAL, fmt, args...); }
|
获取日志器
1 2 3 4
| #define SYLAR_LOG_ROOT() sylar::LoggerMgr::GetInstance()->getRoot()
#define SYLAR_LOG_NAME(name) sylar::LoggerMgr::GetInstance()->getLogger(name)
|
模块使用
例子1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| int main(int argc, char** argv) { 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);
auto mgr = sylar::LoggerMgr::GetInstance(); mgr->getRoot()->addAppender(file_appender);
auto logger = mgr->getLogger("Logger01"); logger->addAppender(std::make_shared<sylar::StdoutLogAppender>());
std::string str("LoggerManager"); SYLAR_LOG_ERROR(mgr->getRoot()) << "test " << str << " Error"; SYLAR_LOG_INFO(logger) << "test logger01";
SYLAR_LOG_FMT_DEBUG(mgr->getRoot(), "test %s Debug <%s>", str.c_str(), "using inline and template");
return 0; }
|
例子2
1 2 3 4 5 6 7 8 9
| static sylar::Logger::ptr g_logger1 = SYLAR_LOG_NAME("system");
SYLAR_LOG_INFO(g_logger1) << "test logger01";
static sylar::Logger::ptr g_logger2 = SYLAR_LOG_ROOT();
SYLAR_LOG_INFO(g_logger2) << "test logger02";
|