手寫muduo網絡庫(一):項目構建和時間戳、日志庫

引言

本文作為手寫 muduo 網絡庫系列開篇,聚焦項目基礎框架搭建與核心基礎工具模塊設計。通過解析 CMake 工程結構設計、目錄規劃原則,結合時間戳與日志系統的架構,為后續網絡庫開發奠定工程化基礎。文中附完整 CMake 配置示例及模塊代碼。?

代碼參考自:https://github.com/youngyangyang04/muduo-core
部分代碼經過修改

一、項目工程化構建:從目錄規劃到編譯配置

1. 分層目錄結構設計

mymuduo/  
├─ src/          # 核心庫源代碼(實現文件)  ├─ CMakeLists.txt #
├─ include/      # 公共頭文件(供外部引用)  
├─ build/        # 編譯輸出目錄(生成庫文件與可執行程序)  
├─ example/      # 示例程序(驗證庫功能)  ├─ CMakeLists.txt #
├─ CMakeLists.txt # 根目錄編譯配置  
└─ lib/          # 靜態庫/動態庫輸出目錄(自動生成)  

設計說明:

  • src 與 include 分離:遵循 “接口與實現分離” 原則,頭文件僅暴露必要 API,隱藏實現細節
  • build 獨立輸出:避免編譯產物污染源碼目錄,支持cmake .. -B build的外部構建模式
  • example 驗證層:通過具體場景測試庫功能,便于快速調試與功能迭代

?2. 根目錄 CMake 配置解析

cmake_minimum_required(VERSION 3.0)  
project(mymuduo)  # 設置C++11標準(muduo原生基于C++03,此處升級為現代C++)  
set(CMAKE_CXX_STANDARD 11)  
set(CMAKE_CXX_STANDARD_REQUIRED ON)  # 統一庫文件輸出路徑  
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)  # 全局依賴庫(如多線程支持)  
set(LIBS pthread)  # 模塊化構建:遞歸編譯子目錄  
add_subdirectory(src)  
add_subdirectory(example)  

關鍵配置點:

  • CMAKE_CXX_STANDARD:強制要求 C++11 編譯環境,支持 Lambda、智能指針等現代特性
  • LIBRARY_OUTPUT_PATH:集中管理庫文件,便于后續集成時統一引用
  • add_subdirectory:通過分模塊編譯,實現 “核心庫 - 示例程序” 的解耦構建

3. 核心庫模塊(src 目錄)編譯配置

# 自動收集當前目錄所有.cpp文件  
file(GLOB SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)  # 生成動態庫(可通過SHARED/STATIC切換庫類型)  
add_library(mymuduo SHARED ${SRC_FILES})  # 暴露頭文件路徑(供外部target引用)  
target_include_directories(mymuduo PUBLIC ${CMAKE_SOURCE_DIR}/include)  

設計考量:

  • 動態庫優先:SHARED 模式便于運行時動態加載,適合需要頻繁升級的庫開發
  • PUBLIC 頭文件:通過 PUBLIC 關鍵字,確保依賴 mymuduo 庫的目標自動包含頭文件路徑

4. 示例程序(example 目錄)編譯配置?

file(GLOB EXAMPLE_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)  
add_executable(testserver ${EXAMPLE_SRCS})  # 鏈接核心庫與全局依賴  
target_link_libraries(testserver mymuduo ${LIBS})  # 編譯選項配置  
target_compile_options(testserver PRIVATE -std=c++11 -Wall)  # 可執行文件輸出到當前目錄  
set_target_properties(testserver PROPERTIES  RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}  
)  

驗證流程設計:

  • 獨立編譯目標:testserver 可執行程序直接依賴 mymuduo 庫,方便單步調試
  • 編譯選項增強:-Wall 開啟嚴格編譯警告,幫助提前發現潛在問題

?二、時間戳

1. 概述

時間戳(Timestamp)是一個表示特定時刻的數值,通常用于記錄事件發生的時間。在本代碼庫中,Timestamp?類提供了一種簡單的方式來處理時間戳,它可以獲取當前時間的時間戳,并將時間戳轉換為可讀的字符串格式。

