日志系统——日志格式化模块设计

一,模块主要成员

该模块的主要作用是对日志消息进行格式化,将日志消息组织成制定格式的字符串

该模块主要成员有两个:1.格式化字符串。 2.格式化子项数组

1.1 格式化字符串

格式化字符串的主要功能是保存日志输出的格式字符串。其格式化字符主要有下面几种

格式化字符串定义了日志的输出格式,而格式化字符更有助于用户自由的控制日志的输出格式 。这里要注意的是由于我们信息中的日期为时间戳,因此在设置其格式化字符时还应设置对应的子格式化字符如[%d{%H:%M:%S}]指的是按照年月日输出时间

 1.2 格式化子项数组

格式化子项数组:用于按顺序保存格式化字符串对应的子格式化对象(FormatItem类)

那么什么是子格式化类(FormatItem类)?子格式化类主要负责日志消息子项的获取以及格式化,其下包含诸多子类,通过继承关系实现多态,即使是不同的子类也能依靠多态由相同的父类指针指向,从而保存到同一个vector中,Formatltem主要有以下子类:

 格式化字符,以及格式化子项的关系如下,这里我们以日期为例子

 

 

1.3 举例说明

例子:"[%d{%H:%M:%S}] %m%n"

pattern = "[%d{%H:%M:%S}] %m%n"
items = {{OtherFormatItem(), "["},{TimeFormatItem(), "%H:%M:%S"},{OtherFormatItem(), "]"},{MsgFormatItem (), ""},{NLineFormatItem (), ""}
}
message msg = {size_t _line = 22;size_t _ctime = 12345678;std::thread::id _tid = 0x12345678;std::string _logger = "logger";std::string _file = "main.cpp";std::string _payload = "创建套接字失败";LogLevel::level _level = ERROR;
};

格式化的过程其实就是按次序从Msg中取出需要的数据进⾏字符串的连接的过程。

最终组织出来的格式化消息: "[22:32:54] 创建套接字失败\n"

 二,代码实现

2.1 代码

核心代码bool AnalyPattern()的分析如下

 

 

