手写muduo网络库(一):项目构建和时间戳、日志库

[复制链接]
发表于 2025-6-8 09:20:38 | 显示全部楼层 |阅读模式

弁言

本文作为手写 muduo 网络库系列开篇,聚焦项目根本框架搭建与焦点根本工具模块设计。通过解析 CMake 工程结构设计、目录规划原则,联适时间戳与日志日志系统的架构,为后续网络库开发奠定工程化根本。文中附完整 CMake 设置示例及模块代码。 
   代码参考自:https://github.com/youngyangyang04/muduo-core
部门代码颠末修改
  一、项目工程化构建:从目录规划到编译设置

1. 分层目录结构设计

  1. mymuduo/  
  2. ├─ src/          # 核心库源代码(实现文件)  
  3.     ├─ CMakeLists.txt #
  4. ├─ include/      # 公共头文件(供外部引用)  
  5. ├─ build/        # 编译输出目录(生成库文件与可执行程序)  
  6. ├─ example/      # 示例程序(验证库功能)  
  7.     ├─ CMakeLists.txt #
  8. ├─ CMakeLists.txt # 根目录编译配置  
  9. └─ lib/          # 静态库/动态库输出目录(自动生成)  
复制代码
设计阐明:


  • src 与 include 分离:遵循 “接口与实现分离” 原则,头文件仅暴露须要 API,隐藏实现细节
  • build 独立输出:避免编译产物污染源码目录,支持cmake .. -B build的外部构建模式
  • example 验证层:通过详细场景测试库功能,便于快速调试与功能迭代
 2. 根目录 CMake 设置解析

  1. cmake_minimum_required(VERSION 3.0)  
  2. project(mymuduo)  
  3. # 设置C++11标准(muduo原生基于C++03,此处升级为现代C++)  
  4. set(CMAKE_CXX_STANDARD 11)  
  5. set(CMAKE_CXX_STANDARD_REQUIRED ON)  
  6. # 统一库文件输出路径  
  7. set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)  
  8. # 全局依赖库(如多线程支持)  
  9. set(LIBS pthread)  
  10. # 模块化构建:递归编译子目录  
  11. add_subdirectory(src)  
  12. add_subdirectory(example)  
复制代码
关键设置点:


  • CMAKE_CXX_STANDARD:逼迫要求 C++11 编译环境,支持 Lambda、智能指针等现代特性
  • LIBRARY_OUTPUT_PATH:集中管理库文件,便于后续集成时同一引用
  • add_subdirectory:通过分模块编译,实现 “焦点库 - 示例程序” 的解耦构建
3. 焦点库模块(src 目录)编译设置

  1. # 自动收集当前目录所有.cpp文件  
  2. file(GLOB SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)  
  3. # 生成动态库(可通过SHARED/STATIC切换库类型)  
  4. add_library(mymuduo SHARED ${SRC_FILES})  
  5. # 暴露头文件路径(供外部target引用)  
  6. target_include_directories(mymuduo PUBLIC ${CMAKE_SOURCE_DIR}/include)  
复制代码
设计考量:


  • 动态库优先:SHARED 模式便于运行时动态加载,恰当必要频仍升级的库开发
  • PUBLIC 头文件:通过 PUBLIC 关键字,确保依赖 mymuduo 库的目的主动包罗头文件路径
4. 示例程序(example 目录)编译设置 

  1. file(GLOB EXAMPLE_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)  
  2. add_executable(testserver ${EXAMPLE_SRCS})  
  3. # 链接核心库与全局依赖  
  4. target_link_libraries(testserver mymuduo ${LIBS})  
  5. # 编译选项配置  
  6. target_compile_options(testserver PRIVATE -std=c++11 -Wall)  
  7. # 可执行文件输出到当前目录  
  8. set_target_properties(testserver PROPERTIES  
  9.     RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}  
  10. )  
复制代码
验证流程设计:


  • 独立编译目的:testserver 可实验程序直接依赖 mymuduo 库,方便单步调试
  • 编译选项增强:-Wall 开启严格编译告诫,帮助提前发现潜在问题
 二、时间戳

1. 概述

时间戳(Timestamp)是一个表示特定时候的数值,通常用于记录事件发生的时间。在本代码库中,Timestamp 类提供了一种简朴的方式来处理时间戳,它可以获取当前时间的时间戳,并将时间戳转换为可读的字符串格式。
2. 类定义(Timestamp.h) 

  1. #pragma once
  2. #include <iostream>
  3. #include <string>
  4. namespace mymuduo
  5. {
  6.     namespace base
  7.     {
  8.         class Timestamp
  9.         {
  10.         public:
  11.             Timestamp();
  12.             explicit Timestamp(int64_t microSecondsSinceEpoch);
  13.             static Timestamp now();
  14.             std::string toString() const;
  15.         private:
  16.             int64_t microSecondsSinceEpoch_;
  17.         };
  18.     }
  19. }
