C++实现高性能服务器框架(1) | PHM's world

LOADING

歡迎來到烏托邦的世界

C++实现高性能服务器框架(1)

该项目是一个高性能的基于协程的服务器框架,主要特性包括支持多线程、多协程协同调度和异步处理,以提高服务器性能。具体功能模块包括日志模块、线程模块、IO协程调度模块、定时器模块、hook模块和HTTP模块。各模块的功能:

日志模块

  • 宏实现流式输出:使用宏来实现日志的流式输出,便于记录日志信息。
  • 同步日志与异步日志:支持同步和异步两种日志记录方式。
  • 自定义日志格式:可以根据需求自定义日志格式。
  • 日志级别:支持不同的日志级别(如DEBUG、INFO、WARN、ERROR等)。
  • 多日志分离:可以将不同类型的日志分开记录,便于管理和查阅。

线程模块

  • 封装pthread相关方法:对pthread库的常用方法进行封装,简化多线程编程。
  • 封装常用锁:包括信号量、读写锁、自旋锁等,提供多种同步机制以满足不同需求。

IO协程调度模块

  • 非对称协程模型:基于ucontext_t实现非对称协程模型,实现轻量级任务切换。
  • 多线程协同调度:以线程池的方式实现多线程、多协程协同调度,提高并发处理能力。
  • 事件监听机制:依赖epoll实现高效的事件监听和处理。

定时器模块

  • 最小堆管理定时器:使用最小堆数据结构管理定时器,确保定时任务按时执行。
  • 协程定时任务调度:配合IO协程调度模块,可以实现基于协程的定时任务调度。

Hook模块

  • 封装同步系统调用为异步操作:将常用的同步系统调用(如acceptrecvsend等)封装成异步操作,大幅提升服务器性能。

HTTP模块

  • 封装socket常用方法:对socket的常用操作进行封装,简化网络编程。
  • 支持HTTP协议解析:实现HTTP请求和响应的解析。
  • 客户端连接池:客户端实现连接池机制,提高连接复用率和请求处理效率。
  • 服务器端servlet模式:服务器端采用servlet模式处理客户端请求,支持灵活的请求处理。
  • Reactor模式:支持单Reactor多线程和多Reactor多线程模式,提升服务器的并发处理能力。

这个服务器框架通过上述各个模块的配合,实现了高性能、高并发的网络服务器,为用户提供了搭建HTTP服务器或WebSocket服务器的便捷方式。

C++实现高性能服务器(1)—Logger模块

1. 主要功能

  • 支持不同日志级别
  • 可以自由的控制日志输出的位置(目前包括输出到控制台,文件)
  • 支持自定义日志格式
  • 设置了一系列工具宏,实现流式输出
  • 目前还是同步日志,后期再添加异步日志

2.主要的类

  • class LogLevel:定义日志级别。并提供将日志级别与文本之间的互相转化

  • class Logger:日志器。定义日志级别,设置输出地,设置日志格式。

  • class LogEvent:记录日志事件。主要记录一下信息

  • class LogEventWarp:日志事件包装器。将logEvent打包,可以直接通过使用该类完成对日志的定义。

  • class LogFormatter:日志格式化。

  • class LogAppender:日志输出目标。有两个子类 class StdoutLogAppenderclass FileLogAppender,可以分别输出到控制台和文件

  • class LoggerManager:日志管理器。单列模式

class LogLevel(日志级别)

通过枚举定义了6个日志级别:

enum Level{
        //  未知 级别
        UNKNOW = 0,
        //  DEBUG 级别
        DEBUG = 1,
        //  INFO 级别
        INFO = 2,
        //  WARN 级别
        WARN = 3,
        //  ERROR 级别
        ERROR = 4,
        //  FATAL 级别
        FATAL = 5
    };

class LogEvent(日志事件)

成员变量:

    const char* m_file = nullptr;   //文件名
    int32_t m_line = 0;             //行号
    uint32_t m_elapse = 0;          //程序启动开始到现在的毫秒数
    uint32_t m_thieadId = 0;        //线程id
    uint32_t m_fiberId = 0;         //协程id
    uint64_t m_time;                //时间戳
    std::string m_threadName;       //线程名称
    std::stringstream m_ss;         //日志内容流
    std::shared_ptr<Logger> m_logger;   //日志器
    LogLevel::Level m_level;        //日志等级