#ifndef _M_FORMAT_H_
#define _M_FORMAT_H_#include "level.hpp"
#include "message.hpp"
#include "util.hpp"
#include <vector>
#include <memory>
#include <sstream>namespace mjwlog
{// 格式化子项基类class Formatlem{public:using ptr=std::shared_ptr<Formatlem>;virtual void format(std::ostream &os, const message& msg) = 0;};// 格式化子项子类// 日期类子类class TimeFormatlem : public Formatlem{public:TimeFormatlem(std::string format="%H:%M:%S"):_fromat(format){}void format(std::ostream &os, const message& msg) override{struct tm time;localtime_r((time_t*)&msg._time,&time);char str[32];strftime(str,sizeof(str)-1,_fromat.c_str(),&time);os<<str;}private:std::string _fromat;//用来控制输出格式};//缩进子类class TabFormatlem : public Formatlem{public:void format(std::ostream &os, const message& msg) override{os<<"\t";}};//线程id子类class ThreadFormatlem : public Formatlem{public:void format(std::ostream &os, const message& msg) override{os<<msg._id;}};//日志级别子类class LevelFormatlem : public Formatlem{public:void format(std::ostream &os, const message& msg) override{os<<LogLevel::LeveltoString(msg._level);}};//日志器名称子类class NameFormatlem : public Formatlem{public:void format(std::ostream &os, const message& msg) override{os<<msg._logger;}};//文件名子类class CFileFormatlem : public Formatlem{public:void format(std::ostream &os, const message& msg) override{os<<msg._filename;}};//行号子类class CLineFormatlem : public Formatlem{public:void format(std::ostream &os, const message& msg) override{os<<msg._line;}};//日志消息主体子类class MsgFormatlem : public Formatlem{public:void format(std::ostream &os, const message& msg) override{os<<msg._msg;}};//换行子类class NLineFormatlem : public Formatlem{public:void format(std::ostream &os, const message& msg) override{os<<"\n";}};//其他字符子类,直接原路返回class OtherFormatlem : public Formatlem{public:OtherFormatlem(std::string str):_str(str){}void format(std::ostream &os, const message& msg) override{os<<_str;}private:std::string _str;};//格式化类class Formatter{/*%d 日期%T 缩进%t 线程id%p 日志级别%c 日志器名称%f 文件名%l 行号%m 日志消息%n 换行*/public://默认格式:时间{年-月-日 时:分:秒}缩进 线程ID 缩进 [日志级别] 缩进 [日志器名称] 缩进 文件名:行号 缩进 消息换行Formatter(std::string pattern="[%d{%H:%M:%S}]%T[%t]%T[%p]%T[%c]%T[%f:%l]%T%n"):_pattern(pattern){}//对msg进行格式化nvoid format(std::ostream& os,const message& msg)//将格式化后的内容写入os执行流{if(AnalyPattern()){for(auto& it:_items){it->format(os,msg);}}}std::string format(const message& msg)//将格式化后的内容以string方式返回{std::stringstream str;if(AnalyPattern()){for(auto& it:_items){it->format(str,msg);}}return str.str();}//对格式化字符串进行分析bool AnalyPattern(){//遍历_parttern进行逐一分析int cur=0;std::string key="",val="";//"abc[%%d{%H:%M:%S}]"while(cur<_pattern.size()){//先排查非格式化字符,如abcif(_pattern[cur]!='%'){val+=_pattern[cur++];//特殊情况:"aaaa[%%d{%H:%M:%S}]"if(cur==_pattern.size()){_items.push_back(createItem(key,val));}continue;}//到这一步,说明碰到%,这是需要排除"%[" "%%"等情况//"%["//if(_pattern[cur]=='%'&&_pattern[cur+1]=='%')if(_pattern[cur]=='%'&&(!isalpha(_pattern[cur+1]))){val+=_pattern[cur++];//特殊情况:"aaaa[%%d{%H:%M:%S}%"/* if(cur==_pattern.size()){_items.push_back(createItem(key,val));} */continue;}//此时,key为空,val中全是非格式化字符字符//不过也有可能val中没有任何字符if(!val.empty()) _items.push_back(createItem(key,val));key.clear();val.clear();//cur+1==_pattern.size()if(cur+1==_pattern.size()){std::cout<<"格式化字符关键字%后数据错误"<<std::endl;return false;}//到这一步,说明目前cur指向%,且cur+1指向的为一个字母字符cur=cur+1;key+=_pattern[cur++];//"abc[%%d{%H:%M:%S}]",此时cur指向d后面的{//因此,这时候我们需要判断后面是否是子格式化字符if(cur<_pattern.size()&&_pattern[cur]=='{'){cur++;while(cur<_pattern.size()&&_pattern[cur]!='}'){val+=_pattern[cur++];}//出while循环有下面两种可能//1.cur==_pattern.size(),说明没有找到'}',说明这是非法子格式化字符if(cur==_pattern.size()){std::cout<<"非法子格式化字符...."<<std::endl;abort();}//2._pattern[cur]=='}'_items.push_back(createItem(key,val));key.clear();val.clear();//此时cur指向},因此进入下一个循环前cur应该++cur++;}else{_items.push_back(createItem(key,val));key.clear();val.clear();}}return true;}private://根据不同的格式创建不同的格式化子类对象//key用来存储格式化字符,val用来存储格式化字符子字符如[%d{%H:%M:%S}]std::shared_ptr<Formatlem> createItem(const std::string& key,const std::string& val){if(key=="d") return Formatlem::ptr(new TimeFormatlem(val));if(key=="T") return Formatlem::ptr(new TabFormatlem());if(key=="p") return Formatlem::ptr(new LevelFormatlem());if(key=="t") return Formatlem::ptr(new ThreadFormatlem());if(key=="c") return Formatlem::ptr(new NameFormatlem());if(key=="f") return Formatlem::ptr(new CFileFormatlem());if(key=="l") return Formatlem::ptr(new CLineFormatlem());if(key=="n") return Formatlem::ptr(new NLineFormatlem());//当key为空时,则说明val里存储的是非格式化化字符,如abcdif(key.empty()) return Formatlem::ptr(new OtherFormatlem(val));//还有一种情况,就是key中存储的不是设定的格式字符,如%gstd::cout<<"没有该格式化字符:%"<<key<<std::endl;abort();return nullptr;}private:std::string _pattern;std::vector<Formatlem::ptr> _items;};
}#endif