复制代码


  • 命名空间:代码利用了嵌套命名空间 mymuduo::base,如允许以避免命名辩论,进步代码的可维护性。
  • 构造函数

    • Timestamp():默认构造函数,将 microSecondsSinceEpoch_ 初始化为 0。
    • explicit Timestamp(int64_t microSecondsSinceEpoch):带参数的构造函数,利用传入的微秒数初始化 microSecondsSinceEpoch_。explicit 关键字用于防止隐式类型转换。

  • 静态成员函数

    • static Timestamp now():静态函数,用于获取当前时间的 Timestamp 对象。

  • 成员函数

    • std::string toString() const:将时间戳转换为可读的字符串格式。

3. 类实现(Timestamp.cpp) 

  1. #include <time.h>
  2. #include "Timestamp.h"
  3. using namespace mymuduo::base;
  4. Timestamp::Timestamp() : microSecondsSinceEpoch_(0)
  5. {
  6. }
  7. Timestamp::Timestamp(int64_t microSecondsSinceEpoch)
  8.     : microSecondsSinceEpoch_(microSecondsSinceEpoch)
  9. {
  10. }
  11. Timestamp Timestamp::now()
  12. {
  13.     return Timestamp(time(NULL));
  14. }
  15. std::string Timestamp::toString() const
  16. {
  17.     char buf[128] = {0};
  18.     tm *tm_time = localtime(&microSecondsSinceEpoch_);
  19.     snprintf(buf, 128, "%4d/%02d/%02d %02d:%02d:%02d",
  20.              tm_time->tm_year + 1900,
  21.              tm_time->tm_mon + 1,
  22.              tm_time->tm_mday,
  23.              tm_time->tm_hour,
  24.              tm_time->tm_min,
  25.              tm_time->tm_sec);
  26.     return buf;
  27. }
  28. //g++ -o test timestamp.cpp -I ../include  //g++编译命令
  29. //测试代码
  30. // #include <iostream>
  31. // int main() {
  32. //     std::cout << Timestamp::now().toString() << std::endl;
  33. //     return 0;
  34. // }
复制代码


  • 构造函数实现

    • Timestamp():将 microSecondsSinceEpoch_ 初始化为 0。
    • Timestamp(int64_t microSecondsSinceEpoch):利用传入的微秒数初始化 microSecondsSinceEpoch_。

  • now() 函数实现

    • 利用 time(NULL) 函数获取当前时间的秒数,并创建一个 Timestamp 对象返回。

  • toString() 函数实现

    • 利用 localtime() 函数将时间戳转换为当地时间的 tm 结构体。
    • 利用 snprintf() 函数将 tm 结构体中的年、月、日、时、分、秒格式化为字符串。
    • 返回格式化后的字符串。

4. 代码示例及编译指令

代码文件中提供了一个简朴的测试示例,用于验证 Timestamp 类的功能:
  1. #include <iostream>
  2. int main() {
  3.     std::cout << Timestamp::now().toString() << std::endl;
  4.     return 0;
  5. }
复制代码
编译指令为:
  1. g++ -o test timestamp.cpp -I ../include
复制代码
这个指令将 timestamp.cpp 文件编译成可实验文件 test,并指定头文件搜索路径为 ../include。
 结果输出:

  三、日志日志

1. 团体概述

日志日志系统接纳了 iostream 风格,这与 muduo 原版日志有所不同。iostream 风格提供了一种直观且易于利用的方式来格式化和输出日志信息,通过重载 << 运算符,使得日志记录代码更加简便和直观。
2. 焦点组件

LogStream 类

LogStream 类是日志系统的焦点,它负责格式化日志信息并将其输出。以下是 LogStream 类的关键特性:


  • 构造函数:在构造时,它会添加日志的根本信息,如时间戳、线程 ID、日志级别、文件名、行号和函数名。
  1. LogStream::LogStream(Logger *loger, const char* file, int line, LogLevel l, const char* func)
  2. :logger_(loger)
  3. {
  4.     const char* file_name = strrchr(file,'/');
  5.     if(file_name)
  6.     {
  7.         file_name = file_name + 1;
  8.     }
  9.     else
  10.     {
  11.         file_name = file;
  12.     }
  13.     stream_ << Timestamp::now().toString() << "[pid]:";
  14.     if(thread_id == 0)
  15.     {
  16.         thread_id = static_cast<pid_t>(::syscall(SYS_gettid));
  17.     }
  18.     stream_ << thread_id;
  19.     stream_ << log_string[l];
  20.     stream_ << "[" << file_name << ":" << line << "]";
  21.     if(func)
  22.     {
  23.         stream_ << "[" << func << "]";
  24.     }
  25. }