format方法(格式化写入日志内容)

1)首先在函数里定义一具va_list型的变量al,这个变量是指向参数的指。

2)使用 va_start(al, fmt) 宏初始化 al, 并将其指向参数列表中的第一个参数。

3)将fmtal传入format中。

4)使用vasprintf(&buf, fmt, al)al按照fmt格式放到buf中。

5)若成功,则将buf输出到流中 。

6)最后使用 va_end() 宏清理 va_list

(这段代码没有被使用)

void LogEvent::format(const char* fmt, ...) {
    va_list al;  		//1)
    va_start(al, fmt);	//2)
    format(fmt, al);	//3)
    va_end(al);			//6)
}

void LogEvent::format(const char* fmt, va_list al){
    char *buf = nullptr;
    // len返回写入buf的长度
    int len = vasprintf(&buf, fmt, al);	//4)
    if(len != -1) {
        m_ss << std::string(buf, len);	//5)
        free(buf);
    }
}

class LogEventWarp(日志事件包装器)

// 日志事件
LogEvent::ptr m_event;

在此说一下使用日志的宏,这里定义了SYLAR_LOG_LEVEL宏,用来输出Level级别的LogEvent,并将LogEvent写入到Logger中。

这里也说明了为什么要使用LogEventWarp这个类。当想使用该宏打一次日志后,由于LogEvent使用的是智能指针,在定义该宏的作用域下这个LogEvent并不会立即释放,所以使用LogEventWarp包装LogEvent::ptr当定义该宏的语句执行完后就会自动进入析构函数,并将LogEvent写入Logger中,打印出日志。

less复制代码#define SYLAR_LOG_LEVEL(logger, level) \
    if (logger->getLevel() <= level) \
        sylar::LogEventWarp(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)

class LogFormatter & class FormatItem

class FormatItem(日志内容格式化)

该类为LogFormatter的public内部类成员,通过该类得到解析后的格式。

此类为抽象类,不同事件的子类继承该类,并且重写纯虚函数format将日志格式转化到流

格式化日志到流

// 消息format
class MessageFormatItem : public LogFormatter::FormatItem{
public:
    MessageFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getContent(); 
    }
};

// 日志级别format
class LevelFormatItem : public LogFormatter::FormatItem{
public:
    LevelFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << LogLevel::ToString(level);
    }
};

// 执行时间format
class ElapseFormatItem : public LogFormatter::FormatItem{
public:
    ElapseFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getElapse(); 
    }
};

// 日志器名称format
class NameFormatItem : public LogFormatter::FormatItem{
public:
    NameFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getLogger()->getName(); 
    }
};

// 线程id format
class ThreadIdFormatItem : public LogFormatter::FormatItem{
public:
    ThreadIdFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getThieadId(); 
    }
};

// 协程id format
class FiberIdFormatItem : public LogFormatter::FormatItem{
public:
    FiberIdFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getFiberId(); 
    }
};

// 线程名称format
class ThreadNameFormatItem : public LogFormatter::FormatItem{
public:
    ThreadNameFormatItem(const std::string& str = "") {}
    void format(std::ostream& os, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override {
        os << event->getThreadName(); 
    }
};

// 时间format
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, std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event) override {
        struct tm tm;
        time_t time = event->getTime();	//创建event时默认给的 time(0) 当前时间戳
        localtime_r(&time, &tm);	//将给定时间戳转换为本地时间,并将结果存储在tm中
        char buf[64];
        strftime(buf, sizeof(buf), m_format.c_str(), &tm);	//将tm格式化为m_format格式,并存储到buf中
        os << buf; 
    }
private:
    std::string m_format;
};

class LogFormatter(日志格式化)

mumber(成员变量)

    // 日志格式模板
    std::string m_pattern;
    // 日志格式解析后格式
    std::vector<FormatItem::ptr> m_items;
    // 判断日志格式错误
    bool m_error = false;

LogFormatter(构造函数)

初始化日志格式,并解析

LogFormatter::LogFormatter(const std::string& pattern)
    :m_pattern(pattern) {
        init();
}

format(返回格式化日志文本)

将解析后的日志信息输出到流中

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();
}

init(解析格式)

得到相应FormatItem放入m_items

默认格式模板为:"%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"

e.g.Y-M-D H:M:S threadId threadName fiberId [Level] [logName] FILE:LINE message

