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

一,模块主要成员

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

该模块主要成员有两个: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进行格式化n

        void 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())
            {
                
                //先排查非格式化字符,如abc
                if(_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里存储的是非格式化化字符,如abcd
            if(key.empty()) return Formatlem::ptr(new OtherFormatlem(val));
            //还有一种情况,就是key中存储的不是设定的格式字符,如%g
            std::cout<<"没有该格式化字符:%"<<key<<std::endl;
            abort();

            return nullptr;
        }
    private:
        std::string _pattern;
        std::vector<Formatlem::ptr> _items;
    };
}

#endif

3.1 测试

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

机器学习赋能乳腺癌预测:如何使用贝叶斯分级进行精确诊断?

一、引言 乳腺癌是女性最常见的恶性肿瘤之一&#xff0c;也会发生在男性身上。每年全球有数百万人被诊断出乳腺癌&#xff0c;对患者的生活和健康造成了巨大的影响。早期的乳腺癌检测和准确的诊断对于提高治疗的成功率至关重要。然而&#xff0c;乳腺癌的早期诊断面临着许多挑战…

大语言模型与语义搜索;钉钉个人版启动内测,提供多项AI服务

&#x1f989; AI新闻 &#x1f680; 钉钉个人版启动内测&#xff0c;提供多项AI服务 摘要&#xff1a;钉钉个人版正式开始内测&#xff0c;面向小团队、个人用户、高校大学生等人群。该版本具有AI为核心的功能&#xff0c;包括文生文AI、文生图AI和角色化对话等。用户可通过…

整理mongodb文档:find方法查询数据

个人博客 整理mongodb文档:find方法查询数据 求关注&#xff0c;求批评&#xff0c;求指出&#xff0c;如果哪儿不清晰&#xff0c;请指出来&#xff0c;谢谢 文章概叙 如题&#xff0c;本文讲的是如何用find查询数据&#xff0c;如何在数组、字段、对象中查询&#xff0c;以…

Git 常用操作

一、Git 常用操作 1、Git 切换分支 git checkout命令可以用于三种不同的实体&#xff1a;文件&#xff0c;commit&#xff0c;以及分支。checkout的意思就是对于一种实体的不同版本之间进行切换的操作。checkout一个分支&#xff0c;会更新当前的工作空间中的文件&#xff0c;…

Web菜鸟教程 - Springboot接入认证授权模块

网络安全的重要性不言而喻&#xff0c;如今早已不是以前随便弄个http请求就能爬到数据的时代&#xff0c;而作为一个架构师&#xff0c;网络安全必须在产品开发之初就考虑好。因为在产品开发的后期&#xff0c;一方面是客户增多&#xff0c;压力变大&#xff0c;可供利用的时间…

JupyterHub实战应用

一、JupyerHub jupyter notebook 是一个非常有用的工具&#xff0c;我们可以在浏览器中任意编辑调试我们的python代码&#xff0c;并且支持markdown 语法&#xff0c;可以说是科研利器。但是这种情况适合个人使用&#xff0c;也就是jupyter notebook以我们自己的主机作为服务器…

iOS设计规范是什么?都有哪些具体规范

iOS设计规范是苹果为移动设备操作系统iOS制定的设计指南。iOS设计规范的制定保证了苹果应用在外观和操作上的一致性和可用性&#xff0c;从而提高了苹果界面设计的用户体验和应用程序的成功性。本文将从七个方面全面分析iOS设计规范。 1.iOS设计规范完整版分享 由「即时设计」…

容器和云原生(二):Docker容器化技术

目录 Docker容器的使用 Docker容器关键技术 Namespace Cgroups UnionFS Docker容器的使用 首先直观地了解docker如何安装使用&#xff0c;并快速启动mysql服务的&#xff0c;启动时候绑定主机上的3306端口&#xff0c;查找mysql容器的ip&#xff0c;使用mysql -h contain…

设计模式——建造者(Builder)模式

建造者模式&#xff08;Builder Pattern&#xff09;&#xff0c;又叫生成器模式&#xff0c;是一种对象构建模式 它可以将复杂对象的建造过程抽象出来&#xff0c;使这个抽象过程的不同实现方法可以构造出不同表现的对象。建造者模式是一步一步创建一个复杂的对象&#xff0c;…