复制代码


  • 析构函数:在析构时,它会添加换行符,并将格式化好的日志信息传递给 Logger 类举行输出。
  1. LogStream::~LogStream()
  2. {
  3.     stream_ << "\n";
  4.     if(logger_)
  5.     {
  6.         logger_->Write(stream_.str());
  7.     }
  8.     else
  9.     {
  10.         std::cout << stream_.str() << std::endl ;
  11.     }
  12. }
复制代码


  • 重载 << 运算符:通过模板函数重载 << 运算符,允许用户以 iostream 风格添加任意类型的数据到日志流中。
  1. template<class T> LogStream& operator<<(const T& value)
  2. {
  3.     stream_ << value;
  4.     return *this;
  5. }
复制代码
Logger 类(和muduo相同只实现最简朴控制台的日志输出,可以修改为带有日志旋转的文件日志)

Logger 类负责管理日志级别和输出日志信息。它提供了以下接口:


  • SetLogLevel:设置日志级别。
  1. void Logger::SetLogLevel(const LogLevel & level)
  2. {
  3.     level_ = level;
  4. }
复制代码


  • GetLogLevel:获取当前日志级别
  1. LogLevel Logger::GetLogLevel() const
  2. {
  3.     return level_;
  4. }
复制代码


  • Write:将格式化好的日志信息输出到尺度输出。
  1. void Logger::Write(const std::string &msg)
  2. {
  3.     std::cout << msg;
  4. }
复制代码
3. 日志宏定义

为了方便利用,日志系统提供了一系列宏定义,用于不同级别的日志记录:
  1. #define LOG_TRACE  \
  2.     if(g_logger&&mymuduo::base::g_logger->GetLogLevel() <= mymuduo::base::kTrace) \
  3.         mymuduo::base::LogStream(mymuduo::base::g_logger,__FILE__,__LINE__,mymuduo::base::kTrace,__func__)
  4. #define LOG_DEBUG  \
  5.     if(g_logger&&mymuduo::base::g_logger->GetLogLevel() <= mymuduo::base::kDebug) \
  6.         mymuduo::base::LogStream(mymuduo::base::g_logger,__FILE__,__LINE__,mymuduo::base::kDebug,__func__)
  7. #define LOG_INFO  \
  8.     if(g_logger&&mymuduo::base::g_logger->GetLogLevel() <= mymuduo::base::kInfo) \
  9.         mymuduo::base::LogStream(mymuduo::base::g_logger,__FILE__,__LINE__,mymuduo::base::kInfo)
  10. #define LOG_WARN  \
  11.         mymuduo::base::LogStream(mymuduo::base::g_logger,__FILE__,__LINE__,mymuduo::base::kWarn)
  12. #define LOG_ERROR  mymuduo::base::LogStream(mymuduo::base::g_logger,__FILE__,__LINE__,mymuduo::base::kError)
复制代码
4. 利用示例

以下是一个简朴的利用示例,展示了怎样利用日志系统:
  1. int main()
  2. {
  3.     g_logger = new Logger();
  4.     g_logger->SetLogLevel(kInfo);
  5.     LOG_INFO << "test info";
  6.     return 0;
  7. }
复制代码
 编译指令:
  1. g++ -o testlog Logger.cpp Timestamp.cpp LogStream.cpp -I ../include
复制代码
结果输出:
  1. 2025/06/06 17:38:15[pid]:8294 INFO [LogStream.cpp:66]test info
复制代码
附录

 Timestamp.h
  1. #pragma once
  2. #include <iostream>
  3. #include <string>
  4. namespace mymuduo
  5. {
  6.     namespace base
  7.     {
  8.         class Timestamp
  9.         {
  10.         public:
  11.             Timestamp();
  12.             explicit Timestamp(int64_t microSecondsSinceEpoch);
  13.             static Timestamp now();
  14.             std::string toString() const;
  15.         private:
  16.             int64_t microSecondsSinceEpoch_;
  17.         };
  18.     }
  19. }
