【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
繁體地址,請注明出處:http://hk.pswp.cn/diannao/87128.shtml
英文地址,請注明出處:http://en.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 系…