2. 類定義(Timestamp.h)?

#pragma once#include <iostream>
#include <string>
namespace mymuduo
{namespace base{class Timestamp{public:Timestamp();explicit Timestamp(int64_t microSecondsSinceEpoch);static Timestamp now();std::string toString() const;private:int64_t microSecondsSinceEpoch_;};}
}
  • 命名空間:代碼使用了嵌套命名空間?mymuduo::base,這樣可以避免命名沖突,提高代碼的可維護性。
  • 構造函數
    • Timestamp():默認構造函數,將?microSecondsSinceEpoch_?初始化為 0。
    • explicit Timestamp(int64_t microSecondsSinceEpoch):帶參數的構造函數,使用傳入的微秒數初始化?microSecondsSinceEpoch_explicit?關鍵字用于防止隱式類型轉換。
  • 靜態成員函數
    • static Timestamp now():靜態函數,用于獲取當前時間的?Timestamp?對象。
  • 成員函數
    • std::string toString() const:將時間戳轉換為可讀的字符串格式。

3. 類實現(Timestamp.cpp)?

#include <time.h>#include "Timestamp.h"
using namespace mymuduo::base;
Timestamp::Timestamp() : microSecondsSinceEpoch_(0)
{
}Timestamp::Timestamp(int64_t microSecondsSinceEpoch): microSecondsSinceEpoch_(microSecondsSinceEpoch)
{
}Timestamp Timestamp::now()
{return Timestamp(time(NULL));
}
std::string Timestamp::toString() const
{char buf[128] = {0};tm *tm_time = localtime(&microSecondsSinceEpoch_);snprintf(buf, 128, "%4d/%02d/%02d %02d:%02d:%02d",tm_time->tm_year + 1900,tm_time->tm_mon + 1,tm_time->tm_mday,tm_time->tm_hour,tm_time->tm_min,tm_time->tm_sec);return buf;
}//g++ -o test timestamp.cpp -I ../include  //g++編譯命令
//測試代碼
// #include <iostream>
// int main() {
//     std::cout << Timestamp::now().toString() << std::endl;
//     return 0;
// }
  • 構造函數實現
    • Timestamp():將?microSecondsSinceEpoch_?初始化為 0。
    • Timestamp(int64_t microSecondsSinceEpoch):使用傳入的微秒數初始化?microSecondsSinceEpoch_
  • now()?函數實現
    • 使用?time(NULL)?函數獲取當前時間的秒數,并創建一個?Timestamp?對象返回。
  • toString()?函數實現
    • 使用?localtime()?函數將時間戳轉換為本地時間的?tm?結構體。
    • 使用?snprintf()?函數將?tm?結構體中的年、月、日、時、分、秒格式化為字符串。
    • 返回格式化后的字符串。

4. 代碼示例及編譯指令

代碼文件中提供了一個簡單的測試示例,用于驗證?Timestamp?類的功能:

#include <iostream>
int main() {std::cout << Timestamp::now().toString() << std::endl;return 0;
}

編譯指令為:

g++ -o test timestamp.cpp -I ../include 

這個指令將?timestamp.cpp?文件編譯成可執行文件?test,并指定頭文件搜索路徑為?../include

?結果輸出:

? 三、日志

1. 整體概述

本日志系統采用了?iostream?風格,這與 muduo 原版日志有所不同。iostream?風格提供了一種直觀且易于使用的方式來格式化和輸出日志信息,通過重載?<<?運算符,使得日志記錄代碼更加簡潔和直觀。

2. 核心組件

LogStream 類

LogStream?類是日志系統的核心,它負責格式化日志信息并將其輸出。以下是?LogStream?類的關鍵特性:

  • 構造函數:在構造時,它會添加日志的基本信息,如時間戳、線程 ID、日志級別、文件名、行號和函數名。
LogStream::LogStream(Logger *loger, const char* file, int line, LogLevel l, const char* func)
:logger_(loger)
{const char* file_name = strrchr(file,'/');if(file_name){file_name = file_name + 1;}else{file_name = file;}stream_ << Timestamp::now().toString() << "[pid]:";if(thread_id == 0){thread_id = static_cast<pid_t>(::syscall(SYS_gettid));}stream_ << thread_id;stream_ << log_string[l];stream_ << "[" << file_name << ":" << line << "]";if(func){stream_ << "[" << func << "]";}
}
  • 析構函數:在析構時,它會添加換行符,并將格式化好的日志信息傳遞給?Logger?類進行輸出。
