0
点赞
收藏
分享

微信扫一扫

Sylar服务器框架 日志模块 源码分析

全栈顾问 2022-02-17 阅读 33

Sylar服务器框架 日志模块 源码分析

文章目录

日志库模块整体设计

文章目录


先上图

日志模块1

关键的类

sylar日志库 一共6个日志级别 UNKNOW,DEBUG,INFO,WARN,ERROR,FATAL

// 日志级别
class LogLevel;
// 日志格式化
class LogFormatter;
// 日志输出目标
class LogAppender;
// 输出到控制台的Appender
class StdoutLogAppender : public LogAppender;
// 输出到文件的Appender
class FileLogAppender : public LogAppender;
// 日志器
class Logger;
// 日志事件
class LogEvent;
// 日志事件包装器
class LogEventWrap;
// 日志器管理类
class LogManager;

上述类详细设计:

  • LogFormatter:日志格式器,可用于格式化日志事件,通过format方法用于将日志事件格式化成字符串;
  • LogAppender:日志输出目标,用于将日志事件输出到对应的输出地,该类包含LogFormatter格式化方法和log方法,可通过继承该类实现到不同的输出地输出;
  • Logger:日志器,负责日志输出,提供log方法,若该日志需要输出则调用LogAppender将日志输出,否则抛弃;
  • LogEvent:日志时间,用于记录日志信息,比如该日志的级别,文件名/行号,日志消息,线程/协程号,所属日志器名称等。;
  • LogEventWrap:日志事件包装器,将日志事件与日志器包装在一起,通过宏定义简化日志模块的使用,当期析构时候,则调用日志器的log方法进行输出;
  • LogManager:日志器管理类,单例模式,用于统一管理所有的日志器,提供日志器的创建与获取方法,内部维护一个名称到日志器的map,当获取的日志器存在时,直接返回对应的日志器指针,否则创建对应的日志器并返回。

日志库整体流程和重要函数说明(最终是由LogAppender的LogFormatter负责相应调用完成最终日志输出)

1、初始化Logger,LogFormatter,LogAppender;
2、通过宏定义生成LogEventWrap对象;
3、LogEventWrap对象析构时,调用log方法输出日志。
总结:通过Loggermanager管理Logger写日志:通过LogEventWrap析构触发不同logger上的LogEvent不同的logger往不同的LoggerAppender上写日志。

红线为 formatter类 在不同类中的 转移流程

日志模块1

sylar::Logger::ptr logger(new sylar::Logger);
// 控制台输出
logger->addAppender(sylar::LogAppender::ptr(new sylar::StdoutLogAppender));
// 文件输出,输出到"./log.txt"
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::LogEvent::ptr event2(new sylar::LogEvent(logger,sylar::LogLevel::ERROR1,__FILE__, __LINE__, 0, 1, 2, time(0),"root"));
// 绑定日志事件
sylar::LogEventWrap wrap(event2);
// 输出hello
wrap.getEvent()->getSS() << "hello";
// 输出
logger->log(sylar::LogLevel::ERROR, wrap.getEvent());
wrap.getEvent()->format("test macro fmt error %c", 'c');

重要代码逐行分析

LogFormatter::init —— 格式解析函数(100行左右)

详情请见https://github.com/zhongluqiang/sylar-from-scratch/blob/main/sylar/log.cpp

/**
     * @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"
     */
     
     结果
2022-02-17 18:34:34     1       test    2       [DEBUG] [root]  /home/knight/framework/tests/test.cc:7 
  • %d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n

  • 第一次 遍历结果是 str = “d” ——fmt = “%Y-%m-%d %H:%M:%S”

  • “T” “t”

  • nstr是用来解析 [ ]等类似字符 —— 对应 StringFormatItem,

  • 分别加入后,由logger->log函数负责一个个按顺序调用,最终实现日志打印

大致流程

从第一个字母开始遍历

第i个是否%
第i+1是否字母
否,加入nstr
第i+1个是否是 {
否,( 且不为{ )退出循环
是,进入解析状态,直到遇到 }
否,退出循环
       // 源代码第12行 ~ 19行 无用代码
		/*if((i + 1) < m_pattern.size()) {
            if(m_pattern[i + 1] == '%') { // 两个%%的情况,基本不会遇到可忽略
                nstr.append(1, '%'); // 字符串末尾添加一个%
                continue;
            }
        }*/
// %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) { // 遍历m_pattern字符串
        if(m_pattern[i] != '%') {
            nstr.append(1, m_pattern[i]); // 字符串末尾添加一个m_pattern[i]
            continue;
        }

        size_t n = i + 1; // 从 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); // 取一个字符
                    //std::cout << "*" << str << std::endl;
                    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); //截取{ }
                    //std::cout << "#" << fmt << std::endl;
                    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; // i 从} 开始从新遍历
        } 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;
                std::cout << "error" << std::endl;
            } else {
                m_items.push_back(it->second(std::get<1>(i)));
            }
        }

        // std::cout << "(" << std::get<0>(i) << ") - (" << std::get<1>(i) << ") - (" << std::get<2>(i) << ")" << std::endl;
    }
    // std::cout << m_items.size() << std::endl;
}
举报

相关推荐

0 条评论