//%xxx %xxx{xxx} %%
// m_pattern "%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(){ 
    //string, 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]);	//在nstr后面添加一个该字符
            continue;
        }

        if((i + 1) < m_pattern.size()) {	//保证m_pattern不越界
            if (m_pattern[i + 1] == '%') {	//解析 "%%"
                nstr.append(1, '%');		//在nstr后面加上%
                continue;
            }
        
                                                
        size_t n = i + 1;		//遇到'%'往下  (e.g.) n = 1, m_pattern[1] = 'd'
        int fmt_status = 0;		//状态1: 解析时间{%Y-%m-%d %H:%M:%S} 状态0:解析之后的
        size_t fmt_begin = 0;	//开始位置 为{

        std::string str;		//d T t N等格式
        std::string fmt;		//保存时间格式 %Y-%m-%d %H:%M:%S	

        while(n < m_pattern.size()){
            // fmt_status != 0, m_attern[n]不是字母,m_pattern[n]不是'{', m_pattern[n]不是'}'
            // (e.g.) %T%  (i -> %, n -> T, while循环 n -> % 此时解析完一个T, break
            // (e.g.) 遇到 [ ] break,取出[%p]中的p
            if(!fmt_status && (!isalpha(m_pattern[n]) && m_pattern[n] != '{' //返回0表示该字符不是字母字符。
                    && 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);	//str = "d"
                    fmt_status = 1;	
                    fmt_begin = n;
                    ++n;
                    continue;
                }
            } else if(fmt_status == 1) {	//结束解析时间格式
                if(m_pattern[n] == '}') {
                    // fmt = %Y-%m-%d %H:%M:%S
                    fmt = m_pattern.substr(fmt_begin + 1, n - fmt_begin - 1);
                    fmt_status = 0;
                    ++n;
                    break;		//解析时间结束break
                }
            }
            ++n;
            if (n == m_pattern.size()) {	//最后一个字符
                if (str.empty()) {
                    str = m_pattern.substr(i + 1);
                }
            }
        }
        if(fmt_status == 0{
            if(!nstr.empty()){	// nstr: [ :
                vec.push_back(std::make_tuple(nstr, std::string(), 0));	// 将[ ]放入, type为0
                nstr.clear();
            }
            vec.push_back(std::make_tuple(str, fmt, 1));	//(e.g.) ("d", %Y-%m-%d %H:%M:%S, 1) type为1
            i = n - 1;	 //跳过已解析的字符,让i指向当前处理的字符,下个for循环会++i处理下个字符
        } else if(fmt_status == 1) {
            std::cout << "Pattern parde 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));	//(e.g.) 最后一个字符为[ ] :
    }
    
    // map类型为<string, cb>, string为相应的日志格式, cb返回相应的FormatItem智能指针
    static std::map<std::string, std::function<FormatItem::ptr(const std::string& fmt)> > 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) {	//若type为0
            //将解析出的FormatItem放到m_items中 [ ] :
            m_items.push_back(FormatItem::ptr(new StringFormatItem(std::get<0>(i))));
        } else {	//type为1
            auto it = s_format_items.find(std::get<0>(i));	//从map中找到相应的FormatItem
            if(it == s_format_items.end()) {	//若没有找到则用StringFormatItem显示错误信息 并设置错误标志位
                m_items.push_back(FormatItem::ptr(new StringFormatItem("<<error_format %" + std::get<0>(i) + ">>")));
                m_error = true;
            } else {	//返回相应格式的FormatItem,其中std::get<1>(i)作为cb的参数
                m_items.push_back(it->second(std::get<1>(i)));
            }
        }
    }
}

class LogAppender & class StdoutLogAppender & class FileLogAppender

class LogAppender(日志输出目标)

class LogAppender是抽象类,有两个子类,分别为StdoutLogAppenderFileLogAppender,分别实现控制台和文件的输出。两个类都重写纯虚函数log方法实现写入日志,重写纯虚函数toYamlString方法实现将日志转化为YAML格式的字符串

mumber(成员变量)

    //日志级别
    LogLevel::Level m_level = LogLevel::DEBUG;
    //日志格式器
    LogFormatter::ptr m_formatter;
    // 互斥锁
    MutexType m_mutex;
    // 是否有formatter
    bool m_hasFormatter = false;

setFormatter(更改日志格式器)

