协程库项目—日志模块

2024-03-03 01:20
文章标签 模块 日志 项目 协程库

本文主要是介绍协程库项目—日志模块,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

日志模块程序结构图

sylarLog
├── LogLevel(日志级别封装类)
│   ├── 提供“从日志级别枚举值转换到字符串”、“从字符串转换相应的日志级别枚举值”等方法
├── LogEvent(日志事件类)
│   ├── 封装日志事件的属性,例如时间、线程id、日志等级、内容等等,并对外提供访问方法
│   └── 日志事件的构造在使用上会通过宏定义来简化
├── LogEventWrap(日志事件包装类)
│   ├── 内含日志事件 LogEvent
│   └── 日志事件在析构时由日志器进行输出
├── LogFormatter(日志格式类)
│   ├── 通过传递日志样式字符串给该类,该类对传入的字符串进行解析,例如 %d%t%p%m%n 表示 时间、线程号、日志等级、内容、换行
│   ├── 内含一个虚基类-日志内容格式化项 FormatItem
│   ├── 有13个子类,消息-MessageFormatItem、日志级别-LevelFormatItem、累计毫秒数-ElapseFormatItem、日志名称-NameFormatItem、线程id-ThreadIdFormatItem、换行-NewLineFormatItem、时间-DateTimeFormatItem、文件名-FilenameFormatItem、行号-LineFormatItem、Tab-TabFormatItem、协程id-FiberIdFormatItem、线程名称-ThreadNameFormatItem、直接打印字符串-StringFormatItem
│   └── 整个日志模块最复杂的逻辑就是该类解析日志样式的函数 init()
├── 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。

数据流转

  1. 首先通过SYLAR_LOG_NAME(name)宏从LoggerMgr中获取对应的Logger对象,然后通过SYLAR_LOG_DEBUG(logger)->SYLAR_LOG_LEVEL(logger,level)宏创建一个新的LogEvent对象,并将其传递给LogEventWrap临时对象。接着,通过std::stringstream将日志内容存入其中。
  2. 当LogEventWrap临时对象析构时,会调用Logger的log方法,遍历其所有的LogAppender,并调用每个LogAppender的log方法(传入event参数)。
  3. 这里以FileLogAppender为例,LogAppender的log方法会加上自己的std::ostream参数(如果是输出到控制台,则是std::cout),然后调用LogFormatter的format方法(传入ostream、event参数)。
  4. LogFormatter的format方法会遍历自己缓存的所有FormatItem(继承了FormatItem的各种子类智能指针),将日志内容格式化(例如加上时间日期、线程id等)。
  5. 调用的是每个FormatItem的format方法(传入ostream、event参数)。最后,每个FormatItem的format方法会将格式化后的内容以流式方式存入std::ostream。如果是输出到控制台,那么这里就直接输出了。如果是文件,因为std::ostream关联了文件,因此会对文件进行缓存写(非实时写)。

LogFormatter类的init方法

LogFormatter类的init方法,用于解析日志格式字符串。主要功能如下:

首先,定义了一个patterns向量,用于存储解析到的模式项。每个模式项包括一个整数类型和一个字符串,类型为0表示该模式是常规字符串,为1表示该模式需要转义。
定义了一个临时变量tmp,用于存储常规字符串。
定义了一个日期格式字符串dateformat,默认把位于%d后面的大括号对里的全部字符都当作格式字符,不校验格式是否合法。
定义了一个布尔变量error,用于标记解析过程中是否出错。
定义了两个布尔变量parsing_string和parsing_pattern,分别表示是否正在解析常规字符和模板字符。初始时,parsing_string为true。
使用一个循环遍历m_pattern字符串中的每个字符,根据不同的情况进行解析。
如果当前字符是"%“,则根据parsing_string的值进行不同的处理。如果正在解析常规字符,则将之前的常规字符串添加到patterns中,并将parsing_string设置为false;如果正在解析模板字符,则将当前的”%“作为模板字符添加到patterns中,并将parsing_string设置为true。
如果当前字符不是”%“,则根据parsing_string的值进行不同的处理。如果正在解析常规字符,则将当前字符添加到tmp中;如果正在解析模板字符,则将当前字符作为模板字符添加到patterns中,并根据不同情况进行特殊处理。
在解析模板字符的过程中,如果遇到”%d",则需要进一步解析日期格式字符串。通过遍历后续字符,直到找到闭合的大括号,将其中的字符添加到dateformat中。
如果在解析过程中出现错误,将m_error设置为true并返回。
最后,根据解析得到的模式项创建相应的格式化项对象,并将其添加到m_items中。
如果解析过程中出现错误,将m_error设置为true并返回。