复制代码
Timestamp.cpp
  1. #include <time.h>
  2. #include "Timestamp.h"
  3. using namespace mymuduo::base;
  4. Timestamp::Timestamp() : microSecondsSinceEpoch_(0)
  5. {
  6. }
  7. Timestamp::Timestamp(int64_t microSecondsSinceEpoch)
  8.     : microSecondsSinceEpoch_(microSecondsSinceEpoch)
  9. {
  10. }
  11. Timestamp Timestamp::now()
  12. {
  13.     return Timestamp(time(NULL));
  14. }
  15. std::string Timestamp::toString() const
  16. {
  17.     char buf[128] = {0};
  18.     tm *tm_time = localtime(&microSecondsSinceEpoch_);
  19.     snprintf(buf, 128, "%4d/%02d/%02d %02d:%02d:%02d",
  20.              tm_time->tm_year + 1900,
  21.              tm_time->tm_mon + 1,
  22.              tm_time->tm_mday,
  23.              tm_time->tm_hour,
  24.              tm_time->tm_min,
  25.              tm_time->tm_sec);
  26.     return buf;
  27. }
  28. //g++ -o test timestamp.cpp -I ../include
  29. // #include <iostream>
  30. // int main() {
  31. //     std::cout << Timestamp::now().toString() << std::endl;
  32. //     return 0;
  33. // }
复制代码
LogStream.h
  1. #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  \
  2.     if(g_logger&&mymuduo::base::g_logger->GetLogLevel() <= mymuduo::base::kTrace) \
  3.         mymuduo::base::LogStream(mymuduo::base::g_logger,__FILE__,__LINE__,mymuduo::base::kTrace,__func__)
  4. #define LOG_DEBUG  \
  5.     if(g_logger&&mymuduo::base::g_logger->GetLogLevel() <= mymuduo::base::kDebug) \
  6.         mymuduo::base::LogStream(mymuduo::base::g_logger,__FILE__,__LINE__,mymuduo::base::kDebug,__func__)
  7. #define LOG_INFO  \
  8.     if(g_logger&&mymuduo::base::g_logger->GetLogLevel() <= mymuduo::base::kInfo) \
  9.         mymuduo::base::LogStream(mymuduo::base::g_logger,__FILE__,__LINE__,mymuduo::base::kInfo)
  10. #define LOG_WARN  \
  11.         mymuduo::base::LogStream(mymuduo::base::g_logger,__FILE__,__LINE__,mymuduo::base::kWarn)
  12. #define LOG_ERROR  mymuduo::base::LogStream(mymuduo::base::g_logger,__FILE__,__LINE__,mymuduo::base::kError)
复制代码
 LogStream.cpp
  1. #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)
  2. :logger_(loger)
  3. {
  4.     const char* file_name = strrchr(file,'/');
  5.     if(file_name)
  6.     {
  7.         file_name = file_name + 1;
  8.     }
  9.     else
  10.     {
  11.         file_name = file;
  12.     }
  13.     stream_ << Timestamp::now().toString() << "[pid]:";
  14.     if(thread_id == 0)
  15.     {
  16.         thread_id = static_cast<pid_t>(::syscall(SYS_gettid));
  17.     }
  18.     stream_ << thread_id;
  19.     stream_ << log_string[l];
  20.     stream_ << "[" << file_name << ":" << line << "]";
  21.     if(func)
  22.     {
  23.         stream_ << "[" << func << "]";
  24.     }
  25. }LogStream::~LogStream()
  26. {
  27.     stream_ << "\n";
  28.     if(logger_)
  29.     {
  30.         logger_->Write(stream_.str());
  31.     }
  32.     else
  33.     {
  34.         std::cout << stream_.str() << std::endl ;
  35.     }
  36. }// //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
  1. #pragma once
  2. #include "NonCopyable.h"
  3. #include <string>
  4. namespace mymuduo
  5. {
  6.     namespace base
  7.     {
  8.         enum LogLevel
  9.         {
  10.             kTrace,
  11.             kDebug,
  12.             kInfo,
  13.             kWarn,
  14.             kError,
  15.             kMaxNumOfLogLevel,
  16.         };
  17.         class Logger: public NonCopyable
  18.         {
  19.         public:
  20.             Logger() = default;
  21.             ~Logger() = default;
  22.             void SetLogLevel(const LogLevel & level);
  23.             LogLevel GetLogLevel() const;
  24.             void Write(const std::string &msg);
  25.         private:
  26.             LogLevel level_ {kDebug};
  27.         };
  28.     }
  29. }
复制代码
Logger.cpp
  1. #include "Logger.h"#include <iostream>using namespace mymuduo::base;void Logger::SetLogLevel(const LogLevel & level)
  2. {
  3.     level_ = level;
  4. }LogLevel Logger::GetLogLevel() const
  5. {
  6.     return level_;
  7. }void Logger::Write(const std::string &msg)
  8. {
  9.     std::cout << msg;
  10. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表