LogStream::~LogStream()
{stream_ << "\n";if(logger_){logger_->Write(stream_.str());}else{std::cout << stream_.str() << std::endl ;}
}
  • 重載?<<?運算符:通過模板函數重載?<<?運算符,允許用戶以?iostream?風格添加任意類型的數據到日志流中。
template<class T> LogStream& operator<<(const T& value)
{stream_ << value;return *this;
}

Logger 類(和muduo相同只實現最簡單控制臺的日志輸出,可以修改為帶有日志旋轉的文件日志)

Logger?類負責管理日志級別和輸出日志信息。它提供了以下接口:

  • SetLogLevel:設置日志級別。
void Logger::SetLogLevel(const LogLevel & level)
{level_ = level;
}
  • GetLogLevel:獲取當前日志級別
LogLevel Logger::GetLogLevel() const
{return level_;
}
  • Write:將格式化好的日志信息輸出到標準輸出。
void Logger::Write(const std::string &msg)
{std::cout << msg;
}

3. 日志宏定義

為了方便使用,日志系統提供了一系列宏定義,用于不同級別的日志記錄:

#define LOG_TRACE  \if(g_logger&&mymuduo::base::g_logger->GetLogLevel() <= mymuduo::base::kTrace) \mymuduo::base::LogStream(mymuduo::base::g_logger,__FILE__,__LINE__,mymuduo::base::kTrace,__func__)#define LOG_DEBUG  \if(g_logger&&mymuduo::base::g_logger->GetLogLevel() <= mymuduo::base::kDebug) \mymuduo::base::LogStream(mymuduo::base::g_logger,__FILE__,__LINE__,mymuduo::base::kDebug,__func__)#define LOG_INFO  \if(g_logger&&mymuduo::base::g_logger->GetLogLevel() <= mymuduo::base::kInfo) \mymuduo::base::LogStream(mymuduo::base::g_logger,__FILE__,__LINE__,mymuduo::base::kInfo)#define LOG_WARN  \mymuduo::base::LogStream(mymuduo::base::g_logger,__FILE__,__LINE__,mymuduo::base::kWarn)#define LOG_ERROR  mymuduo::base::LogStream(mymuduo::base::g_logger,__FILE__,__LINE__,mymuduo::base::kError)

4. 使用示例

以下是一個簡單的使用示例,展示了如何使用日志系統:

int main()
{g_logger = new Logger();g_logger->SetLogLevel(kInfo);LOG_INFO << "test info";return 0;
}

?編譯指令:

g++ -o testlog Logger.cpp Timestamp.cpp LogStream.cpp -I ../include

結果輸出:

2025/06/06 17:38:15[pid]:8294 INFO [LogStream.cpp:66]test info

附錄

?Timestamp.h

#pragma once#include <iostream>
#include <string>
namespace mymuduo
{namespace base{class Timestamp{public:Timestamp();explicit Timestamp(int64_t microSecondsSinceEpoch);static Timestamp now();std::string toString() const;private:int64_t microSecondsSinceEpoch_;};}
}

Timestamp.cpp

#include <time.h>#include "Timestamp.h"
using namespace mymuduo::base;
Timestamp::Timestamp() : microSecondsSinceEpoch_(0)
{
}Timestamp::Timestamp(int64_t microSecondsSinceEpoch): microSecondsSinceEpoch_(microSecondsSinceEpoch)
{
}Timestamp Timestamp::now()
{return Timestamp(time(NULL));
}
std::string Timestamp::toString() const
{char buf[128] = {0};tm *tm_time = localtime(&microSecondsSinceEpoch_);snprintf(buf, 128, "%4d/%02d/%02d %02d:%02d:%02d",tm_time->tm_year + 1900,tm_time->tm_mon + 1,tm_time->tm_mday,tm_time->tm_hour,tm_time->tm_min,tm_time->tm_sec);return buf;
}//g++ -o test timestamp.cpp -I ../include 
// #include <iostream>
// int main() {
//     std::cout << Timestamp::now().toString() << std::endl;
//     return 0;
// }

