日志系统-实用类

ops/2024/10/16 0:22:34/

 使用工具类设计

#ifndef __UTIL_H__
#define __UTIL_H__
/*通用功能类,与业务无关的功能实现1. 获取系统时间2. 获取文件大小3. 创建目录4. 获取文件所在目录
*/
#include <iostream>
#include<ctime>
#include <unistd.h>
#include <sys/stat.h>
namespace wjj_logs
{namespace util{class Date{public:static size_t now(){return (size_t)time(nullptr);}};class File{public:static bool exists(const std::string &pathname)//判断文件是否存在{// return (access(pathname.c_str(),F_OK)==0);//系统调用接口,跨平台会有问题struct stat st;if(stat(pathname.c_str(),&st)<0) return false;return true;}static std::string path(const std::string &pathname){size_t pos=pathname.find_last_of("/\\");if(pos==std::string::npos) return ".";else return pathname.substr(0,pos+1);//./dir/a.txt->./dir/} //获取文件所在目录static void createDirectory(const std::string &pathname)//创建目录,有可能有分级创建多个目录{// dir3/dir2/dir1/a.txtsize_t pos=0,idx=0;std::cout<<pathname<<std::endl;while (idx<pathname.size()){pos=pathname.find_first_of("/\\",idx);if(pos==std::string::npos){mkdir(pathname.c_str(),0777);std::cout<<"mkdir"<<pathname<<std::endl;}std::string parent_dir=pathname.substr(0,pos+1);if(exists(parent_dir)==true){idx=pos+1;continue;}mkdir(parent_dir.c_str(),0777);std::cout<<"mkdir"<<pathname<<std::endl;idx=pos+1;} }};} }
#endif

日志等级设计

日志等级模块:
        1、定义出日志系统所包含的所有日志等级:
        NNKNOW = 0; DEBUG,调试等级的日志;INFO, 提示等级的日志;WARN,警告等级的日志;ERROR,错误等级的日志;FATAL,致命错误等级的日志;OFF,关闭;
        每一个项目中都会设置一个默认的日志输出等级,只有输出的日志等级大于等于默认限制
等级的时候才可以进行输出
        2、提供一个接口,将对应等级的枚举,转换为一个对应的字符串:DEBUG --->"DEBUG"

#ifndef __LEVEL_H__
#define __LEVEL_H__
/*定义枚举类,枚举出日志等级提供转换接口:将枚举转换成为对应的字符出啊
*/
namespace wjj_logs
{class LogLevel{public:enum class value{UNKOW=0,DEBUG,INFO,WARN,ERROR,FATAL,OFF};static const char* toString(LogLevel::value level){switch (level){case LogLevel::value::DEBUG: return "DEBUG";case LogLevel::value::INFO: return "INFO";case LogLevel::value::WARN: return "WARN";case LogLevel::value::ERROR: return "ERROR";case LogLevel::value::FATAL: return "FATAL";case LogLevel::value::OFF: return "OFF";}return "UNKOW";}};
}
#endif 

日志消息类

日志消息模块:中间存储日志输出所需的各项要素信息 。

时间:描述本条日志的输出时间。

日志源文件名称和行号:定位错误

日志等级:描述本条日志的等级。用于进行日志过滤。

线程ID:描述本条日志是哪个线程输出的。

日志主体内容

日志器名称:当前支持多日志器的同时使用

#ifndef __MESSAGE_H__
#define __MESSAGE_H__
/*
日志消息模块:中间存储日志输出所需的各项要素信息 。
时间:描述本条日志的输出时间。
日志源文件名称和行号:定位错误
日志等级:描述本条日志的等级。用于进行日志过滤。
线程ID:描述本条日志是哪个线程输出的。
日志主体内容
日志器名称:当前支持多日志器的同时使用
*/
#include <iostream>
#include <string>
#include <thread>
#include "level.hpp"
#include "util.hpp"
namespace wjj_logs
{struct LogMsg{size_t _ctime;//日志产生时间戳std::string _file;//源码文件名称size_t _line;//源码行号LogLevel::value _level;//日志等级std::thread::id _tid;//线程idstd::string _logger;//日志器名称std::string _payload;//日志主体消息LogMsg(LogLevel::value level,size_t line,const std::string file,const std::string logger,const std::string msg):_ctime(util::Date::now()),_level(level),_line(line),_tid(std::this_thread::get_id()),_file(file),_logger(logger),_payload(msg){}};} // namespace wjj_logs#endif // DEBUG

 日志格式化

对日志消息进行格式化组织成为指定格式的字符串。格式化字符串控制了日志输出的格式,定义格式化字符串是为了让日志系统进行日志格式化时更加的灵活方便。

#ifndef __FORMAT_H__
#define __FORMAT_H__
#include "level.hpp"
#include "message.hpp"
#include <ctime>
#include <vector>
#include <cassert>
#include <sstream>
namespace wjj_logs
{class FormatItem{public:using ptr=std::shared_ptr<FormatItem>;virtual void format(std::ostream& out,const LogMsg &msg)=0;};class FileFormatItem : public FormatItem {public:void format(std::ostream &out,const LogMsg &msg) override{out << msg._file;}};class MsgFormatItem:public FormatItem{public:void format(std::ostream& out,const LogMsg &msg) override{out<<msg._payload;}};class LevelFormatItem:public FormatItem{public:void format(std::ostream& out,const 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,const LogMsg &msg) override{struct tm result;localtime_r(&msg._ctime,&result);//将时间戳转换成为时间结构化数据resultchar tmp[32]={0};strftime(tmp,31,_time_fmt.c_str(),&result);//将result按照指定的格式生成字符串放入到tmp当中out<<tmp;}private:std::string _time_fmt;//%H:%M:%S};class LineFormatItem : public FormatItem {public:virtual void format(std::ostream &out,const LogMsg &msg) override{out << msg._line;}};class ThreadFormatItem : public FormatItem {   public:virtual void format(std::ostream &out,const LogMsg &msg) override{out << msg._tid;}};class LoggerFormatItem : public FormatItem {   public:virtual void format(std::ostream &out,const LogMsg &msg) override{out << msg._logger;}};class TabFormatItem : public FormatItem //制表符{   public:virtual void format(std::ostream &out,const LogMsg &msg) override{out << "\t";}};class NLFormatItem : public FormatItem //制表符{   public:virtual void format(std::ostream &out,const LogMsg &msg) override{out <<"\n";//new line换行}};class OherFormatItem : public FormatItem //制表符{   public:OherFormatItem(const std::string &str):_str(str){}virtual void format(std::ostream &out,const LogMsg &msg) override{out << _str;}private:std::string _str;};/*%d 日期,包含资格是{%H%M%S}%t 线程id%c 日志器名称%f 源码文件名%l 源码行号%p 日志级别%T 制表符缩进%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());//解析必须成功,不然无法知道按照什么格式输出}std::string format(LogMsg &msg) //对msg进行格式化返回一个格式化的字符串{std::stringstream ss;format(ss,msg);return ss.str();}void format(std::ostream &out,LogMsg &msg)//将msg格式化放到io流中{for(auto &item:_items){item->format(out,msg);}}private:bool parsePattern()//对格式化规则字符串_pattern进行解析放入到_items中,不要对外面暴漏无实际意义或者用不到的接口。{//1.对格式化规则字符串进行解析//avc%d{%H:%M:%S}%Tuuu%t%T[%p]%T[%c]:从前向后遍历,遇到%就代表其后面是一个格式化字符,一开始如果没遇到%就代表他是原始字符串,格式化字符如果后面紧跟{代表后面是一个字串std::vector<std::pair<std::string,std::string>> fmt_order;//存放解析之后的结果size_t pos=0;std::string key,val;while(pos<_pattern.size()){//1.处理原始字符串,判断是否是%,不是就是原始字符if(_pattern[pos]!='%'){val.push_back(_pattern[pos++]);continue;}//能走下来就代表pos位置上就是%字符,先考虑%%这种原始字符%的情况if(pos+1<_pattern.size() && _pattern[pos+1]=='%'){val.push_back('%');pos+=2;continue;}//这时候开始处理原始字符串valif(val.empty()==false){fmt_order.push_back(std::make_pair("",val));val.clear();}//接下来开始格式化字符的处理,此时pos指向'%'的位置pos+=1;if(pos==_pattern.size()){std::cout<<"%之后,没有对应的格式化字符!\n";return false;}key=_pattern[pos];//获取格式化字符//判断格式化字符后面是否有{}即是否有子串,但也要考虑是否已经到达格式化规则字符串末尾的情况pos+=1;if(pos<_pattern.size() && _pattern[pos]=='{'){pos+=1;//这时候pos指向'{'之后即子规则的起始位置while (pos<_pattern.size() && _pattern[pos]!='}'){val.push_back(_pattern[pos++]);}if(pos==_pattern.size()) //走到末尾跳出循环,说明格式化规则字符串有问题,没有遇到'}'。{std::cout<<"子规则{}匹配出错!\n";return false; }pos+=1;//此时pos从'}'移动到了下一循环处理的新起始位置}fmt_order.push_back(std::make_pair(key,val));key.clear();val.clear();}//2.根据得到的数据初始化格式化子项数组成员_itemsfor(auto &ch:fmt_order){_items.push_back(createItem(ch.first,ch.second));}return true;}private:FormatItem::ptr createItem(const std::string &key,const std::string &val)//根据不同的格式化字符key创建不同的格式化子项对象{if (key == "d") return std::make_shared<TimeFormatItem>(val);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<NLFormatItem>();  if (key=="") return std::make_shared<OherFormatItem>(val);//key为空就说明此时是原始字符串std::cout<<"没有对应的格式化字符 %"<<key<<std::endl;abort();return FormatItem::ptr();}private:std::string _pattern;//格式化规则字符串std::vector<FormatItem::ptr> _items;};}
#endif


http://www.ppmy.cn/ops/94970.html

相关文章

系统重构新旧流量平滑迁移方案

背景 旧交易系统存活时间比较久&#xff0c;随着组织架构的不断调整&#xff0c;旧交易新系统在各个团队轮转&#xff0c;技术和代码腐化严重&#xff0c;针对于新业务支持能力很差。 经过内部慎重决策&#xff0c;在旧交易系统基础上&#xff0c;针对技术和业务上进行重构&a…

15.2 Scikit-learn简介与常用模型

欢迎来到我的博客,很高兴能够在这里和您见面!欢迎订阅相关专栏: 工💗重💗hao💗:野老杂谈 ⭐️ 全网最全IT互联网公司面试宝典:收集整理全网各大IT互联网公司技术、项目、HR面试真题. ⭐️ AIGC时代的创新与未来:详细讲解AIGC的概念、核心技术、应用领域等内容。 ⭐…

PHP模拟高并发异步请求测试+redis的setnx处理并发和防止死锁处理

/** PHP并发异步请求测试* /test/curlMulti*/public function curlMultiAction(){$urls ["http://localhost:801/api/order/create","http://localhost:801/api/order/create","http://localhost:801/api/order/create","http://localhos…

【Nodejs】五、Node.js 模块化

一、介绍 1.1 什么是模块化与模块 ? 将一个复杂的程序文件依据一定规则&#xff08;规范&#xff09;拆分成多个文件的过程称之为 模块化 其中拆分出的 每个文件就是一个模块 &#xff0c;模块的内部数据是私有的&#xff0c;不过模块可以暴露内部数据以便其他 模块使用…

使用 Python 进行 PDF 文件加密

使用 Python 解密加密的 PDF 文件-CSDN博客定义一个名为的函数&#xff0c;该函数接受三个参数&#xff1a;输入的加密 PDF 文件路径input_pdf、输出的解密 PDF 文件路径output_pdf和密码password。https://blog.csdn.net/qq_45519030/article/details/141256661 在数字化时代…

顶顶通呼叫中心中间件-通话之前录音配置方法(mod_cti基于FreeSWITCH)

顶顶通呼叫中心中间件-通话之前录音配置方法(mod_cti基于FreeSWITCH) 1、修改配置文件 点击配置文件 -> 点击vars -> 根据图中配置 -> 点击提交XML ->重新启动freeswitch 修改成true就是电话接通开始录音&#xff0c;修改成false就是通话之前开始录音。 <!--应…

Qt 系统相关 - 网络与音视频

目录 一、Qt 网络 1. UDP Socket 1.1 核心 API 概览 1.2 回显服务器 1.3 回显客户端 2. TCP Socket 2.1 核心 API 概览 2.2 回显服务器 2.3 回显客户端 3. HTTP Client 3.1 核心 API 3.2 代码示例 二、Qt 音视频 1. Qt 音频 1.1 核心API概览 1.2 示例 2. Qt 视…

Vue项目-Table添加Form表单校验

一、HTML <template><div class"taskInfo"><el-form:model"generateParams":rules"formRules"ref"formRef"class"taskInfoForm"label-width"100px"><ul class"taskInfoSearch"&g…