【Linux】基于策略模式的简单日志设计

📝前言:

这篇文章我们来讲讲Linux——基于策略模式的简单日志设计

🎬个人简介:努力学习ing
📋个人专栏:Linux
🎀CSDN主页 愚润求学
🌄其他专栏:C++学习笔记,C语言入门基础,python入门基础,C++刷题专栏


这里写目录标题

  • 一,认识日志
  • 二,日志设计
    • 1. 总体概述
    • 2. Mylog.hpp
    • 3. Main.cpp
  • 4. 运行效果

一,认识日志

的日志是记录系统和软件运行中发生事件的文件,主要作用是监控运行状态、记录异常信息,帮助快速定位问题并支持程序员进行问题修复。它是系统维护、故障排查和安全管理的重要工具。

日志格式中通常包括:时间戳、日志等级、日志内容
还可能包括:、文件名、行号、进程,线程相关id信息等

尽管复制已经有了大佬写好的现成的东西,但是本文还是采用设计模式- 略模式来进行一个简单日志的设计

格式要求:

[时间] [⽇志等级] [进程pid] [对应⽇志的⽂件名][⾏号] - 消息内容(⽀持可变参数)

二,日志设计

1. 总体概述

我们的日志的关键设计包括以下两点:

  1. 根据不同的策略,把日志内容输出到不同的“文件”
    • 显示器文件
    • log.txt日志文件
  2. 形成一条完整的日志内容
    • 时间的获取
    • 日志等级的设计
    • 进程PID
    • 日志文件名和行号
    • 消息内容的“插入”(插入日志信息的string里),同时要支持可变参数的插入<<

2. Mylog.hpp

我们主要设计以下几个类:

  • LogStrategy策略模式基类,里面提供刷新方式SyncLog的“标准”,需要子类继承并重新给刷新方法实现多态
    • 子类1 ScreenLogStrategy :往显示器上刷新
    • 子类2 FileLogStrategy :往log文件里面刷新
  • Log日志主体,我们要实现的就是以后log << "日志内容"就能写入日志
    • 内部类LogMessage,采用RAII的设计思想,通过生命周期来控制日志内容的“写入”(构造)和“刷新”(析构)

以下是具体的代码:

#pragma once
#include <sstream>
#include <fstream>
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <memory>
#include <ctime>
#include <sys/types.h>
#include <unistd.h>
#include <filesystem> // C++17的文件库, 需要⾼版本编译器和-std=c++17#define FILEPATH "./log/"
#define FILENAME "log.txt"namespace tr
{// 枚举类型,设置日志等级enum class LogLevel{DEBUG,INFO,WARNING,ERROR,FATAL};std::string Level2String(LogLevel loglevel){switch (loglevel){// C++11后枚举类有严格的作用域,这里要指明是LogLevel::case LogLevel::DEBUG:return "DEBUG";case LogLevel::INFO:return "INFO";case LogLevel::WARNING:return "WARNING";case LogLevel::ERROR:return "ERROR";case LogLevel::FATAL:return "FATAL";default:return "UNKNOWN";}}// 策略模式// 基类class LogStrategy{public:virtual ~LogStrategy() = default;virtual void SyncLog(std::string &message) = 0;};// 策略 1: 刷新到屏幕上class ScreenLogStrategy : public LogStrategy{public:void SyncLog(std::string &message) override{_mutex.lock();std::cerr << message << std::endl; // 打印到 cerr 上可以立即刷新_mutex.unlock();}~ScreenLogStrategy(){std::cout << "~ScreenLogStrategy()" << '\n';}private:std::mutex _mutex; // 用 C++ 的锁对象};// 策略 2: 刷新到日志文件中class FileLogStrategy : public LogStrategy{public:FileLogStrategy(const std::string &logpath = "./", const std::string logname = "log.txt"): _logpath(logpath),_logname(logname){if (std::filesystem::exists(_logpath))return;try{std::filesystem::create_directories(_logpath); // 如果抛异常抛的是:const std::exception 类型的}catch (const std::exception &e) // 用基类捕获所有异常{std::cerr << e.what() << '\n';}}void SyncLog(std::string &message) override{_mutex.lock();std::string logfile = _logpath + _logname;std::ofstream outfile(logfile, std::ios_base::out | std::ios_base::app); // 文件不存在会创建if (!outfile.is_open())return;outfile << message << '\n';_mutex.unlock();}private:std::string _logpath;std::string _logname;std::mutex _mutex;};// 日志主体class Log{public:Log(){UseScreenLogStrategy(); // 默认使用策略 1}~Log(){}void UseScreenLogStrategy(){_logstrategy = std::make_unique<ScreenLogStrategy>();}void UseFileLogStrategy(){_logstrategy = std::make_unique<FileLogStrategy>();}// 日志信息(内置类)// 为了后续实现 Mylog 的 operator() 重载的时候,返回临时变量// 然后利用临时变量的每行生命周期来实现 logmessage 的刷新class LogMessage{public:LogMessage(LogLevel type, std::string &filename, int line, Log& loger): _level(type),_pid(getpid()),_filename(filename),_time(GetTime()),_line(line),_loger(loger){std::stringstream ss;ss  << "[" << _time << "]"<< "[" << Level2String(_level)  << "]"<< "[" << _pid  << "]"<< "[" << _filename  << "]"<< "[" << _line  << "]";_loginfo = ss.str(); // 日志的左半部分}~LogMessage() // 生命周期结束,刷新日志{if(_loger._logstrategy)_loger._logstrategy->SyncLog(_loginfo);}std::string GetTime(){time_t tm = time(nullptr); // 时间戳struct tm curr;localtime_r(&tm, &curr); // 传入时间戳,会输出一个 struct tm 里面记录着时间char timebuffer[64]; // 用来保存格式化后的时间信息snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d %02d:%02d:%02d",curr.tm_year + 1900,curr.tm_mon,curr.tm_mday,curr.tm_hour,curr.tm_min,curr.tm_sec);return timebuffer;}// 重载流插入,为了让 Log 能支持<<// 底层是将插入的日志的右半部分信息添加到 _loginfotemplate <typename T>LogMessage &operator<<(const T &info){std::string ss = info;_loginfo += ss; //return *this; // 返回自己,实现多次 << }private:LogLevel _level;pid_t _pid;std::string _filename;std::string _time;int _line;std::string _loginfo; // 整条日志信息Log &_loger; // 外部类对象,用来调用刷新};// Log 的仿函数,特意返回临时变量// 利用 RAII 的设计特点,创建一个LogMessage临时对象// 在构造的时候,准备好左半部分, 在 << 的时候准备好 右半部分,最后在该行结束时,生命周期结束,刷新日志LogMessage operator()(LogLevel level, std::string filename, int line){return LogMessage(level, filename, line, *this);}private:std::unique_ptr<LogStrategy> _logstrategy;};Log logger; // 定义全局对象// 使⽤宏,可以进⾏代码插⼊,⽅便随时获取⽂件名和⾏号#define LOG(type) logger(type, __FILE__, __LINE__) // __FILE__  和 __LINE__ 可以自动获取文件名和行号// 提供选择使⽤何种日志策略的⽅法#define ENABLE_CONSOLE_LOG_STRATEGY() logger.UseScreenLogStrategy()#define ENABLE_FILE_LOG_STRATEGY() logger.UseFileLogStrategy()
}

3. Main.cpp

测试代码

#include "Mylog.hpp"using namespace tr;
int main()
{// ENABLE_CONSOLE_LOG_STRATEGY();ENABLE_FILE_LOG_STRATEGY();LOG(LogLevel::DEBUG) << "hello world";LOG(LogLevel::ERROR) << "hello world";LOG(LogLevel::FATAL) << "hello world";LOG(LogLevel::INFO) << "hello world";LOG(LogLevel::INFO) << "hello world";return 0;
}
  • ENABLE_CONSOLE_LOG_STRATEGY():选择往屏幕刷新的策略
  • ENABLE_FILE_LOG_STRATEGY():选择往文件里面刷新

