syalr-1-log
对sylar的日志模块进行说明
使用
代码
#include "sylar/log.h"
int main(int argc, char** argv) {
// 1. 方法一:自己new一个logger
sylar::Logger::ptr logger(new sylar::Logger);
// 1.1 添加stdoutLogAppender,往标准输出写
logger->addAppender(sylar::LogAppender::ptr(new sylar::StdoutLogAppender));
// 1.2 添加fileLogAppender,往文件写
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";
// 2. 方法二:通过loggerManager获得logger对象
auto l = sylar::LoggerMgr::GetInstance()->getLogger("xx");
SYLAR_LOG_INFO(l) << "xxx";
return 0;
}
结果
> ./bin/test1
2024-04-03 00:37:51 234715 UNKNOW 0 [INFO] [root] tests/test.cc:21 test macro
2024-04-03 00:37:51 234715 UNKNOW 0 [ERROR] [root] tests/test.cc:22 test macro error
2024-04-03 00:37:51 234715 UNKNOW 0 [ERROR] [root] tests/test.cc:24 test macro fmt error aa
2024-04-03 00:37:51 234715 UNKNOW 0 [INFO] [xx] tests/test.cc:27 xxx
> cat log.txt
2024-04-03 00:37:51 ERROR test macro error
实现
log模块的类图及其之间的关系如下图所示。先根据上述的使用过程进行分析,给出一条log日志是如何输出的。然后给出各个子模块(类)的简易说明。

一条log是如何输出的?
-
用户通过loggerManager获取一个logger,可根据log.h提供的宏
SYLAR_LOG_ROOT快速获取一个根logger,此宏也是调用了sylar::LoggerMgr::GetInstance()->getRoot()方法获取 -
logger有两个重要的成员,formatter和appender,其中formatter指定日志的输出格式(输出什么);而appender指定日志输出的位置(往哪里输出)。在Logger构造的时候,会初始化formatter,而appender通过Logger的成员方法addAppender添加。
-
在初始化formatter的时候,会调用
LogFormatter::init,对用户传输的格式化字符串解析,如(%t:线程id,%f,文件名…),然后创建相应的ForamItem对象加入formatter的vector成员m_items。至此logger和其成员初始化完毕,等待用户调用Logger->log()进行输出 -
logEvent封装了发生的事件(如:什么时候发生的,在哪个文件哪一行打印,输出日志的线程id,协程Id,消息等)。当用户使用
SYLAR_LOG_DEBUG(logger)打印debug日志或其他不同宏打印不同等级日志时,会封装一个LogEvent对象,并返回其ostream类型的m_ss成员接受msg(如cout« msg中的msg)。然后,在析构时调用logEvent保存的Logger的log方法将日志输出。如下给出了宏SYLAR_LOG_DEBUG的宏定义,它调用了SYLAR_LOG_LEVEL,SYLAR_LOG_LEVEL根据上下文信息封装了logEvent对象,在if退出时,调用LogEventWrapper的析构,在析构函数中调用其成员logger的log方法将日志输出
#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)
...
各个类功能说明
- LogLevel:定义了日志级别,如debug=1,info=2,warn=3, error=4, fatal=5。其中数字越大级别越高,当用户指定级别为error时,只有大于等于error级别的日志才会输出。还提供了枚举类型转String的相关方法
- LogEvent/LogEventWrapper:LogEvent对事件进行封装,如日志是在那个线程,哪个协程,哪个文件哪一行,什么时间等信息进行封装,LogEventWrapper是对LogEvent的进一层封装,比如LogEventWrapper析构时,会调用LogEvent的logger成员的log方法对日志进行输出
- LoggerManager:LoggerManager用单例模式管理起来,其对系统的所有logger进行管理
- Logger:logger协调logEvent(输出什么), logAppender(往哪输出),logFormatter(输出格式)
- LogAppender:一个抽象类,定义了日志往哪输出,目前实现了StdoutLogAppender,FileLogAppender,分别往标准输出、文件输出
- LogFormatter:定义了日志输出的格式,包含多个LogFormatItem,定义了输出什么信息
- LogFormatItem:抽象类,有StringFormatItem,DateTimeFormatItem等实现,定义了各个item具体输出实现