3.1 测试

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/41834.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WPF 界面结构化处理

文章目录 概要一、xaml界面结构化处理二、逻辑树与视觉树 概要 WPF 框架是开源的&#xff0c;但是不能跨平台&#xff0c;可以使用MAUI&#xff0c;这个框架可以跨平台&#xff0c;WPF源码可以在github上下载&#xff0c;下载地址&#xff1a;https://gitbub.com/dotnet/wpf。…

【C++ 记忆站】命名空间

文章目录 命名空间概念命名空间的定义1、正常的命名空间定义2、命名空间可以嵌套3、同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中 命名空间的使用1、加命名空间名称及作用域限定符2、使用using将命名空间中某个成员引入3、使用using namespac…

初试时间官宣!研招网发布下半年重要时间节点!今日速报来了

距24考研初试还有127天&#xff0c;今天给大家带来初试和报名时间官宣消息、考研报名注意事项、研招网发布的2024考研“保姆级”下半年重要时间节点。有用记得收藏 24考研报名和初试时间官宣 已有学校在招生简章中明确24考研初试时间 初试时间预计为&#xff1a;2023年12月23…

初试rabbitmq

rabbitmq的七种模式 Hello word 客户端引入依赖 <!--rabbitmq 依赖客户端--><dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><version>5.8.0</version></dependency> 生产者 imp…

邀请函|澎峰科技邀您参加CCF HPC China2023

一年一度的全球超算盛会&#xff01; 以“算力互联智领未来”为主题的第十九届全国高性能计算学术年会&#xff08;CCF HPC China 2023&#xff09;将于8月24-26日&#xff08;展览23-25日&#xff09;在青岛红岛国际会议展览中心举办。 九大院士领衔 打造顶级超算盛会 力邀…

《离散数学及其应用(原书第8版)》ISBN978-7-111-63687-8 第11章 11.1.3 树的性质 节 第664页的例9说明

《离散数学及其应用&#xff08;原书第8版&#xff09;》ISBN978-7-111-63687-8 第11章 11.1.3 树的性质 节 第664页的定理3的引申 定理3 带有i个内点的m叉树含有nmi1个顶点 见本人博文 内点定义不同的讨论 如果对于一个m叉正则树&#xff0c;即任意分支节点的儿子恰好有m个&am…

谈谈IP地址和子网掩码的概念及应用

个人主页&#xff1a;insist--个人主页​​​​​​ 本文专栏&#xff1a;网络基础——带你走进网络世界 本专栏会持续更新网络基础知识&#xff0c;希望大家多多支持&#xff0c;让我们一起探索这个神奇而广阔的网络世界。 目录 一、IP地址的概念 二、IP地址的分类 1、A类 …

长胜证券:散户可以随大流吗?怎么做才好?

在我国的股市里边&#xff0c;最不缺的或许便是散户了&#xff0c;一方面&#xff0c;散户促进了股市的活泼&#xff0c;可一方面又特容易望风而动&#xff0c;追涨杀跌。因此&#xff0c;散户能够随大流吗&#xff1f;该怎么做才好&#xff1f;对于这些&#xff0c;长胜证券为…

IntelliJ IDEA热部署:JRebel插件的安装与使用

热部署 概述JRebel 概述 热部署&#xff0c;指修改代码后&#xff0c;无需停止应用程序&#xff0c;即可使修改后的代码生效&#xff0c;其有利于提高开发效率。 热部署方式&#xff1a; 手动热部署&#xff1a;修改代码后&#xff0c;重新编译项目&#xff0c;然后启动应用程…

