编辑器:
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 是 Google 开源的一个命令行参数解析与管理的 C++ 库。它的核心功能是让你能够轻松地定义、解析和管理从命令行传递给程序的参数。一个更强大、更系统化的 main(int argc, char** argv) 参数处理方案。 安装:
sudo apt-get install libgflags-dev特性:
flag(标志),它就可以在程序的任何地方(包括其他源文件)直接使用,而无需显式地传递参数。这是它名字中 “g” (global) 的由来。bool, int32, int64, uint64, double, std::string 等。DEFINE_bool, DEFINE_string 等)来定义 flag,非常直观。--help 信息,列出所有已定义的 flag、它们的类型、默认值和描述。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 的全称是 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是一个轻量、高效和易用的特点,被广泛应用于各种 C++ 项目中,在小型工具到大型商业应用都能看到它的身影。
特点:
spdlog 专为速度而设计,即使在高负载情况下也能保持良好的性能。Windows、Linux、macOS 等操作系统。安装:
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::logger 是 spdlog 的核心类,负责接收日志消息并将其分发到一个或多个“落地类”进行处理。
异步日志记录器类
对于高性能要求的场景,spdlog 提供了异步日志记录器。
工作原理:
info(), error())时,并不会立即执行耗时的 I/O 操作(如写入文件)。而是将日志消息放入一个内存队列(环形缓冲区)。sink 进行实际的输出。日志记录器工厂类 spdlog::registry 是一个单例类,它充当了日志记录器的工厂和仓库。
主要职责:
spdlog::create<> 或 spdlog::stdout_color_mt 等工厂函数时,这些函数内部会通过 registry 来创建 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
结果:

可以发现在代码逻辑上异步日志的是在前执行的,但是在整个程序执行完后才在后面输出。执行异步输出语句后,只是放在内存中,没有落盘操作,而用额外的线程池来处理。
原因:
思想: 封装出一个全局接口,用户进行日志器的创建与初始化
对日志输出的接口,进行宏的封装,加入文件名行号的输出
#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;
}测试结果:

非常感谢您能耐心读完这篇文章。倘若您从中有所收获,还望多多支持呀!