LogStream.h

#pragma once #include "Logger.h"#include <sstream>namespace mymuduo
{namespace base{extern Logger* g_logger;class LogStream{public:LogStream(Logger *loger, const char* file, int line, LogLevel l, const char* func=nullptr);~LogStream();template<class T> LogStream& operator<<(const T& value){stream_ << value;return *this;}private:std::ostringstream stream_;Logger* logger_{nullptr};};}
}#define LOG_TRACE  \if(g_logger&&mymuduo::base::g_logger->GetLogLevel() <= mymuduo::base::kTrace) \mymuduo::base::LogStream(mymuduo::base::g_logger,__FILE__,__LINE__,mymuduo::base::kTrace,__func__)#define LOG_DEBUG  \if(g_logger&&mymuduo::base::g_logger->GetLogLevel() <= mymuduo::base::kDebug) \mymuduo::base::LogStream(mymuduo::base::g_logger,__FILE__,__LINE__,mymuduo::base::kDebug,__func__)#define LOG_INFO  \if(g_logger&&mymuduo::base::g_logger->GetLogLevel() <= mymuduo::base::kInfo) \mymuduo::base::LogStream(mymuduo::base::g_logger,__FILE__,__LINE__,mymuduo::base::kInfo)#define LOG_WARN  \mymuduo::base::LogStream(mymuduo::base::g_logger,__FILE__,__LINE__,mymuduo::base::kWarn)#define LOG_ERROR  mymuduo::base::LogStream(mymuduo::base::g_logger,__FILE__,__LINE__,mymuduo::base::kError)

?LogStream.cpp

#include "LogStream.h"
#include "Timestamp.h"#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <iostream>using namespace mymuduo::base;
Logger* mymuduo::base::g_logger = nullptr;static thread_local pid_t thread_id = 0;
const char* log_string[] = {" TRACE "," DEBUG "," INFO "," WARN "," ERROR ",
};LogStream::LogStream(Logger *loger, const char* file, int line, LogLevel l, const char* func)
:logger_(loger)
{const char* file_name = strrchr(file,'/');if(file_name){file_name = file_name + 1;}else{file_name = file;}stream_ << Timestamp::now().toString() << "[pid]:";if(thread_id == 0){thread_id = static_cast<pid_t>(::syscall(SYS_gettid));}stream_ << thread_id;stream_ << log_string[l];stream_ << "[" << file_name << ":" << line << "]";if(func){stream_ << "[" << func << "]";}
}LogStream::~LogStream()
{stream_ << "\n";if(logger_){logger_->Write(stream_.str());}else{std::cout << stream_.str() << std::endl ;}
}// //g++ -o testlog Logger.cpp Timestamp.cpp LogStream.cpp -I ../include
// int main()
// {
//     g_logger = new Logger();
//     g_logger->SetLogLevel(kInfo);
//     LOG_INFO << "test info";
//     return 0;
// }

Logger.h

#pragma once
#include "NonCopyable.h"#include <string>namespace mymuduo
{namespace base{enum LogLevel{kTrace,kDebug,kInfo,kWarn,kError,kMaxNumOfLogLevel,};class Logger: public NonCopyable{public:Logger() = default;~Logger() = default;void SetLogLevel(const LogLevel & level);LogLevel GetLogLevel() const;void Write(const std::string &msg);private:LogLevel level_ {kDebug};};}
}

Logger.cpp

