日志与策略模式

什么是设计模式

IT⾏业 ,为了让 菜鸡们不太拖⼤佬的后腿, 于是⼤佬们针对⼀些经典的常⻅的场景, 给定了⼀些对应的解决⽅案, 这个就是  设计模式 

日志认识

计算机中的⽇志是记录系统和软件运⾏中发⽣事件的⽂件,主要作⽤是监控运⾏状态、记录异常信 息,帮助快速定位问题并⽀持程序员进⾏问题修复。它是系统维护、故障排查和安全管理的重要⼯ 具。

⽇志格式以下⼏个指标是必须得有的

  • 时间戳
  • ⽇志等级
  • ⽇志内容

以下几个指标是可选的

  • 文件名行号
  • 进程,线程相关id信息等

⽇志有现成的解决⽅案,如:spdlog、glog、Boost.Log、Log4cxx等等,我们依旧采⽤⾃定义⽇志的方式。

这⾥我们采⽤设计模式-策略模式来进⾏⽇志的设计,

策略模式是一种行为型设计模式,它允许在运行时选择算法或行为。该模式将算法族定义为一组可互换的策略,使得算法可以独立于使用它的客户端变化。

策略模式基于以下设计原则:

  • 封装变化:将易变的算法部分单独封装

  • 面向接口编程:定义策略接口,而不是具体实现

  • 组合优于继承:通过组合策略对象来获得灵活性,而非通过继承

策略模式包含三个主要角色:

  1. Context(上下文)

    • 维护对策略对象的引用

    • 可以定义一个接口让策略访问它的数据

  2. Strategy(策略接口)

    • 定义所有支持的算法的公共接口

    • Context使用这个接口调用具体策略定义的算法

  3. ConcreteStrategy(具体策略)

    • 实现策略接口的具体算法

例子:

#include <iostream>
#include <memory>// 策略接口
class SortingStrategy {
public:virtual void sort(int* data, int size) const = 0;virtual ~SortingStrategy() = default;
};// 具体策略A:快速排序
class QuickSort : public SortingStrategy {
public:void sort(int* data, int size) const override {std::cout << "Sorting using QuickSort\n";// 实际快速排序实现...}
};// 具体策略B:冒泡排序
class BubbleSort : public SortingStrategy {
public:void sort(int* data, int size) const override {std::cout << "Sorting using BubbleSort\n";// 实际冒泡排序实现...}
};// 上下文类
class Sorter {
private:std::unique_ptr<SortingStrategy> strategy;public:explicit Sorter(std::unique_ptr<SortingStrategy> strategy) : strategy(std::move(strategy)) {}void setStrategy(std::unique_ptr<SortingStrategy> newStrategy) {strategy = std::move(newStrategy);}void executeSort(int* data, int size) {strategy->sort(data, size);}
};int main() {int data[] = {5, 2, 7, 1, 9};Sorter sorter(std::make_unique<QuickSort>());sorter.executeSort(data, 5);  // 使用快速排序sorter.setStrategy(std::make_unique<BubbleSort>());sorter.executeSort(data, 5);  // 改为冒泡排序return 0;
}

我们想要的⽇志格式如下:

[可读性很好的时间] [⽇志等级] [进程pid] [打印对应⽇志的⽂件名][⾏号] - 消息内容,⽀持可
变参数
[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [16] - hello world
[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [17] - hello world
[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [18] - hello world
[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [20] - hello world
[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [21] - hello world
[2024-08-04 12:27:03] [WARNING] [202938] [main.cc] [23] - hello world

log.hpp

#pragma once
#include <iostream>
#include <string>
#include "Mutex.hpp"
#include <filesystem> //c++17
#include <fstream>    //c++文件流
#include <sstream>    //c++字符串流
#include <memory>
#include <time.h>//基于策略模式的日志
namespace LogModule
{using namespace LockModule;// 获取时间的函数std::string CurrentTime(){time_t time_stamp = ::time(nullptr); // 获取时间戳struct tm curr;//_r代表可以重入,支持多线程localtime_r(&time_stamp, &curr); // 将时间戳转化成可读性较强的时间信息char buffer[1024];// bugsnprintf(buffer, sizeof(buffer), "%4d-%02d-%02d %02d:%02d:%02d",curr.tm_year + 1900,curr.tm_mon + 1,curr.tm_mday,curr.tm_hour,curr.tm_min,curr.tm_sec);return buffer;}// 日志构成两个阶段: 一.构建日志信息  二.刷新落盘screen / file(向哪里刷新)// 二. 刷新落盘// 1. 日志文件的默认路径和名称const std::string dafaultlogpath = "./log/";const std::string dafaultlogname = "log.txt";// 2. 日志等级enum class LogLevel{DEBUG = 1,INFO, // 正常的WARNNING,ERROR,FATAL // 致命的};std::string Level2String(LogLevel level){switch (level){case LogLevel::DEBUG:return "DEBUG";case LogLevel::INFO:return "INFO";case LogLevel::WARNNING:return "WARNNING";case LogLevel::ERROR:return "ERROR";case LogLevel::FATAL:return "FATAL";default:return "NONE";}}// 3.刷新策略class LogStrategy // 基类{public:virtual ~LogStrategy() = default;   //虚析构函数(保证派生类对象能正确析构):确保通过基类指针删除派生类对象时能正确调用派生类的析构函数virtual void SyncLog(const std::string &message) = 0;//纯虚函数使得基类为抽象类 ,其派生类必须重构此函数才能构建对象};// 3.1控制台策略class ConsoleLogStrategy : public LogStrategy{public:ConsoleLogStrategy(){}~ConsoleLogStrategy(){}void SyncLog(const std::string &message){// 屏幕也是临界资源LockGuard lockguard(_mutex);std::cout << message << std::endl;}private:Mutex _mutex;};// 3.2文件级策略class FileLogStrategy : public LogStrategy{public:FileLogStrategy(const std::string &logpath = dafaultlogpath, const std::string &logname = dafaultlogname): _logpath(logpath),_logname(logname){LockGuard lockguard(_mutex);// 确认_logpath存在if (std::filesystem::exists(_logpath)){return;}try{std::filesystem::create_directories(_logpath);}catch (std::filesystem::filesystem_error &e){std::cerr << e.what() << "\n";}}~FileLogStrategy(){}void SyncLog(const std::string &message){LockGuard lockguard(_mutex);// c++文件操作std::string log = _logpath + _logname;// 创建一个ofstream文件输出流对象,以追加模式打开日志文件std::ofstream out(log, std::ios::app); // 日志是追加写入if (!out.is_open()){return;}out << message << "\n";out.close();}private:std::string _logpath;std::string _logname;Mutex _mutex; // 保证资源安全};// 一. 构建日志信息// 日志类 ,构建日志字符串(内部类实现) ,根据策略进行刷新class Logger{public:Logger(){// 默认使用控制台刷新_strategy = std::make_shared<ConsoleLogStrategy>();}void EnableConsoleLog(){_strategy = std::make_shared<ConsoleLogStrategy>();}void EnableFileLog(){_strategy = std::make_shared<FileLogStrategy>();}~Logger(){}// 定义了内部类   一个logmessage就包含了一条完整的日志信息//  一条完整的日志信息: [2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [16] + 日志的可变部分(<< "hello world" << 3.14 << a << b;)class LogMessage{public:LogMessage(LogLevel level, const std::string &filename, int line, Logger &logger): _currtime(CurrentTime()), _level(level), _pid(getpid()), _src_name(filename), _line(line), _logger(logger){// 用stringstream进行流式拼接std::stringstream ssbuffer;ssbuffer << "[" << _currtime << "] "<< "[" << Level2String(_level) << "] " // 我们想要字符串式的日志等级<< "[" << _pid << "] "<< "[" << _src_name << "] "<< "[" << _line << "] - ";_loginfo = ssbuffer.str();}// LOG(DEBUG) << "hello " << 3.14 << a << b;想要实现需要重载<<template <typename T>LogMessage &operator<<(const T &info) // 返回使用引用 (要保证后面的信息都拼接到同一个LogMessage){std::stringstream ss;ss << info;_loginfo += ss.str();return *this; // 返回自己}~LogMessage(){// 析构时,执行Logger所对应的根据指定策略进行刷新一条方法if (_logger._strategy)//设置策略了就刷新{_logger._strategy->SyncLog(_loginfo);}}private:std::string _currtime; // 时间LogLevel _level;       // 日志等级pid_t _pid;            // 进程pidstd::string _src_name; // 原文件名称int _line;             // 行号Logger &_logger;       // 负责根据不同的策略进行刷新std::string _loginfo;  // 一条完整的日志信息};// 仿函数重载() ,返回一个完整的日志信息// 故意没有写引用 ,就是要拷贝,返回临时的LogMessage??? 临时的LogMessage 自动析构时 自动刷新日志  LogMessage operator()(LogLevel level, const std::string &filename, int line){return LogMessage(level, filename, line, *this);}private:std::shared_ptr<LogStrategy> _strategy; // 日志的刷新方案// LogStrategy是纯虚类 ,不能定义对象,能定义指针};// 使用Logger logger;#define LOG(level) logger(level, __FILE__, __LINE__) //__是预处理符    logger()运算符重载
#define ENABLE_CONSOLE_LOG() logger.EnableConsoleLog()
#define ENABLE_FILE_LOG() logger.EnableFileLog()
}

Main.cc

#include"log.hpp"using namespace LogModule;int main()
{//用C++版的流,实现可变参数//日志输出格式//LOG(DEBUG) << "hello " << 3.14 << a << b;//会被替换成下面格式//logger(level ,__FILE__ ,__LINE__)<< "hello " << 3.14 << a << b;    //()执行完构建一个临时的LogMessage,临时的LogMessage会执行<<//LogMessage<< "hello " << 3.14 << a << b;        //LogMessage重载了<<ENABLE_FILE_LOG();LOG(LogLevel::INFO)<<"hello"<<666;return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解锁Ubuntu高效部署!自动安装配置文件YAML全解析

我们之前介绍了两种Ubuntu系统的安装方式&#xff0c;分别对应桌面版&#xff08;准备搞OpenStack了&#xff0c;先装一台最新的Ubuntu 23.10&#xff09;和服务器版&#xff08;Ubuntu 22.04 LTS服务器版本安装演示&#xff09;。但对于有些用户&#xff0c;因为技术问题&…

关系代数和关系数据库语言(SQL)

阅读提示&#xff1a;本篇文章较长&#xff0c;建议从目录上选取想看的内容。代码上的话&#xff0c;我习惯用小写&#xff0c;如果看不习惯建议跳过。有问题欢迎讨论&#xff01;&#xff01;&#xff01; 一、基础概念 1.1数据库的概念 数据库(Database)是按照数据结构来组…

EXO 可以将 Mac M4 和 Mac Air 连接起来,并通过 Ollama 运行 DeepSeek 模型

EXO 可以将 Mac M4 和 Mac Air 连接起来&#xff0c;并通过 Ollama 运行 DeepSeek 模型。以下是具体实现方法&#xff1a; 1. EXO 的分布式计算能力 EXO 是一个支持 分布式 AI 计算 的开源框架&#xff0c;能够将多台 Mac 设备&#xff08;如 M4 和 Mac Air&#xff09;组合成…

区块链基本理解

文章目录 前言一、什么是分布式账本(DLT)二、什么是P2P网络?二、共识算法三、密码算法前言 区块链是由一个一个数据块组成的链条,按照时间顺序将数据块逐一链接,通过哈希指针链接,所有的数据块共同维护一份分布式账本(DLT),每个节点(可以理解为一个玩家,一台计算机)都拥…

Node.js中的洋葱模型

文章目录 前言 前言 Node.js中的洋葱模型是一种中间件执行机制&#xff0c;主要用于处理HTTP请求和响应的流程控制。该模型通过层层包裹的中间件结构&#xff0c;实现请求从外到内穿透、响应从内向外返回的顺序执行。以下从核心概念、实现原理、框架差异及实际应用等方面解析&…

UI-TARS Desktop:用自然语言操控电脑,AI 重新定义人机交互

在人工智能技术飞速发展的今天,从文本生成到图像识别,AI 的能力边界不断被打破。而字节跳动近期开源的 UI-TARS Desktop,则将这一技术推向了更复杂的交互场景——通过自然语言直接控制计算机界面,实现了图形用户界面(GUI)的智能化自动化。这款工具不仅降低了操作门槛,更…

一个可拖拉实现列表排序的WPF开源控件

从零学习构建一个完整的系统 推荐一个可通过拖拉&#xff0c;来实现列表元素的排序的WPF控件。 项目简介 gong-wpf-dragdrop是一个开源的.NET项目&#xff0c;用于在WPF应用程序中实现拖放功能&#xff0c;可以让开发人员快速、简单的实现拖放的操作功能。 可以在同一控件内…

C语言中字符串函数的详细讲解

C语言提供了丰富的字符串处理函数&#xff0c;这些函数在<string.h>头文件中声明。以下是一些常用字符串函数的详细讲解&#xff1a; 字符串拷贝函数 strcpy 功能&#xff1a;将源字符串&#xff08;包括结尾的\0&#xff09;复制到目标字符串。原型&#xff1a;char *s…

可视化数据图表怎么做?如何实现三维数据可视化?

目录 一、三维数据可视化的要点 1. 明确数据可视化的目标 2. 筛选与整理数据 3. 选择合适的图表类型 4. 运用专业工具制作 5. 优化图表的展示效果 二、数据可视化图表怎么做&#xff1f; 1. 理解三维数据的特性 2. 数据处理与三维建模 3. 设置光照与材质效果 4. 添加…

在Linux服务器上部署Jupyter Notebook并实现ssh无密码远程访问

Jupyter notebook版本7.4.2&#xff08;这个版本AI提示我Jupyter7&#xff08;底层是 jupyter_server 2.x&#xff09; 服务器开启服务 安装Jupyter notebook 7.4.2成功后&#xff0c;终端输入 jupyter notebook --generate-config 这将在 ~/.jupyter/ 目录下生成 jupyter_…

走出 Demo,走向现实:DeepSeek-VL 的多模态工程路线图

目录 一、引言&#xff1a;多模态模型的关键转折点 &#xff08;一&#xff09;当前 LMM 的三个关键挑战 1. 数据的真实性不足 2. 模型设计缺乏场景感知 3. 语言能力与视觉能力难以兼顾 &#xff08;二&#xff09;DeepSeek-VL 的根本出发点&#xff1a;以真实任务为锚点…

数据库原理及其应用 第六次作业

题目 参考答案 题目1. 教材P148第1题 问题&#xff1a;什么是数据库的安全性&#xff1f; 答案&#xff1a;数据库的安全性是指保护数据库以防止不合法的使用所造成的数据泄露、更改或破坏 。它通过用户身份鉴别、存取控制&#xff08;包括自主存取控制和强制存取控制&#x…

2025系统架构师---选择题知识点(押题)

1.《计算机信息系统安全保护等级划分准则》(GB 17859-1999)由低到高定义了五个不同级别的计算机系统安全保护能力。 第一级:用户自主保护级---通过隔离用户与数据实现访问控制,保护用户信息安全; 第二级:系统审计保护级---实施更细粒度的访问控制,通过审计和隔离资源确…

Qt操作SQLite数据库教程

Qt 中操作 SQLite 数据库的步骤如下&#xff1a; 1. 添加 SQLite 驱动并打开数据库 #include <QSqlDatabase> #include <QSqlError> #include <QSqlQuery>// 创建数据库连接 QSqlDatabase db QSqlDatabase::addDatabase("QSQLITE"); db.setData…

从紫光集团看基本财务分析

PE 46PE 代表投资人对他的期望是它的业绩至少要增长50%才算及格。 但实际业绩 一年不如一年. 所以&#xff0c;这个PE 应该是 业绩倒退了&#xff0c;但是市值还没有掉下去&#xff0c;导致运算的结果处在高PE阶段。 那么随着股价的下跌&#xff0c;这个数字会慢慢变小。 当然…

基于MNIST数据集的手写数字识别(CNN)

目录 一&#xff0c;模型训练 1.1 数据集介绍 1.2 CNN模型层结构 1.3 定义CNN模型 1.4 神经网络的前向传播过程 1.5 数据预处理 1.6 加载数据 1.7 初始化 1.8 模型训练过程 1.9 保存模型 二&#xff0c;模型测试 2.1 定义与训练时相同的CNN模型架构 2.2 图像的预处…

centos中postfix的作用

/usr/libexec/postfix/master 是 Postfix 邮件服务器的主进程&#xff0c;qmgr 和 pickup 是 Postfix 的子进程。这些进程本身是正常的&#xff0c;但如果你怀疑服务器被用于钓鱼活动&#xff0c;需要进一步检查 Postfix 的配置和日志&#xff0c;确保它没有被滥用。 1. 检查 P…

蓝牙耳机什么牌子好?倍思值得冲不?

最近总被问“蓝牙耳机什么牌子好”&#xff0c;作为踩过无数坑的资深耳机党&#xff0c;必须安利刚入手的倍思M2s Pro主动降噪蓝牙耳机&#xff01;降噪、音质、颜值全都在线&#xff0c;性价比直接拉满。 -52dB降噪&#xff0c;通勤摸鱼神器 第一次开降噪就被惊到&#xff01…

游戏引擎学习第285天:“Traversables 的事务性占用”

回顾并为当天的工作做准备 我们有一个关于玩家移动的概念&#xff0c;玩家可以在点之间移动&#xff0c;而且当这些点移动时&#xff0c;玩家会随之移动。现在这个部分基本上已经在工作了。我们本来想实现的一个功能是&#xff1a;当玩家移动到某个点时&#xff0c;这个点能“…

java中的包机制

包机制 为了更好地组织类&#xff0c;java提供了包机制&#xff0c;用于区分类名的命名空间 包语句的语法格式为 package pkg1[. pkg2[. pkg3...]]一般利用公司域名倒置作为包名 &#xff1a; 公司域名&#xff1a;www.baidu.com 包名&#xff1a;com.baidu.www 为了能够…