站在巨人的肩膀上
C++高性能分布式服务器框架
从零开始重写sylar C++高性能分布式服务器框架
概述
- 仿照 Log4pp 写的日志模块。
- 提供宏定义给外界使用。
LogLevel
- 日志级别封装类。
- 提供“从日志级别枚举值转换到字符串”、“从字符串转换相应的日志级别枚举值”等方法。
LogEvent
- 日志事件类。
- 封装日志事件的属性,例如时间、线程id、日志等级、内容等等,并对外提供访问方法。
- 日志事件的构造在使用上会通过宏定义来简化。
LogEventWrap
- 日志事件包装类,内含日志事件 LogEvent。
- 该类的作用主要是便于编写宏,方便外界使用。
- 日志事件在析构时由日志器进行输出。
LogFormatter
- 日志格式类。
- 通过传递日志样式字符串给该类,该类对传入的字符串进行解析,例如 %d%t%p%m%n 表示 时间、线程号、日志等级、内容、换行。
- 内含一个虚基类-日志内容格式化项 FormatItem,像时间、线程号、日志等级、内容、换行等这些日志格式项都有其对应的子类,其子类正是继承 FormatItem。有13个子类,
- 消息-MessageFormatItem、
- 日志级别-LevelFormatItem、
- 累计毫秒数-ElapseFormatItem、
- 日志名称-NameFormatItem、
- 线程id-ThreadIdFormatItem、
- 换行-NewLineFormatItem、
- 时间-DateTimeFormatItem、
- 文件名-FilenameFormatItem、
- 行号-LineFormatItem、
- Tab-TabFormatItem、
- 协程id-FiberIdFormatItem、
- 线程名称-ThreadNameFormatItem、
- 直接打印字符串-StringFormatItem。
- 内含一个 vector,存放解析后的格式。vector 存放的是基类 FormatItem 的智能指针。
- 整个日志模块最复杂的逻辑就是该类解析日志样式的函数 init()。
- LogFormatter 提供 format 方法给外界访问,format 方法有两个形式(重载),传入参数 Logger、LogLevel、LogEvent,传出参数是格式化后的字符串。通过遍历存放在 vector 里面的日志样式,对日志内容进行格式化。
LogAppender
- 日志输出目的地类。
- LogAppender 为虚基类,有纯虚函数,留给子类去各自实现。实现的子类如 StdoutLogAppender 和 FileLogAppender。
- Appender 自带一个默认的 LogFormatter,以默认方式输出。
- StdoutLogAppender
- FileLogAppender
Logger
- 日志器类。
- 设置日志名称、设置日志等级 LogLevel、设置日志输出位置 LogAppender、设置日志格式、根据日志级别控制日志输出等。
LoggerManager
- 日志管理器类。
- 利用 map 存放各个 Logger 实例,其中 key 是日志器的名称,value 是日志器的智能指针。
- 还内含一个主日志器 root。
其他说明
- 每个类都 typedef std::shared_ptr ptr,方便外界使用其智能指针。
- 普遍使用 Spinlock 实现互斥,保证线程安全。Spinlock 比 普通的 Mutex 效率高,但耗CPU。
数据流转
// 以使用流式方式写日志举例:
通过宏SYLAR_LOG_NAME(name)从LoggerMgr取得对应的Logger,假设是g_logger
|
|
\|/
通过宏SYLAR_LOG_DEBUG(logger)->SYLAR_LOG_LEVEL(logger, level)
|
|
\|/
宏SYLAR_LOG_LEVEL new出一个LogEvent,然后再在该实例传给LogEventWrap,从而创建出LogEventWrap临时对象,从该临时对象拿到std::stringstream
|
|
\|/
通过上面拿到的std::stringstream,用<<操作符即可把日志内容存入std::stringstream
|
|
\|/
LogEventWrap临时对象析构时,将通过构造时传入的LogEvent拿到其中的Logger,在调用Logger的log方法
|
|
\|/
Logger的log方法是遍历自己的LogAppender,调用每个LogAppender的log方法(传入logger,level,event参数),假设这里是FileLogAppender
|
|
\|/
LogAppender的log方法是加上自己的std::ostream参数(如果是输出到控制台,则是std::cout)后,调用LogFormatter的format方法(传入std::ostream,logger,level,event参数)。
|
|
\|/
LogFormatter的format方法是遍历自己缓存的所有FormatItem(继承了FormatItem的各种子类智能指针),将日志内容格式化(例如加上时间日期、线程id等)。调用的是每个FormatItem的format方法(传入std::ostream,logger,level,event参数)
|
|
\|/
每个FormatItem的format方法是格式化后的内容以流式方式存入std::ostream。如果是输出到控制台,那么这里就直接输出了。如果是文件,因为std::ostream关联了文件,因此会对文件进行缓存写(非实时写)。
待完善
部分相关代码
/**
* @filename log.h
* @brief 日志模块
* @author L-ge
* @version 0.1
* @modify 2022-06-11
*/
#ifndef __SYLAR_LOG_H__
#define __SYLAR_LOG_H__
#include <string>
#include <memory>
#include <sstream>
#include <fstream>
#include <list>
#include <vector>
#include <map>
#include <stdarg.h>
#include <iostream>
#include "mutex.h"
#include "util.h"
#include "singleton.h"
#include "thread.h"
/**
* @brief 使用流式方式写日志
*/
#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)
/**
* @brief 使用格式化方式写日志
*/
#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__)
/**
* @brief 获取主日志器
*/
#define SYLAR_LOG_ROOT() sylar::LoggerMgr::GetInstance()->getRoot()
/**
* @brief 获取name的日志器
*/
#define SYLAR_LOG_NAME(name) sylar::LoggerMgr::GetInstance()-&g