4. 运行效果

在这里插入图片描述


🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

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

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

相关文章

C#引用传递代码记录

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace 值传递和引用传递 {internal class Program{static void Main(string[] args){person P1new person();P1.name "张三";person P2 …

React动态渲染:如何用map循环渲染一个列表(List)

React动态渲染&#xff1a;如何用map循环渲染一个列表(List)&#xff1f; 系列回顾&#xff1a; 在上一篇中&#xff0c;我们学习了如何通过onClick等事件处理&#xff0c;让React应用响应用户的操作。现在我们的组件已经能“听懂话”了。但是&#xff0c;目前为止我们展示的内…

React Native【实战范例】水平滚动分类 FlatList

import React from "react"; import { FlatList, SafeAreaView, StyleSheet, Text, View } from "react-native"; interface itemType {id: string;title: string;icon: string; } // 水平滚动数据 const horizontalData: itemType[] [{ id: "1"…

iOS swiftUI的实用举例

SwiftUI 是 Apple 推出的声明式 UI 框架&#xff0c;以下是一些实用技巧和最佳实践&#xff0c;可以帮助你更高效地开发 iOS/macOS/watchOS/tvOS 应用。 1. 布局技巧 灵活的空间占用 // 使用 Spacer 填充可用空间 HStack {Text("Left")Spacer() // 填充中间空间 …

SpringMVC异步处理Servlet

使用SpringMVC异步处理Servlet解决的问题 可以不阻塞有限的tomcat 线程&#xff08;默认是200~250个&#xff0c;springboot3是200个&#xff09;&#xff0c;确保网络请求可以持续响应特定业务使用自定义线程池&#xff0c;可以处理的业务量更大对上层业务完全无感知&#xf…

同步与异步编程范式全景研究——从CPU时钟周期到云原生架构的范式演进

第一章 时空观的根本分歧 1.1 物理时间的约束性 同步操作的本质是对牛顿绝对时间的服从&#xff0c;其阻塞特性源于冯诺依曼体系下指令顺序执行的基因。现代CPU的流水线技术&#xff08;如Intel Hyper-Threading&#xff09;通过指令级并行实现伪异步&#xff0c;但开发者仍需…

【零散技术】5分钟完成Odoo18 登陆页面全自定义

序言:时间是我们最宝贵的财富,珍惜手上的每个时分 从最初的tinyERP到Open ERP&#xff0c;再由OpenERP到Odoo&#xff0c;虽然UI已经过了多次大改&#xff0c;Odoo登录界面依旧丑陋&#xff0c;同时还有各种Odoo版权信息&#xff0c;对于定制项目而言是不友好的。 今天以Odoo18…

Vue3 + TypeScript + Element Plus + el-pagination 分页查询实例分享

前端技术栈&#xff1a;Vue3 TypeScript Element Plus el-pagination 后端技术栈&#xff1a;Java Spring Boot Mybatis 应用异常情况说明&#xff1a;点击页码2&#xff0c;会发送两次请求&#xff0c;并且自动跳回页码1 代码&#xff1a; Reagent.vue <script set…

LoadRunner 2023 安装部署

下载地址&#xff1a;链接: https://caiyun.139.com/w/i/2nQQRYCZ1Ssjl 提取码:3gz0 复制内容打开139-云盘 主要下载Micro_Focus_LoadRunner_2023_Community_Edition.exe来安装就可以。 如要汉化&#xff0c;则再下载安装Language_Packs.exe的安装包 说明&#xff1a;LoadR…

ABC410 : F - Balanced Rectangles

https://atcoder.jp/contests/abc410/tasks/abc410_fhttps://atcoder.jp/contests/abc410/tasks/abc410_f首先可以一眼看出暴力 &#xff1a;枚举左上角和右下角&#xff0c;用前缀和算出矩形中#的数量&#xff0c;判断即可 但这样是,爆!!! 考虑优化&#xff0c;我们可以枚举…

嵌入式学习笔记 - HAL库对外设的封装

一 外设封装结构 HAL库对外设的封装使用了xx_HandleTypeDef类型的外设句柄结构体&#xff0c;这个句柄结构体的第一个成员Instance(xx_TypeDef类型)一般为该外设的所有寄存器的起始基地址&#xff0c;第二个成员Init&#xff08;xx_InitTypeDef类型&#xff09;一般为该外设的设…

高精度模板

加法 P1601 AB Problem&#xff08;高精&#xff09; #include<iostream>using namespace std; const int N 1e6 10; int a[N],b[N],c[N]; int len1,len2,lenMax; //长度要提前定义在全局&#xff0c;在函数中要使用 void add(int c[],int a[],int b[]) {for(int i0…

monorepo使用指北

|  WARN  node_modules is present. Lockfile only installation will make it out-of-date  ERR_PNPM_FETCH_404  GET https://registry.npmjs.org/common%2Fcommon: Not Found - 404 This error happened while installing a direct dependency of G:\monorepo\vue3 comm…

Java八股文——Spring「MyBatis篇」

与传统的JDBC相比&#xff0c;MyBatis的优点&#xff1f; 面试官您好&#xff0c;MyBatis相比于传统的JDBC&#xff0c;它并不是要完全颠覆JDBC&#xff0c;而是作为JDBC的一个强大的“增强框架”。它的核心价值在于&#xff0c;在保留了SQL最大灵活性的前提下&#xff0c;极大…

JavaScript基础-常用的鼠标事件

一、前言 在前端开发中&#xff0c;鼠标事件 是实现用户交互的重要手段之一。通过监听用户的点击、移动、悬停等操作&#xff0c;我们可以构建出丰富而灵活的网页交互体验。 本文将带你深入了解&#xff1a; JavaScript 中常见的鼠标事件&#xff1b;各类鼠标事件的触发时机…

windows录频软件

一.很反感有些做软件的&#xff0c;把别人开源的改个界面收费&#xff0c;所以我找了一个开源免费的。 二.准备工具 一台电脑&#xff0c; Captura:完全开源免费的录频软件。 ffmpeg&#xff1a;音频格式转换软件&#xff0c;这可是非常大名鼎鼎的工具。 三.安装Captura 网址…

python中的模块化编程:日期模块、math算术模块、random模块

内置模块&#xff08;math、random、时间&#xff09;自定义模块&#xff08;自己写的部分代码&#xff09;第三方模块&#xff08;引入的第三方代码库的模块&#xff09; math模块 import math#圆周率 print(math.pi) #自然常数 print(math.e) #圆周率的二倍 print(math.tau…

【学习笔记】Langchain基础(二)

前文&#xff1a;【学习笔记】Langchain基础 文章目录 8 [LangGraph] 实现 Building Effective Agents&#xff0c;各种 workflows 及 AgentAugmented LLMPrompt ChainingParallelizationRoutingOrchestrator-Worker (协调器-工作器)Evaluator-optimizer (Actor-Critic)Agent 8…

Java大模型开发入门 (9/15):连接外部世界(中) - 向量嵌入与向量数据库

前言 在上一篇文章中&#xff0c;我们成功地将一篇长文档加载并分割成了一系列小的文本片段&#xff08;TextSegment&#xff09;。我们现在有了一堆“知识碎片”&#xff0c;但面临一个新问题&#xff1a;计算机如何理解这些碎片的内容&#xff0c;并找出与用户问题最相关的片…

Windows下MySQL安装全流程图文教程及客户端使用指南(付整合安装包)

本教程是基于5.7版本安装&#xff0c;5.7和8.0的安装过程大差不差 安装包「windows上mysql中安装包资源」 链接&#xff1a;https://pan.quark.cn/s/de275899936d 一、安装前的准备 1.1 获取 MySQL 安装程序 官网 前往 MySQL 官方下载页面&#xff0c;下载适用于 Windows 系…