void LogAppender::setFormatter(LogFormatter::ptr val) {
    MutexType::Lock lock(m_mutex);
    m_formatter = val;
    if (m_formatter) {
        m_hasFormatter = true;
    } else {
        m_hasFormatter = false;
    }
}

getFormatter(获得日志格式器)

LogFormatter::ptr LogAppender::getFormatter() {
    MutexType::Lock lock(m_mutex);
    return m_formatter;
}

class StdoutLogAppender(输出到控制台的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;
};

log(输出到流)

void StdoutLogAppender::log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event){
    if(level >= m_level) {
        MutexType::Lock lock(m_mutex);
        std::cout << m_formatter->format(logger, level, event);	//这里调用Logformat的format,它会遍历m_items调用相应的format输出到流
    }
}

toYamlString(输出Yaml格式字符串)

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();
}

class FileLogAppender(输出到文件的Appender)

mumber(成员变量)

    // 文件路径
    std::string m_filename;
    // 文件流
    std::ofstream m_filestream;
    // 每秒reopen一次,判断文件有没有被删
    uint64_t m_lastTime = 0;

FileLogAppender(构造函数)

初始化文件路径,并且打开文件

FileLogAppender::FileLogAppender(const std::string& filename)
    :m_filename(filename){
        reopen();
}

reopen(写入文件)

php复制代码bool FileLogAppender::reopen(){
    MutexType::Lock lock(m_mutex);
    if (m_filestream){
        m_filestream.close();
    }

    m_filestream.open(m_filename, std::ios::app);	//以追加的方式写入文件中
    return !!m_filestream;
}

log(输出到文件)

重写log方法,输出到文件

void FileLogAppender::log(std::shared_ptr<Logger> logger, LogLevel::Level level, LogEvent::ptr event){
    if (level >= m_level){
        uint64_t now = time(0);
        if (now != m_lastTime) {	//每秒重新reopen
            reopen();
            m_lastTime = now;
        }
        MutexType::Lock lock(m_mutex);
        if (!(m_filestream << m_formatter->format(logger, level, event))) {		//写到m_filestream流中
            std::cout << "error" << std::endl;
        }
    }
}

toYamlString(转化为YAML格式字符串)

重写toYamlString方法,转化为YAML格式字符串

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();
}

class Logger(日志器)

mumber(成员变量

    //日志名称
    std::string m_name;
    //日志级别
    LogLevel::Level m_level;
    // 互斥锁
    MutexType m_mutex;
    // 日志目标集合
    std::list<LogAppender::ptr> m_appenders;
    //日志格式器
    LogFormatter::ptr m_formatter;
    // root Log
    Logger::ptr m_root;

Logger(构造函数)

名称def = root

日志级别def = DEBUG

日志格式def = "%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n"

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"));
}

log(写不同Level日志到日志目标)

m_appenders为日志目标地,将当前logger输出到相应的appender,因为Appender的log要传入logger的智能指针,所以使用shared_from_this()获得当前logger的智能指针

void Logger::log(LogLevel::Level level, LogEvent::ptr event){
    if (level >= m_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);
            }
        } else if(m_root) {		//当logger的appenders为空时,使用root写logger
            m_root->log(level, event);
        }
    }	
}

addAppender(添加日志目标)

若appender没有formatter的话就将默认formatter赋给他,若有formatter则直接添加到m_appenders队列中

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);
}

delAppender(删除日志目标)

在m_appenders中找到要删除的appender,erase掉

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;
         }
    }
}

setFormatter(通过智能指针 )

将新的formatter赋给m_formatter,若appender没有formatter,则将appender的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;
        }
    }
}

setFormatter(通过字符串)

new一个新的formatter,若格式没错,调用上面的setFormatter设置Formatter。

void Logger::setFormatter(const std::string &val){
    sylar::LogFormatter::ptr new_val(new sylar::LogFormatter(val));
    if (new_val->isError()) {
         std::cout << "Logger setFormatter name = " << m_name
                   << "value = " << val << "invalid formatter"
                   << std::endl;
         return;
    }
    // m_formatter = new_val;
    setFormatter(new_val);
}

toYamlString(转换为YAML格式输出)

将当前logger name,level,formatter,appenders YAML格式按流输出

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();
}

class LoggerManager(日志管理器)

使用单列模式管理日志管理器,都要通过LoggerMgr来获得logger

typedef sylar::Singleton<LoggerManager> LoggerMgr;

mumber(成员变量)

