当前位置: 首页 > news >正文

Linux系统C++开发环境搭建工具(一)—— gflags/gtest/spdlog 使用指南

文章目录

  • 一、基础工具安装:
  • 二、gflags框架
    • 简介
    • 示例
  • 三、gtest
    • 简介
    • 示例
  • 四、spdlog
    • 简介
    • 示例
    • spdlog二次封装

一、基础工具安装:

编辑器:sudo apt-get install vim
编译器:sudo apt-get install gcc g++
调试器:sudo apt-get install gdb

项目构建工具:sudo apt-get install make cmake
学习链接:Linux开发工具——make/Makefile

传输管理工具:sudo apt-get install lrzsz

版本控制工具:sudo apt-get install git
学习链接:企业开发工具git的使用:从入门到高效团队协作

二、gflags框架

简介

  gflags 是 Google 开源的一个命令行参数解析与管理的 C++ 库。它的核心功能是让你能够轻松地定义、解析和管理从命令行传递给程序的参数。一个更强大、更系统化的 main(int argc, char** argv) 参数处理方案。
安装:

sudo apt-get install libgflags-dev

特性:

  1. 全局可访问:一旦在某个文件中定义了一个 flag(标志),它就可以在程序的任何地方(包括其他源文件)直接使用,而无需显式地传递参数。这是它名字中 “g” (global) 的由来。
  2. 类型安全:支持多种数据类型,如 bool, int32, int64, uint64, double, std::string 等。
  3. 声明式定义:使用简单的宏(如 DEFINE_bool, DEFINE_string 等)来定义 flag,非常直观。
  4. 自动生成帮助信息:程序会自动生成 --help 信息,列出所有已定义的 flag、它们的类型、默认值和描述。
  5. 灵活性flag 的值不仅可以通过命令行设置,还可以通过环境变量、特定的配置文件来设置。

示例

#include <gflags/gflags.h>
#include <iostream>
//DEFINE_*:定义并注册一个命令行参数
//参数1:设置命令行参数名称
//参数2:默认值
//参数3:参数说明
DEFINE_string(ip,"127.0.0.1","ip地址,格式:127.0.0.1");
DEFINE_int32(port,7272,"端口号,格式:7272");
DEFINE_bool(debug_enable,true,"是否开启调试模式,格式:true/false");int main(int argc,char* argv[])
{//使用ParseCommandLineFlags解析命令行参数并将其赋值给对应的 flag 变量google::ParseCommandLineFlags(&argc,&argv,true);//在gflags内部会把传入的name定义为FLAGS_name格式的全局变量//我们将它输出std::cout<<FLAGS_ip<<std::endl;std::cout<<FLAGS_port<<std::endl;std::cout<<FLAGS_debug_enable<<std::endl;return 0;
}

在这里插入图片描述
查看帮助信息:
在这里插入图片描述
通过配置文件传入参数:
在这里插入图片描述

三、gtest

简介

  gtest 的全称是 Google Test,是由 Google 开发的一个跨平台的 C++ 单元测试框架,gtest 的核心目的是进行 单元测试。单元测试是指对软件中的最小可测试单元(通常是函数、类的方法)进行检查和验证。

安装:

sudo apt-get install libgtest-dev

断言是测试的基石,用于验证条件是否成立。gtest 提供了两类主要的断言宏:

ASSERT_ 系列:当断言失败时,立即终止当前测试函数。
例如:ASSERT_EQ(5, Add(2, 3)); // 如果不等,测试立即停止。

EXPECT_ 系列:当断言失败时,报告错误但继续执行当前测试函数中的后续断言。
例如:EXPECT_TRUE(IsPrime(11)); // 如果不是 true,报告错误但继续。

这种区分让你可以选择是遇到致命错误就停止,还是收集一个测试函数中的所有错误。

示例

