当前位置: 首页 > news >正文

项目日志——日志输出格式化模块的设计、实现、测试

文章目录

    • 日志输出格式化模块
      • 日志格式化的设计
      • 日志格式化子项的设计
        • 日志格式化子类的实现
      • 日志格式器的实现
        • 解析器的实现
      • 日志输出格式化模块测试

项目仓库: 日志系统
前置介绍: 博客专栏

日志输出格式化模块

日志格式化的设计

对日志消息进行格式化,指定成为指定格式的字符串

定义有下面的格式化字符,支持用户自定义输出格式

  • pattern成员
    • %d 日期,子格式{%H:%M:%S}
    • %T 缩进
    • %t 线程ID
    • %p 日志级别
    • %c 日志器名称
    • %f 文件名
    • %l 行号
    • %m 日志消息
    • %n 换行
  • std::vector<FormatItem::ptr> items;成员
    • 用于格式化对应子项的内容,将日志信息的有效内容提取出来

FormatItem类负责日志消息子项的获取以及格式化,包含以下的子类

  • MsgFormatItem : 从LogMsg中取出有效的日志数据
  • LevelFormatItem : 取出日志等级
  • NameFormatItem : 取出日志器名称
  • ThreadFormatItem : 取出线程ID
  • TimeFormatItem : 取出时间戳,并按照指定格式格式化
  • CFileFormatItem : 取出源文件名称
  • CLineFormatItem : 取出源码所在行号
  • TabFormatItem : 一个制表符缩进
  • NLineFormatItem : 一个换行
  • OtherFormatItem : 其他非格式化字符的子项

日志格式化子项的设计

从日志消息中取出指定元素,进行字符串连接

设计思想就是,抽象出一共格式化子项的基类,派生出不同的格式化子项的子类

子类就包含了上面的日志消息子项的内容

在父类中定义父类指针数组,指向不同的格式化子类对象,这样就能在父类中调用不同子类的功能

日志格式化子类的实现
#pragma once#include "level.hpp"
#include "message.hpp"
#include <ctime>namespace Xulog
{// 抽象格式化子项的基类class FormatItem{public:using ptr = std::shared_ptr<FormatItem>;virtual void format(std::ostream &out, LogMsg &msg) = 0;};// 派生格式化子项的子类class MsgFormatItem : public FormatItem{public:void format(std::ostream &out, LogMsg &msg) override{out << msg._payload;}};class LevelFormatItem : public FormatItem{public:void format(std::ostream &out, LogMsg &msg) override{out << LogLevel::toString(msg._level);}};class TimeFormatItem : public FormatItem{public:TimeFormatItem(std::string &fmt = "%H:%M:%S"): _time_fmt(fmt){}void format(std::ostream &out, LogMsg &msg) override{struct tm st;localtime_r(&msg._ctime, &st);char tmp[32] = {0};strftime(tmp, 31, _time_fmt.c_str(), &st);out << tmp;}private:std::string _time_fmt; // %H:%M:%S};class FileFormatItem : public FormatItem{public:void format(std::ostream &out, LogMsg &msg) override{out << msg._file;}};class LineFormatItem : public FormatItem{public:void format(std::ostream &out, LogMsg &msg) override{out << msg._line;}};class ThreadFormatItem : public FormatItem{public:void format(std::ostream &out, LogMsg &msg) override{out << msg._tid;}};class LoggerFormatItem : public FormatItem{public:void format(std::ostream &out, LogMsg &msg) override{out << msg._logger;}};class TabFormatItem : public FormatItem{public:void format(std::ostream &out, LogMsg &msg) override{out << "\t";}};class NLineFormatItem : public FormatItem{public:void format(std::ostream &out, LogMsg &msg) override{out << "\n";}};class OtherFormatItem : public FormatItem{public:OtherFormatItem(std::string &str): _str(str){}void format(std::ostream &out, LogMsg &msg) override{out << _str;}private:std::string _str;};
}

日志格式器的实现