void LogFormatter::init() {// 按顺序存储解析到的pattern项// 每个pattern包括一个整数类型和一个字符串,类型为0表示该pattern是常规字符串,为1表示该pattern需要转义// 日期格式单独用下面的dataformat存储std::vector<std::pair<int, std::string>> patterns;// 临时存储常规字符串std::string tmp;// 日期格式字符串,默认把位于%d后面的大括号对里的全部字符都当作格式字符,不校验格式是否合法std::string dateformat;// 是否解析出错bool error = false;// 是否正在解析常规字符,初始时为truebool parsing_string = true;// 是否正在解析模板字符,%后面的是模板字符// bool parsing_pattern = false;size_t i = 0;while(i < m_pattern.size()) {std::string c = std::string(1, m_pattern[i]);if(c == "%") {if(parsing_string) {if(!tmp.empty()) {patterns.push_back(std::make_pair(0, tmp));}tmp.clear();parsing_string = false; // 在解析常规字符时遇到%,表示开始解析模板字符// parsing_pattern = true;i++;continue;} else {patterns.push_back(std::make_pair(1, c));parsing_string = true; // 在解析模板字符时遇到%,表示这里是一个%转义// parsing_pattern = false;i++;continue;}} else { // not %if(parsing_string) { // 持续解析常规字符直到遇到%,解析出的字符串作为一个常规字符串加入patternstmp += c;i++;continue;} else { // 模板字符,直接添加到patterns中,添加完成后,状态变为解析常规字符,%d特殊处理patterns.push_back(std::make_pair(1, c));parsing_string = true; // parsing_pattern = false;// 后面是对%d的特殊处理,如果%d后面直接跟了一对大括号,那么把大括号里面的内容提取出来作为dateformatif(c != "d") {i++;continue;}i++;if(i < m_pattern.size() && m_pattern[i] != '{') {continue;}i++;while( i < m_pattern.size() && m_pattern[i] != '}') {dateformat.push_back(m_pattern[i]);i++;}if(m_pattern[i] != '}') {// %d后面的大括号没有闭合,直接报错std::cout << "[ERROR] LogFormatter::init() " << "pattern: [" << m_pattern << "] '{' not closed" << std::endl;error = true;break;}i++;continue;}}} // end while(i < m_pattern.size())if(error) {m_error = true;return;}// 模板解析结束之后剩余的常规字符也要算进去if(!tmp.empty()) {patterns.push_back(std::make_pair(0, tmp));tmp.clear();}// for debug // std::cout << "patterns:" << std::endl;// for(auto &v : patterns) {//     std::cout << "type = " << v.first << ", value = " << v.second << std::endl;// }// std::cout << "dataformat = " << dateformat << std::endl;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(c, LoggerNameFormatItem),        // c:日志器名称
//        XX(d, DateTimeFormatItem),          // d:日期时间XX(r, ElapseFormatItem),            // r:累计毫秒数XX(f, FileNameFormatItem),          // f:文件名XX(l, LineFormatItem),              // l:行号XX(t, ThreadIdFormatItem),          // t:编程号XX(F, FiberIdFormatItem),           // F:协程号XX(N, ThreadNameFormatItem),        // N:线程名称XX(%, PercentSignFormatItem),       // %:百分号XX(T, TabFormatItem),               // T:制表符XX(n, NewLineFormatItem),           // n:换行符
#undef XX};//根据解析得到的模式项创建相应的格式化项对象,并将其添加到m_items中。for(auto &v : patterns) {if(v.first == 0) {m_items.push_back(FormatItem::ptr(new StringFormatItem(v.second)));} else if( v.second =="d") {m_items.push_back(FormatItem::ptr(new DateTimeFormatItem(dateformat)));} else {auto it = s_format_items.find(v.second);if(it == s_format_items.end()) {std::cout << "[ERROR] LogFormatter::init() " << "pattern: [" << m_pattern << "] " << "unknown format item: " << v.second << std::endl;error = true;break;} else {m_items.push_back(it->second(v.second));}}}if(error) {m_error = true;return;}
}

这篇关于协程库项目—日志模块的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/768018

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11 二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

Vue3项目开发——新闻发布管理系统(六)

文章目录 八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染 4、退出功能实现①注册点击事件②添加退出功能③数据清理 5、代码下载 八、首页设计开发 登录成功后,系统就进入了首页。接下来,也就进行首页的开发了。 1、页面设计 系统页面主要分为三部分,左侧为系统的菜单栏,右侧

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0

我在移动打工的日志

客户:给我搞一下录音 我:不会。不在服务范围。 客户:是不想吧 我:笑嘻嘻(气笑) 客户:小姑娘明明会,却欺负老人 我:笑嘻嘻 客户:那我交话费 我:手机号 客户:给我搞录音 我:不会。不懂。没搞过。 客户:那我交话费 我:手机号。这是电信的啊!!我这是中国移动!! 客户:我不管,我要充话费,充话费是你们的 我:可是这是移动!!中国移动!! 客户:我这是手机号 我:那又如何,这是移动!你是电信!!

SpringBoot项目是如何启动

启动步骤 概念 运行main方法,初始化SpringApplication 从spring.factories读取listener ApplicationContentInitializer运行run方法读取环境变量,配置信息创建SpringApplication上下文预初始化上下文,将启动类作为配置类进行读取调用 refresh 加载 IOC容器,加载所有的自动配置类,创建容器在这个过程

Maven创建项目中的groupId, artifactId, 和 version的意思

文章目录 groupIdartifactIdversionname groupId 定义:groupId 是 Maven 项目坐标的第一个部分,它通常表示项目的组织或公司的域名反转写法。例如,如果你为公司 example.com 开发软件,groupId 可能是 com.example。作用:groupId 被用来组织和分组相关的 Maven artifacts,这样可以避免