#include <gtest/gtest.h>
#include <iostream>
int Add(int num1,int num2)
{return num1+num2;
}
TEST(测试名称1,加法测试用例)
{ASSERT_EQ(Add(1,1),2);//相等ASSERT_LT(Add(2,3),8);//小于
}
TEST(测试名称2,字符串比较测试)
{std::string str = "linux";ASSERT_EQ(str,"linux");EXPECT_EQ(str,"Linux");ASSERT_EQ(str,"hello");EXPECT_EQ(str,"Hello");
}
int main(int argc,char* argv[])
{//单元测试框架初始化testing::InitGoogleTest(&argc,argv);//开始所有单元测试return RUN_ALL_TESTS();
}

效果:
在这里插入图片描述

四、spdlog

简介

  spdlog是一个轻量、高效和易用的特点,被广泛应用于各种 C++ 项目中,在小型工具到大型商业应用都能看到它的身影。

特点:

  • 高性能spdlog 专为速度而设计,即使在高负载情况下也能保持良好的性能。
  • 零配置:无需复杂的配置,只需包含头文件即可在项目中使用。
  • 异步日志:支持异步日志记录,减少对主线程的影响。
  • 格式化:支持自定义日志消息的格式化,包括时间戳、线程 ID、日志级别等。
  • 多平台:跨平台兼容,支持 WindowsLinuxmacOS 等操作系统。
  • 丰富的 API:提供丰富的日志级别和操作符重载,方便记录各种类型的日志。

安装:

sudo apt-get install libspdlog-dev

日志等级枚举
  spdlog 使用枚举 spdlog::level::level_enum 来定义日志级别,这决定了日志信息的重要性。级别从高到低(从最不重要到最重要)排列如下:

#include <spdlog/spdlog.h>
// 等级枚举值
spdlog::level::trace;    // 跟踪信息,最详细的调试信息
spdlog::level::debug;    // 调试信息,用于开发阶段
spdlog::level::info;     // 一般信息,用于报告程序正常运行状态
spdlog::level::warn;     // 警告信息,表示可能有问题,但程序仍能运行
spdlog::level::err;      // 错误信息,表示程序执行中发生了错误
spdlog::level::critical; // 严重错误信息,可能导致程序崩溃
spdlog::level::off;      // 关闭所有日志输出

日志记录器类
  spdlog::loggerspdlog 的核心类,负责接收日志消息并将其分发到一个或多个“落地类”进行处理。

异步日志记录器类
对于高性能要求的场景,spdlog 提供了异步日志记录器。

工作原理:

  • 前端线程(业务线程)在调用日志函数(如 info(), error())时,并不会立即执行耗时的 I/O 操作(如写入文件)。而是将日志消息放入一个内存队列(环形缓冲区)。
  • 后端有一个专用的工作线程,不断从队列中取出消息,并批量地分发给各个 sink 进行实际的输出。

日志记录器工厂类
spdlog::registry 是一个单例类,它充当了日志记录器的工厂和仓库。

主要职责:

  • 创建和存储 logger:当你使用 spdlog::create<>spdlog::stdout_color_mt 等工厂函数时,这些函数内部会通过 registry 来创建 logger 并存储起来。
  • 按名称检索 logger:你可以通过 spdlog::get("logger_name")registry 中获取之前创建的 logger
  • 全局配置:可以通过 registry 设置全局的日志级别、格式模式等,这些设置会自动应用到所有已注册的 logger 上(除非 logger 有自己的特定设置)。

落地类
  sink(落地类)是实际负责将日志消息写入到特定目标的对象。spdlog::logger 本身不处理输出,而是将消息传递给其拥有的所有 sink

全局接口
spdlog 提供了一组非常方便的全局函数,用于快速记录日志,这些函数使用一个默认的全局 logger

常用全局函数:

  • spdlog::set_level(level): 设置默认 logger 的级别。
  • spdlog::flush_every(std::chrono):设置刷新策略,如每秒刷新
  • spdlog::flush_on(spdlog::level::level_enum):遇到debug以上等级的日志立即刷新
  • spdlog::trace(),debug(), info(), warn(), error(), critical(): 使用相应级别记录一条消息。
  • spdlog::get("name"): 通过名称获取一个已注册的 logger
  • spdlog::drop("name"): 从注册表中移除一个 logger
  • spdlog::drop_all(): 移除所有 logger
  • spdlog::shutdown(): 释放所有资源并关闭日志系统。