Python文件操作教程,Python文件操作笔记

文件的打开与关闭 想一想&#xff1a; 如果想用word编写一份简历&#xff0c;应该有哪些流程呢&#xff1f; 打开word软件&#xff0c;新建一个word文件写入个人简历信息保存文件关闭word软件 同样&#xff0c;在操作文件的整体过程与使用word编写一份简历的过程是很相似的…

使用 BERT 进行文本分类 (01/3)

摄影&#xff1a;Max Chen on Unsplash 一、说明 这是使用 BERT 语言模型的一系列文本分类演示的第一部分。以文本的分类作为例&#xff0c;演示它们的调用过程。 二、什么是伯特&#xff1f; BERT 代表 来自变压器的双向编码器表示。 首先&#xff0c;转换器是一种深度学习模…

【Linux命令详解 | gzip命令】 gzip命令用于压缩文件,可以显著减小文件大小

文章标题 简介一&#xff0c;参数列表二&#xff0c;使用介绍1. 基本压缩和解压2. 压缩目录3. 查看压缩文件内容4. 测试压缩文件的完整性5. 强制压缩6. 压缩级别7. 与其他命令结合使用8. 压缩多个文件9. 自动删除原文件 总结 简介 在Linux中&#xff0c;gzip命令是一款强大的文…

C语言笔试训练【第九天】

文章目录 &#x1f47f;1、下列程序的输出是&#xff08; &#xff09;&#x1f48e;2、二维数组X按行顺序存储&#xff0c;其中每个元素占1个存储单元。若 X[4][4] 的存储地址为 Oxf8b82140 , X[9][9] 的存储地址为 Oxf8b8221c ,则 X[7][7] 的存储地址为&#xff08; &#xf…

交换实验一

题目 交换机上接口配置 SW1 interface GigabitEthernet0/0/1 port hybrid tagged vlan 2 port hybrid untagged vlan 3 to 6 interface Ethernet0/0/2 port hybrid pvid vlan 3 port hybrid untagged vlan 2 to 6 interface Ethernet0/0/3 port link-type access port d…

C++基础语法——继承

1.继承是什么&#xff1f; 继承是一种面向对象编程的概念&#xff0c;它允许一个类&#xff08;称为子类或派生类&#xff09;从另一个类&#xff08;称为基类或父类&#xff09;继承属性和方法。继承使得子类能够使用基类已有的代码&#xff0c;并且可以在此基础上进行扩展或修…

99页4万字XX大数据湖项目建设方案

导读&#xff1a;原文《99页4万字XX大数据湖项目建设方案》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 目 录 1.项目综述 1.1.项目背景 1.2.项目目标 1.3.项…

Error = [Microsoft][SQL Server Native Client 10.0]无法打开 BCP 主数据文件(已解决)

需求&#xff1a;分页方式查出表中数据存入excel中&#xff0c;并给excel标记不同的编号 前提&#xff1a; #all_4表中数据已准备好 版本&#xff1a; sql server 2008 declare n int set n 1 while n 100 begin print n:print nEXEC master.dbo.sp_configure show adva…

什么是flexbox布局?它有什么特点和优势?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 什么是 Flexbox 布局&#xff1f;⭐ 特点和优势⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那…

(三)行为模式:1、责任链模式(Chain of Responsibility Pattern)(C++示例)

目录 1、责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;含义 2、责任链模式的UML图学习 3、责任链模式的应用场景 4、责任链模式的优缺点 5、C实现责任链模式的实例 1、责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;含义 责任…

2682. 找出转圈游戏输家

题目描述&#xff1a; n 个朋友在玩游戏。这些朋友坐成一个圈&#xff0c;按 顺时针方向 从 1 到 n 编号。从第 i 个朋友的位置开始顺时针移动 1 步会到达第 (i 1) 个朋友的位置&#xff08;1 < i < n&#xff09;&#xff0c;而从第 n 个朋友的位置开始顺时针移动 1 步…