Springboot项目启动后按顺序加载自定义类 (demo)

1. 实现ApplicationRunner接口, 重写run方法 import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.core.annotation.Order; import org.springframewor…

【C语言】const修饰普通变量和指针

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解const修饰普通变量和指针&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 文章目录 一.const修饰普通变量二.const修饰指针1.const 放在 * 左边2.const 放在…

Nacos - 安装指南(Windows系统)

一、下载安装包 Nacos现在虽然已经出到二点几的版本&#xff0c;但二点几版本还处在测试阶段&#xff0c;我们选择下载成熟的 1.4.6 版本 下载地址&#xff1a;Nacos 1.4.6 GitHub的Release下载页 拉到页面最底部&#xff0c;可以看到下载按钮&#xff0c;windows版本使用naco…

htmlCSS-----弹性布局

目录 前言 什么是弹性布局 样式 学习概要 容器和项目 弹性布局的排列方式 1.横向排列&#xff08;默认样式&#xff09; 2.父元素容器的属性&#xff08;*5&#xff09; &#xff08;1&#xff09;主轴 代码示例&#xff1a; &#xff08;2&#xff09;交叉轴 3.子元素…

正则表达式试炼

序 我希望在这里列出我很多想写的正则表达式&#xff0c;很多我想写&#xff0c;但是不知道怎么写的。分享点滴案例。未来这个文章会越来越长 前言 互联网时代&#xff0c;除了文本还有更好的学习方式&#xff0c;下面是几个不错的练习网站&#xff0c;如果你想系统地学习&a…

【Linux】【驱动】应用层和驱动层传输数据

【Linux】【驱动】应用层和驱动层传输数据 绪论1.如果我在应用层使用系统0 对设备节点进行打开&#xff0c;关闭&#xff0c;读写等操作会发生什么呢? 2 我们的应用层和内核层是不能直接进行数据传输的3 驱动部分的代码4 应用代码5 编译以及运行代码 绪论 Linux一切皆文件! 文…

如何使用CSS实现一个下拉菜单?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 使用CSS实现下拉菜单⭐ HTML 结构⭐ CSS 样式⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些…

学习笔记」左偏树

dist 的性质 对于一棵二叉树&#xff0c;我们定义左孩子或右孩子为空的节点为外节点&#xff0c;定义外节点的 distdist 为 11&#xff0c;空节点的 distdist 为 00&#xff0c;不是外节点也不是空节点的 distdist 为其到子树中最近的外节点的距离加一。 一棵根的 distdist 为…

DevOps系列文章之 GitlabCICD自动化部署SpringBoot项目

一、概述 本文主要记录如何通过Gitlab CI/CD自动部署SpringBoot项目jar包。 二、前期准备 准备三台 CentOS7服务器&#xff0c;分别部署以下服务&#xff1a; 序号系统IP服务1CentOS7192.168.56.10Gitlab2CentOS7192.168.56.11Runner &#xff08;安装Docker&#xff09;3Cen…

Spring boot中的线程池-ThreadPoolTaskExecutor

一、jdk的阻塞队列&#xff1a; 二、Spring boot工程的有哪些阻塞队列呢&#xff1f; 1、默认注入的ThreadPoolTaskExecutor 视频解说&#xff1a; 线程池篇-springboot项目中的service层里简单注入ThreadPoolTaskExecutor并且使用_哔哩哔哩_bilibili 程序代码&#xff1a;…

预测算法|改进粒子群算法优化极限学习机IDM-PSO-ELM

回归拟合&#xff1a; 分类 本文是作者的预测算法系列的第四篇&#xff0c;前面的文章中介绍了BP、SVM、RF及其优化&#xff0c;感兴趣的读者可以在作者往期文章中了解&#xff0c;这一篇将介绍——极限学习机 过去的几十年里基于梯度的学习方法被广泛用于训练神经网络&am…