spdlog支持自定义日志输出格式,常用格式占位符:

  • %L:日志级别的简短表示。
  • %l:日志级别的全称。
  • %v%@:实际的日志消息本身。这是最重要的占位符,代表你调用 spdlog::info("Hello") 中的 "Hello"
  • %t:线程 ID。用于区分不同线程输出的日志,在多线程程序中非常有用。
  • %P:进程 ID。
  • %n:日志器的名称。当你使用多个命名日志器时,用于区分来源。
  • %a:星期的缩写名称。
  • %A:星期的全称。
  • %b:月份的缩写名称。
  • %B:月份的全称。
  • %c:标准的日期时间字符串。
  • %Y:四位数的年份。
  • %m:两位数的月份 (01-12)。
  • %d:两位数的日期 (01-31)。
  • %H:24小时制的小时 (00-23)。
  • %M:分钟 (00-59)。
  • %S:秒 (00-59)。
  • %e:毫秒 (000-999)。
  • %f:微秒 (000000-999999)。
  • %o:来源信息(文件名:行号,函数名)。需要编译时定义宏 SPDLOG_USE_STD_FORMAT 或特定编译器支持。

示例

#include <iostream>
#include <spdlog/spdlog.h>
#include <spdlog/async.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
int main()
{//设置全局刷新策略//每秒刷新spdlog::flush_every(std::chrono::seconds(1));//遇到debug以上等级的日志立即刷新//spdlog::flush_on(spdlog::level::level_enum::debug);//设置全局日志输出等级spdlog::set_level(spdlog::level::level_enum::trace);//创建同步日志auto log1 = spdlog::stdout_color_mt("default-logger");//参数为日志器的名称//创建异步日志init_thread_pool(3000,1)//初始化日志输出线程配置,设置日志队列容量最大为3000条,分配1条线程auto log2 = spdlog::stdout_color_mt<spdlog::async_factory>("sync-logger");//创建同步日志输出到文件auto log3 = spdlog::basic_logger_mt("file-logger","test.log");//设置日志输出格式log1->set_pattern("[%H:%M:%S][%t][%-8l - %v] "); log2->set_pattern("[%H:%M:%S][%t][%-8l - %v] "); log3->set_pattern("[%H:%M:%S][%t][%-8l - %v] "); //日志输出log2->trace("我是异步日志");log2->debug("hellp {}","log");//{}是占位符,不用我们指定输出的什么类型,像auto一样能自动检测log2->info("hello {}",2+3);log1->trace("我是同步日志");log1->debug("hellp {}","log");log1->info("hello {}",2+3);log3->trace("我是同步日志输出到文件");log3->debug("hellp {}","log");log3->info("hello {}",2+3);return 0;
}

编译时需要带选项连接库:-lspdlog-lfmt
结果:
在这里插入图片描述
可以发现在代码逻辑上异步日志的是在前执行的,但是在整个程序执行完后才在后面输出。执行异步输出语句后,只是放在内存中,没有落盘操作,而用额外的线程池来处理。

spdlog二次封装

原因:

  1. 避免单例的锁冲突,因此直接创建全局的线程安全的日志器进行使用
  2. 因为日志输出没有文件名行号,因此使用宏进行二次封装输出日志的文件名和行号
  3. 封装出一个初始化接口,便于使用:调试模式则输出到标准输出,否则输出到文件中

思想:
封装出一个全局接口,用户进行日志器的创建与初始化

  1. 参数1:运行模式/调试模式 – true/false
  2. 参数2:输出文件名 – 用于发布模式
  3. 参数3:输出日志等级 – 用于发布模式

对日志输出的接口,进行宏的封装,加入文件名行号的输出

