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

郑州建设网站推广公司淄博网站建设团队

郑州建设网站推广公司,淄博网站建设团队,教育机构电商网站建设加盟,熊掌号提交wordpressC项目 —— 基于多设计模式下的同步&异步日志系统(2)(工厂模式) 基类实现滚动文件文件名问题 工厂模式一些扩展点struct tm 成员列表关键注意事项使用示例1. 获取当前时间并打印2. 与 strftime 配合使用3. 构造自定义时间 常见…

C++项目 —— 基于多设计模式下的同步&异步日志系统(2)(工厂模式)

  • 基类实现
  • 滚动文件
    • 文件名问题
  • 工厂模式
  • 一些扩展点
      • `struct tm` 成员列表
      • 关键注意事项
      • 使用示例
        • 1. 获取当前时间并打印
        • 2. 与 `strftime` 配合使用
        • 3. 构造自定义时间
      • 常见问题
      • 可视化记忆表

我们在之前把日志消息的主体已经组织好了,而且我们创建的适当的类控制日志格式,并且按照我们的格式组织日志消息。如果还没有看过的小伙伴可以点击这里:

https://blog.csdn.net/qq_67693066/article/details/147162921?sharetype=blogdetail&sharerId=147162921&sharerefer=PC&sharesource=qq_67693066&spm=1011.2480.3001.8118

我们这次的任务是要完成日志器中的一个小功能实现:日志输出的方向,我们日志输出的方向有:控制台固定文件滚动文件。这个模块的实现还会涉及到工厂模式的使用

基类实现

首先设计思想还是比较清晰的,设计一个基类,派生出三个不同的方向,我们先实现两个方向比较简单的:

namespace logs
{//基类实现class BaseSink{public:using ptr = std::shared_ptr<BaseSink>; BaseSink(){}virtual ~BaseSink() {}virtual void log(const char *data, size_t len) = 0; //要继承实现的接口};class StdoutSink : public BaseSink{public://将日志消息写到标准输出void log(const char *data, size_t len){std::cout.write(data, len);}};class FixFileSink : public BaseSink {public:FixFileSink(const std::string& pathname):_pathname(pathname){//1.创建文件所在路径logs::utils::File::createDiretory(logs::utils::File::path(_pathname));//2.创建文件并打开_ofs.open(_pathname,std::ios::binary | std::ios::app);assert(_ofs.is_open());}//将日志消息写到固定文件中void log(const char *data, size_t len){_ofs.write(data,len);assert(_ofs.good());}private:std::string _pathname; //创建文件时的文件路径std::ofstream _ofs; //流式文件操作};
}

我们也可以顺便测试一下:

#include"utils.hpp"
#include"level.hpp"
#include"message.hpp"
#include"fometter.hpp"
#include "sink.hpp"int main()
{// std::cout << logs::utils::File::path("./abc/def");// logs::utils::File::createDiretory("./abc/def");//std::cout <<logs::Loglevel::toString(logs::Loglevel::value::DEBUG);logs::logMsg msg(logs::Loglevel::value::DEBUG,"main.cc",53,"root","格式化功能测试....");logs::Formetter fmt("abc[%d{%H:%M:%S}][%c]%T%m%n");std::string str = fmt.format(msg);//标准输入测试size_t len = str.size();logs:: StdoutSink st;st.log(str.c_str(),len);//文件测试logs::FixFileSink fx("../test/test.txt");fx.log(str.c_str(),len);
}

滚动文件

滚动文件意思是,如果这个文件日志已经被写满了,会自动换到一个新的文件写日志,写满了的话又继续换。

文件名问题