#pragma once#include "level.hpp"
#include "message.hpp"
#include <ctime>
#include <vector>
#include <cassert>
#include <sstream>
#include <memory>
namespace Xulog
{// 抽象格式化子项的基类class FormatItem{public:using ptr = std::shared_ptr<FormatItem>;virtual void format(std::ostream &out, LogMsg &msg) = 0;};// 派生格式化子项的子类class MsgFormatItem : public FormatItem{public:void format(std::ostream &out, LogMsg &msg) override{out << msg._payload;}};class LevelFormatItem : public FormatItem{public:void format(std::ostream &out, LogMsg &msg) override{out << LogLevel::toString(msg._level);}};class TimeFormatItem : public FormatItem{public:TimeFormatItem(const std::string &fmt = "%H:%M:%S"): _time_fmt(fmt){}void format(std::ostream &out, LogMsg &msg) override{struct tm st;localtime_r(&msg._ctime, &st);char tmp[32] = {0};strftime(tmp, 31, _time_fmt.c_str(), &st);out << tmp;}private:std::string _time_fmt; // %H:%M:%S};class FileFormatItem : public FormatItem{public:void format(std::ostream &out, LogMsg &msg) override{out << msg._file;}};class LineFormatItem : public FormatItem{public:void format(std::ostream &out, LogMsg &msg) override{out << msg._line;}};class ThreadFormatItem : public FormatItem{public:void format(std::ostream &out, LogMsg &msg) override{out << msg._tid;}};class LoggerFormatItem : public FormatItem{public:void format(std::ostream &out, LogMsg &msg) override{out << msg._logger;}};class TabFormatItem : public FormatItem{public:void format(std::ostream &out, LogMsg &msg) override{out << "\t";}};class NLineFormatItem : public FormatItem{public:void format(std::ostream &out, LogMsg &msg) override{out << "\n";}};class OtherFormatItem : public FormatItem{public:OtherFormatItem(const std::string &str): _str(str){}void format(std::ostream &out, LogMsg &msg) override{out << _str;}private:std::string _str;};/*%d 日期,子格式{%H:%M:%S}%T 缩进%t 线程ID%p 日志级别%c 日志器名称%f 文件名%l 行号%m 日志消息%n 换行*/class Formatter{public:Formatter(const std::string &pattern = "[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n"): _pattern(pattern){assert(parsePattern());}// 对msg进行格式化std::string Format(LogMsg &msg){std::stringstream ss;Format(ss, msg);return ss.str();}void Format(std::ostream &out, LogMsg &msg){for (auto &item : _items){item->format(out, msg);}}// 对格式化内容进行解析/*1. 没有以%起始的字符都是原始字符串2. 遇到%则是原始字符串的结束3. %%表示原始的%4. 如果遇到{则说明这是格式化字符的子格式,遇到}结束*/private:bool parsePattern(){std::vector<std::pair<std::string, std::string>> fmt_order;size_t pos = 0;std::string key, val;// 字符串解析while (pos < _pattern.size()){if (_pattern[pos] != '%'){val.push_back(_pattern[pos++]);continue;}if (pos + 1 < _pattern.size() && _pattern[pos + 1] == '%'){val.push_back('%');pos += 2;continue;}// 原始字符串处理完毕if (val.empty() == false){fmt_order.push_back(std::make_pair("", val));val.clear();}if (pos >= _pattern.size()){std::cout << "%后没有有效字符" << std::endl;return false;}pos+=1;key = _pattern[pos];if (pos + 1 < _pattern.size() && _pattern[pos + 1] == '{'){pos += 2;while (pos < _pattern.size() && _pattern[pos] != '}'){val.push_back(_pattern[pos++]);}if (pos >= _pattern.size()){std::cout << "规则中{}匹配出错" << std::endl;return false;}}fmt_order.push_back(std::make_pair(key, val));pos++;key.clear();val.clear();}// 初始化成员for (auto &it : fmt_order){_items.push_back(createItem(it.first, it.second));}return true;}// 根据不同的格式化字符,创建不同的格式化子项的对象FormatItem::ptr createItem(const std::string &key, const std::string &value){if (key == "d")return std::make_shared<TimeFormatItem>(value);if (key == "t")return std::make_shared<ThreadFormatItem>();if (key == "c")return std::make_shared<LoggerFormatItem>();if (key == "f")return std::make_shared<FileFormatItem>();if (key == "l")return std::make_shared<LineFormatItem>();if (key == "p")return std::make_shared<LevelFormatItem>();if (key == "T")return std::make_shared<TabFormatItem>();if (key == "m")return std::make_shared<MsgFormatItem>();if (key == "n")return std::make_shared<NLineFormatItem>();if(key.empty())return std::make_shared<OtherFormatItem>(value);std::cout<<"没有对应的格式化字符:'%"<<key<<"'\n";exit(1);return FormatItem::ptr();}private:std::string _pattern; // 格式化规则字符串std::vector<FormatItem::ptr> _items;};
}
解析器的实现