#include <iostream>
#include <spdlog/spdlog.h>
#include <spdlog/async.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
std::shared_ptr<spdlog::logger> default_logger;
void init_logger(bool mode,std::string file,int32_t level)
{if(mode == false){//调试模式default_logger = spdlog::stdout_color_mt("default-logger");default_logger->set_level(spdlog::level::trace);default_logger->flush_on(spdlog::level::trace);}else{//发布模式default_logger = spdlog::basic_logger_mt("default-logger",file);default_logger->set_level((spdlog::level::level_enum)level);default_logger->flush_on((spdlog::level::level_enum)level);}
}
#define LOG_TRACE(format,...) default_logger->trace(std::string("[{}:{}]")+format,__FILE__,__LINE__,##__VA_ARGS__)
#define LOG_DEBUG(format,...) default_logger->debug(std::string("[{}:{}]")+format,__FILE__,__LINE__,##__VA_ARGS__)
#define LOG_INFO(format,...) default_logger->info(std::string("[{}:{}]")+format,__FILE__,__LINE__,##__VA_ARGS__)
#define LOG_WARN(format,...) default_logger->warn(std::string("[{}:{}]")+format,__FILE__,__LINE__,##__VA_ARGS__)
#define LOG_ERR(format,...) default_logger->error(std::string("[{}:{}]")+format,__FILE__,__LINE__,##__VA_ARGS__)

测试文件:

#include "logger.hpp"
#include <gflags/gflags.h>
DEFINE_bool(mode,false,"程序的运行模式:false--debug模式(默认模式,true--发布模式");
DEFINE_string(file,"","在发布模式下日志的输出文件");
DEFINE_int32(level,0,"在发布模式下的日志输出等级");
int main(int argc,char* argv[])
{google::ParseCommandLineFlags(&argc,&argv,true);init_logger(FLAGS_mode,FLAGS_file,FLAGS_level);LOG_TRACE("hello");LOG_DEBUG("hello {}","linux");LOG_INFO("hello info");LOG_WARN("hello warn");LOG_ERR("hello err");return 0;
}

测试结果:
在这里插入图片描述

非常感谢您能耐心读完这篇文章。倘若您从中有所收获,还望多多支持呀!在这里插入图片描述

http://www.dtcms.com/a/422453.html

相关文章:

  • MySQL逻辑备份工具mysqldump:原理剖析与实操指南
  • Java-Spring入门指南(十一)代理模式与Spring AOP实战
  • 实名认证接口-识破虚假身份:科技为信任筑起第一道防线
  • 柘林网站建设wordpress改背景图片
  • RokcetMQ事务消息详解
  • Athena + S3 数据分析实战(深度版):从数据湖到可视化 BI
  • IP纯净度检测工具
  • 第四部分:VTK常用类详解(第114章 vtkStreamTracer流线追踪类)
  • MATLAB的CFAR(恒虚警率)图像目标检测
  • 2025三掌柜赠书活动第三十五期 AI辅助React Web应用开发实践:基于React 19和GitHub Copilot
  • HRPC在Polaris存储系统中的应用
  • 网站在百度无法验证码怎么办网站开发技术有包括
  • 【AI时代速通QT】第八节:Visual Studio与Qt-从项目迁移到多版本管理
  • Spring线程池:ThreadPoolExecutor与ThreadPoolTaskExecutor终极对比
  • IDEA创建SpringBoot项目使用JDK1.8
  • 深入分析JAR和WAR包的区别 (指南七)
  • 详解 OpenCV 中的仿射变换:原理与实战案例
  • 计算机视觉(opencv)——基于 dlib 和 CNN卷积神经网络 的人脸检测
  • 黑色背景的网站开发工具微信商城收费吗
  • html快速学习
  • 门户网站 模板之家办公室门户网站建设和管理工作
  • Git 基础 - 查看提交历史
  • 《Linux 构建工具核心:make 命令、进度条、Gitee》
  • vlan batch { vlan-id1 [ to vlan-id2 ] } 概念及题目
  • 济宁网站建设服务互联网公司怎么赚钱
  • Linux-简单命令
  • Linux ​​ls​​ 命令进阶:从隐藏文件到递归显示,成为文件浏览大师
  • VPS服务器锁等待超时处理,如何有效解决数据库性能瓶颈
  • 英伟达服务器维修市场崛起:捷智算GPU维修中心的技术突围之路
  • 第四部分:VTK常用类详解(第102章 vtkButtonWidget按钮控件类)