既然会不停的创建文件,我们就不得不考虑一个问题,文件名。文件名是不能重复的,那么有没有一种方式,能保证我们的文件名是绝对不会重复的呢?有的,时间戳,时间只要在流逝,时间戳就会变。我们可以让时间戳作为我们文件名的一部分,这样就可以保证文件绝对不会重复了。

    class RollFileSink : public BaseSink{public:private:std::string createNewFile(){//1.获取当前时间戳time_t time = logs::utils::Date::get_time();struct tm lt;localtime_r(&time,&lt);std::stringstream filename;filename << _basename;filename << lt.tm_year + 1900;filename << lt.tm_mon + 1;filename << lt.tm_mday;filename << lt.tm_hour;filename << lt.tm_min;filename << lt.tm_sec;filename << "-";filename << _name_count++;filename << ".log";return filename.str();}// 基础文件名 + 扩展文件名(以时间生成)组成一个实际的当前输出文件名size_t _name_count;std::string _basename; //基础文件名std::ofstream _ofs; //流式文件size_t _max_size; //最大文件大小size_t _cur_size: //当前文件大小};
   class RollFileSink : public BaseSink{public:RollFileSink(const std::string& basename,size_t max_size):_basename(basename),_name_count(0),_max_size(max_size),_cur_size(0){std::string pathname = createNewFile();//1、创建文件的所在路径logs::utils::File::createDiretory(logs::utils::File::path(pathname));// 2.创建并打开日志文件_ofs.open(pathname, std::ios::binary | std::ios::app);assert(_ofs.is_open());}// 将日志消息写到固定文件中void log(const char *data, size_t len){if(_cur_size > _max_size){_ofs.close();std::string pathname = createNewFile();//1、创建文件的所在路径logs::utils::File::createDiretory(logs::utils::File::path(pathname));// 2.创建并打开日志文件_ofs.open(pathname, std::ios::binary | std::ios::app);assert(_ofs.is_open());}_ofs.write(data, len);assert(_ofs.good());_cur_size += len;}private:std::string createNewFile(){//1.获取当前时间戳time_t time = logs::utils::Date::get_time();struct tm lt;localtime_r(&time,&lt);std::stringstream filename;filename << _basename;filename << lt.tm_year + 1900;filename << lt.tm_mon + 1;filename << lt.tm_mday;filename << lt.tm_hour;filename << lt.tm_min;filename << lt.tm_sec;filename << "-";filename << _name_count++;filename << ".log";return filename.str();}// 基础文件名 + 扩展文件名(以时间生成)组成一个实际的当前输出文件名size_t _name_count;std::string _basename; //基础文件名std::ofstream _ofs; //流式文件size_t _max_size; //最大文件大小size_t _cur_size; //当前文件大小};

我们可以测试一下:

    logs::RollFileSink roll("../test/mytest",1024);size_t cur = 0;while(cur < 1024 * 2){roll.log(str.c_str(),len);cur+=len;}

在这里插入图片描述

工厂模式

代码定义了一个名为 SinkFactory 的工厂类,用于创建日志输出器(Sink)的智能指针实例。它使用了现代 C++ 的模板和可变参数特性,是一个非常灵活通用的工厂实现:

    class SinkFactory{public:template<typename SinkType,typename... Args>static BaseSink::ptr create(Args && ...args){return std::make_shared<SinkType>(std::forward<Args>(args)...);}};
    auto st1 = logs::SinkFactory::create<logs::StdoutSink>();st1->log(str.c_str(),len);

这样我们不用用户直接接触接口,而是通过工厂,这样更具灵活性。

一些扩展点

struct tm 是 C/C++ 标准库中用于表示日历时间的结构体,定义在 <ctime> 头文件中。以下是其成员变量及详细说明:


struct tm 成员列表

成员类型说明取值范围注意事项
tm_secint0-61 (通常 0-59)允许闰秒
tm_minint分钟0-59
tm_hourint小时(24小时制)0-23
tm_mdayint月中的第几天(Day of month)1-31
tm_monint月份(从0开始)0-11 (0=1月)使用时需 +1
tm_yearint年份(从1900开始)0+=1900年使用时需 +1900
tm_wdayint星期几(从0开始,0=周日)0-6 (0=周日)
tm_ydayint年中的第几天(从0开始)0-365
tm_isdstint夏令时标志:
正数=启用
0=禁用
负数=信息不可用
-1, 0, 1

关键注意事项

  1. 特殊计数规则

    • 月份tm_mon 从 0 开始(0=1月,11=12月),显示时需要 +1
    • 年份tm_year 是 1900 年起的偏移量,真实年份 = tm_year + 1900
    • 星期tm_wday 中 0 表示周日
  2. 夏令时处理

    if (timeinfo.tm_isdst > 0) {std::cout << "夏令时生效";
    }
    
  3. 有效范围扩展

    • tm_sec 允许 60-61 以兼容闰秒
    • 其他字段超出范围时,mktime() 会自动标准化

使用示例

1. 获取当前时间并打印
#include <ctime>
#include <iostream>int main() {time_t now = time(nullptr);struct tm timeinfo;localtime_r(&now, &timeinfo);  // 线程安全版本std::cout << "当前时间: " << 1900 + timeinfo.tm_year << "-" << 1 + timeinfo.tm_mon << "-"<< timeinfo.tm_mday << " "<< timeinfo.tm_hour << ":"<< timeinfo.tm_min << ":"<< timeinfo.tm_sec;
}
2. 与 strftime 配合使用
char buf[64];
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &timeinfo);
std::cout << "格式化时间: " << buf;
3. 构造自定义时间
struct tm custom_time = {0};
custom_time.tm_year = 2023 - 1900;  // 2023年
custom_time.tm_mon = 6 - 1;         // 6月
custom_time.tm_mday = 15;           // 15日
time_t t = mktime(&custom_time);    // 转换为time_t

常见问题

  1. 为什么年份从1900开始?
    历史原因,早期系统用2位数存储年份,1900为基准年。

  2. 如何获取时区信息?
    通过 tm_gmtofftm_zone(非标准扩展,需检查平台支持):

    #ifdef __linux__
    std::cout << "UTC偏移: " << timeinfo.tm_gmtoff / 3600 << "小时";
    #endif
    
  3. 线程安全注意

    • 优先使用 localtime_r()(POSIX)或 localtime_s()(Windows)
    • 避免使用非线程安全的 localtime()

可视化记忆表

struct tm {int tm_sec;    // 秒 [0,61]int tm_min;    // 分 [0,59]int tm_hour;   // 时 [0,23]int tm_mday;   // 日 [1,31]int tm_mon;    // 月 [0,11] ← 注意+1int tm_year;   // 年-1900  ← 注意+1900int tm_wday;   // 周几 [0,6] (0=周日)int tm_yday;   // 年内天数 [0,365]int tm_isdst;  // 夏令时标志
};

掌握这些成员的含义和特性,可以高效处理各种时间操作需求。

http://www.dtcms.com/wzjs/806986.html

相关文章:

  • 中型电商网站维护费用网站开发加盟
  • 公司网站打不开网站开发 业务流程图
  • 建站宝盒建站系统邢台专业网站建设费用
  • 克拉玛依建设局官方网站企业网站大图
  • 个人创办网站亚马逊做品牌备案自有网站
  • 制作平台网站方案网站建设中图片联系方式
  • 新手做网站盈利品质商城网站建设
  • wordpress调用站点标题推荐专业做网站公司
  • 爱丫爱丫影院在线看免费百度推广优化技巧
  • 开网站做备案需要什么资料辽宁建设工程信息网招标公告桓仁金山热电厂防水工程
  • 自学网站开发哪个网站好如何制作页设计
  • 织梦网站地图模版免费发布信息网站大全
  • 小学生课程同步做网站软件免费按模板制作微网站
  • 网站的运营模式阿里巴巴国际站
  • asp网站源码+access+机械前端页面优化
  • 什么网站可以赚钱啊运用vs2010c 做网站
  • 建网站的尺寸百度收录批量查询
  • 南京设计公司有哪些公司成都网站建设seo
  • 企业网站建设推荐乐云seo查找企业名录
  • 网站建设费计入什么科目pc网站 手机网站 微信公众平台
  • pc开奖网站建设云主机网站的空间在哪里
  • 企业网站建设时间表镇江模板网站
  • 设计实例网站wordpress插件吧
  • 网站维护排名怎么自己做网站版面设计
  • 代理网址网站二维码转链接
  • 企业国际网站建设教你做美食的网站
  • 厂家网站怎么做网页设计什么软件好
  • 莱芜网络公司网站国外做的好的医疗网站
  • 网站建设策划书怎么写jsp网站缓存在哪
  • 遵化建设局网站网站制作如何