格式化字符串的解析规则是这样的

  1. 没有以%起始的字符都是原始字符串
  2. 遇到%则是原始字符串的结束
  3. %%表示原始的%
  4. 如果遇到{则说明这是格式化字符的子格式,遇到}结束

这样一直循环遍历,将处理的内容(key,value)保存下来,添加到成员items成员数组中

日志输出格式化模块测试

#include "util.hpp"
#include "level.hpp"
#include "format.hpp"
int main()
{// 测试日志格式化模块Xulog::LogMsg msg(Xulog::LogLevel::value::ERROR, 124, "main.cc", "root", "格式化功能测试");Xulog::Formatter fmt1;Xulog::Formatter fmt2("%c{}");std::string str1 = fmt1.Format(msg);std::string str2 = fmt2.Format(msg);std::cout << str1 << str2;return 0;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【MySQL】MySQL基础
  • C++学习笔记(9)
  • zblog自动生成文章插件(百度AI写作配图,图文并茂)
  • 从材料到应用:螺杆支撑座材质选择的多样性与精准性!
  • 结构化开发方法的三种基本控制结构
  • 为什么构造函数不能为虚函数?为什么析构函数可以为虚函数,如果不设为虚函数可能会存在什么问题?
  • WebShell流量特征检测_蚁剑篇
  • 高级法医视频分析技术 2024
  • PPP 、PPPoE 浅析和配置示例
  • vim 快捷命令
  • mysql-PXC实现高可用
  • UniApp实现漂亮的音乐歌词滚动播放效果
  • 如何提升网站的收录率?
  • Linux 进程信号
  • 【JAVA基础】StringUtils.isEmpty、StringUtils.isBlank()、Objects.isNull()三者区别
  • 《用数据讲故事》作者Cole N. Knaflic:消除一切无效的图表
  • 【跃迁之路】【699天】程序员高效学习方法论探索系列(实验阶段456-2019.1.19)...
  • 2018一半小结一波
  • classpath对获取配置文件的影响
  • css选择器
  • docker python 配置
  • java中的hashCode
  • k8s如何管理Pod
  • KMP算法及优化
  • Nacos系列:Nacos的Java SDK使用
  • react-core-image-upload 一款轻量级图片上传裁剪插件
  • 分布式事物理论与实践
  • 海量大数据大屏分析展示一步到位:DataWorks数据服务+MaxCompute Lightning对接DataV最佳实践...
  • 物联网链路协议
  • C# - 为值类型重定义相等性
  • PostgreSQL 快速给指定表每个字段创建索引 - 1
  • ​ssh-keyscan命令--Linux命令应用大词典729个命令解读
  • ​zookeeper集群配置与启动
  • ​埃文科技受邀出席2024 “数据要素×”生态大会​
  • #鸿蒙生态创新中心#揭幕仪式在深圳湾科技生态园举行
  • $(function(){})与(function($){....})(jQuery)的区别
  • (+3)1.3敏捷宣言与敏捷过程的特点
  • (27)4.8 习题课
  • (delphi11最新学习资料) Object Pascal 学习笔记---第13章第1节 (全局数据、栈和堆)
  • (ZT) 理解系统底层的概念是多么重要(by趋势科技邹飞)
  • (附源码)计算机毕业设计SSM基于java的云顶博客系统
  • (免费领源码)Java#Springboot#mysql农产品销售管理系统47627-计算机毕业设计项目选题推荐
  • (入门自用)--C++--抽象类--多态原理--虚表--1020
  • (三分钟)速览传统边缘检测算子
  • (四十一)大数据实战——spark的yarn模式生产环境部署
  • (算法)Game
  • (一)十分简易快速 自己训练样本 opencv级联haar分类器 车牌识别
  • (原創) 博客園正式支援VHDL語法著色功能 (SOC) (VHDL)
  • (转)jdk与jre的区别
  • (转)winform之ListView
  • (转)关于pipe()的详细解析
  • .NET Windows:删除文件夹后立即判断,有可能依然存在
  • .NET序列化 serializable,反序列化
  • @拔赤:Web前端开发十日谈
  • @我的前任是个极品 微博分析