欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 文化 > 日志系统-实用类

日志系统-实用类

2024/11/30 2:49:19 来源:https://blog.csdn.net/weixin_48133237/article/details/141263344  浏览:    关键词:日志系统-实用类

 使用工具类设计

#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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com