// 互斥锁
    MutexType m_mutex;
    // 日志器容器
    std::map<std::string, Logger::ptr> m_loggers;
    // 主日志器
    Logger::ptr m_root;

LoggerManager(构造函数)

LoggerManager::LoggerManager() {
    m_root.reset(new Logger);
    m_root->addAppender(LogAppender::ptr(new StdoutLogAppender));

    m_loggers[m_root->m_name] = m_root;
}

getLogger(获取日志器)

在map中找到相应的logger就返回他,若没有就创建一个logger并将他放到日志器容器m_loggers中,再返回他

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;	//将logger的root赋值,当没有appender时,使用root写logger
    m_loggers[name] = logger;
    return logger;
}

toYamlString(将日志格式转化为YAML字符串)

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();
}

宏定义

使用流的方式,将不同日志级别的事件写入logger中

#define SYLAR_LOG_LEVEL(logger, level) \
    if (logger->getLevel() <= level) \
        sylar::LogEventWarp(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)

使用格式化方式, 将不同日志级别的事件写入logger中

#define SYLARY_LOG_FMT_LEVEL(logger, level, fmt, ...) \
    if (logger->getLevel() <= level) \
        sylar::LogEventWarp(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 SYLARY_LOG_FMT_DEBUG(logger, fmt, ...) SYLARY_LOG_FMT_LEVEL(logger, sylar::LogLevel::DEBUG, fmt, __VA_ARGS__)
#define SYLARY_LOG_FMT_INFO(logger, fmt, ...) SYLARY_LOG_FMT_LEVEL(logger, sylar::LogLevel::INFO, fmt, __VA_ARGS__)
#define SYLARY_LOG_FMT_WARN(logger, fmt, ...) SYLARY_LOG_FMT_LEVEL(logger, sylar::LogLevel::WARN, fmt, __VA_ARGS__)
#define SYLARY_LOG_FMT_ERROR(logger, fmt, ...) SYLARY_LOG_FMT_LEVEL(logger, sylar::LogLevel::ERROR, fmt, __VA_ARGS__)
#define SYLARY_LOG_FMT_FATAL(logger, fmt, ...) SYLARY_LOG_FMT_LEVEL(logger, sylar::LogLevel::FATAL, fmt, __VA_ARGS__)

获得主日志器

#define SYLAR_LOG_ROOT() sylar::LoggerMgr::GetInstance()->getRoot()

获得相应名字的日志器

#define SYLAR_LOG_NAME(name) sylar::LoggerMgr::GetInstance()->getLogger(name)

总结

这一个部分每个类之间的耦合度非常高。

sylar在写代码时使用了大量的宏定义,这个谁用谁知道啊,在后面的项目过程中,也大量使用了宏定义,让代码更加的简洁。

日志管理使用单例模式,保证从容器m_loggers中拿出的日志器是唯一不变的。日志管理器会初始化一个主日志器放到容器中,若再创建一个新的日志器时没有设置appender,则会使用这个主日志器进行日志的输出,但是输出时日志名称并不是主日志器的,因为在输出时是按照event中的logger的名称输出的。

最后在这里给大家再梳理一遍当一个日志器被定义再到打印出日志是怎么一个流程,可以边看代码边看,希望可以帮助大家更好的理解日志模块的代码。

  1. 首先我们使用日志管理器LoggerMgr获得一个logger,例如这里获得主日志器,可以使用宏SYLAR_LOG_ROOT()获得,例如sylar::Logger::ptr g_logger = SYLAR_LOG_ROOT()。此时LoggerManagernew一个新的Logger,默认为名字为rootlevelDEBUGformatter%d{%Y-%m-%d %H:%M:%S}%T%t%T%N%T%F%T[%p]%T[%c]%T%f:%l%T%m%n。并且addAppend()StdoutLogAppender,然后将root logger放入日志器容器中。此时,一个日志器就初始化好了。

  2. 在过程1中,在初始化g_logger时,它的formatter也会被init()方法初始化,并将解析对应的FormatItem按照格式顺序放到m_items中。在也会将StdoutLogAppender加到m_appenders中,在addAppender()时,若appender没有formatter时,会将g_loggerformatter赋给它。

  3. 当我们想打印日志时,需要创建相应日志级别的LogEvent,其中包含了时间 线程号 线程名称 协程号 [日志级别] [日志器名称] 文件名:行号 消息