#include "Logger.h"#include <iostream>using namespace mymuduo::base;void Logger::SetLogLevel(const LogLevel & level)
{level_ = level;
}LogLevel Logger::GetLogLevel() const
{return level_;
}void Logger::Write(const std::string &msg)
{std::cout << msg;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/83064.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/83064.shtml
英文地址,請注明出處:http://en.pswp.cn/web/83064.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

NLP學習路線圖(三十二): 模型壓縮與優化

一、 核心壓縮與優化技術詳解 1. 知識蒸餾:智慧的傳承(Knowledge Distillation, KD) 核心思想:“師授徒業”。訓練一個龐大、高性能但笨重的“教師模型”(Teacher Model),讓其指導訓練一個輕量級的“學生模型”(Student Model)。學生模型學習模仿教師模型的輸出行為(…

vue前端字典映射

1.界面展示 2.圖中狀態字段接收的數據如下 3.代碼轉換&#xff0c;添加計算屬性代碼 再在綁定屬性的地方做轉換 computed: {statusMap() {return {"-1": "已退號",1: "掛號",2: "接診",3: "已完診",};},},<m-input:spa…

基于 llama-factory進行模型微調

# GLM4-9B-chat Lora 微調. 介紹如何基于 llama-factory 框架&#xff0c;對 glm-4-9b-chat 模型進行 Lora 微調。Lora 是一種高效微調方法&#xff0c;深入了解其原理可參見博客&#xff1a;[知乎|深入淺出 Lora](https://zhuanlan.zhihu.com/p/650197598)。 ## 環境配置 在完…

不到 2 個月,OpenAI 火速用 Rust 重寫 AI 編程工具。尤雨溪也覺得 Rust 香!

一、OpenAI 用 Rust 重寫 Codex CLI OpenAI 已用 Rust 語言重寫了其 AI 命令行編程工具 Codex CLI&#xff0c;理由是此舉能提升性能和安全性&#xff0c;同時避免對 Node.js 的依賴。他們認為 Node.js “可能讓部分用戶感到沮喪或成為使用障礙”。 Codex 是一款實驗性編程代理…

Go 并發編程深度指南

Go 并發編程深度指南 Go 語言以其內置的并發原語而聞名&#xff0c;通過 goroutine 和 channel 提供了一種高效、安全的并發編程模型。本文將全面解析 Go 的并發機制及其實際應用。 核心概念&#xff1a;Goroutines 和 Channels 1. Goroutines (協程) Go 的輕量級線程實現&…

vue和uniapp聊天頁面右側滾動條自動到底部

1.vue右側滾動條自動到底部 <div ref"newMessage1"></div> <!-- 定義<div ref"newMessage1"></div>與<div v-for”item in list“>循環同級定義-->定義方法 scrollToBottomCenter(){this.$nextTick(() > {this.$re…

iOS 項目怎么構建穩定性保障機制?一次系統性防錯經驗分享(含 KeyMob 工具應用)

崩潰、內存飆升、后臺任務未釋放、頁面卡頓、日志丟失——穩定性問題&#xff0c;不一定會立刻崩&#xff0c;但一旦積累&#xff0c;就是“上線后救不回來的代價”。 穩定性保障不是某個工具的功能&#xff0c;而是一套貫穿開發、測試、上線全流程的“觀測分析防范”機制。 …

JMeter函數整理

"_csvRead"函數 csvRead函數是從外部讀取參數&#xff0c;csvRead函數可以從一個文件中讀取多個參數。 下面具體講一下如何使用csvread函數&#xff1a; 1.新建一個csv或者text文件&#xff0c;里面保存要讀取的參數&#xff0c;每個參數間用逗號相隔。每行表示每一組…

深入理解 React Hooks

在當今的 React 開發中,Hooks 已經成為構建函數組件的核心工具。自 React 16.8 版本引入以來,Hooks 徹底改變了開發者編寫 React 組件的方式,使得狀態管理和副作用處理變得更加簡潔和直觀。本文將全面介紹 React 提供的各種 Hooks,從基礎的 useState 和 useEffect,到高級的…

Doris-2:單虛擬機上非docker化安裝Doris實驗環境

Doris-2:單虛擬機上非docker化安裝Doris實驗環境 1.安裝1.1.環境說明1.2.基礎準備1.2.1.JDK1.2.2.操作系統配置(使用root或者有權賬戶)1.2.2.1.修改環境變量1.2.2.2.修改虛擬內存區域1.2.2.3.關閉swap1.2.2.4.關閉防火墻1.2.2.5.創建用戶和組1.3.安裝doris1.3.1.解壓1.3.2.配置…

C# SqlSugar:依賴注入與倉儲模式實踐

C# SqlSugar&#xff1a;依賴注入與倉儲模式實踐 在 C# 的應用開發中&#xff0c;數據庫操作是必不可少的環節。為了讓數據訪問層更加簡潔、高效且易于維護&#xff0c;許多開發者會選擇成熟的 ORM&#xff08;對象關系映射&#xff09;框架&#xff0c;SqlSugar 就是其中備受…

Razor編程中@Helper的用法大全

文章目錄 第一章&#xff1a;Helper基礎概念1.1 Helper的定義與作用1.2 Helper的基本語法結構1.3 Helper與HtmlHelper的區別 第二章&#xff1a;基礎Helper用法2.1 無參數Helper2.2 帶簡單參數的Helper2.3 帶默認值的參數2.4 使用模型作為參數 第三章&#xff1a;高級Helper用法…

Python-正則表達式(re 模塊)

目錄 一、re 模塊的使用過程二、正則表達式的字符匹配1. 匹配開頭結尾2. 匹配單個字符3. 匹配多個字符4. 匹配分組5. Python 代碼示例 三、re 模塊的函數1. 函數一覽表2. Python 代碼示例1&#xff09;search 與 finditer2&#xff09;findall3&#xff09;sub4&#xff09;spl…

前端知識導圖

前端知識導圖 參考&#xff1a;字節標準 前端知識導圖 通用基礎 1、編程語言 HTML CSS JS TS 2、計算機基礎 計算機網略 數據結構 算法&#xff1a;二分查找、十大排序、二叉樹先中后和層次遍歷、集合交并集、leetcode刷題經驗 編譯構建 webpack & vite 應用基礎 開…

moon游戲服務器-demo運行

下載地址 https://github.com/sniper00/MoonDemo redis安裝 Redis-x64-3.0.504.msi 服務器配置文件 D:\gitee\moon_server_demo\serverconf.lua 貌似不修改也可以的&#xff0c;redis不要設置密碼 windows編譯 安裝VS2022 Community 下載premake5.exe放MoonDemo\server\moon 雙…

Webpack性能優化:構建速度與體積優化策略

一、構建速度優化 1、??升級Webpack和Node.js?? ??優化效果??&#xff1a;Webpack 4比Webpack 3構建時間降低60%-98%。??原因??&#xff1a; V8引擎優化&#xff08;for of替代forEach、Map/Set替代Object&#xff09;。默認使用更快的md4哈希算法。AST直接從Loa…

ajax學習手冊

Ajax 通俗易懂學習手冊 目錄 Ajax 基礎概念XMLHttpRequest 詳解Fetch API (現代方式)處理不同數據格式錯誤處理和狀態碼Ajax 高級技巧實戰項目案例最佳實踐 Ajax 基礎概念 什么是 Ajax&#xff1f; Ajax Asynchronous JavaScript And XML 通俗解釋&#xff1a; Ajax 就像…

人工智能學習02-安裝環境

人工智能學習概述—快手視頻 人工智能學習02-安裝—快手視頻 Python安裝 Python安裝分為兩種方法&#xff0c;一是從官網(https://www.python.org/)下載Python工具(比如python-2.7.msi)進行安裝&#xff0c;并設置Path環境變量&#xff1b;二是下載工具Anaconda集成環境進行安…

電腦開不了機,主板顯示67碼解決過程

文章目錄 現象分析內存條問題BIOS設置問題其它問題 解決清理內存條金手指所需工具操作步驟注意事項 電腦在運行過程中&#xff0c;顯示內存不足&#xff0c;重啟電腦卻無法啟動。 現象 System Initialization 主板風扇是轉的&#xff0c;也有燈光顯示&#xff0c;插上屏幕&am…

在ubuntu等linux系統上申請https證書

使用 Certbot 自動申請 安裝 Certbot Certbot 是 Let’s Encrypt 官方推薦的自動化工具&#xff0c;支持多種操作系統和服務器環境。 在 Ubuntu/Debian 上&#xff1a; sudo apt update sudo apt install certbot申請證書 純手動方式&#xff08;不自動配置&#xff09;&…