gflags+spdlog实战:C++命令行参数与高性能日志的极致搭配行动指南

gflags+spdlog实战:C++命令行参数与高性能日志的极致搭配行动指南

文章目录

在这里插入图片描述

本篇摘要

本文介绍gflags命令行参数解析库(轻量高效、类型安全)与spdlog高性能日志库(同步/异步、多平台),涵盖安装、基础使用及二次封装等帮助C++项目灵活配置与高效日志管理。

一.gflags 介绍及简单使用

简单介绍

Google 开源的命令行参数解析库,用于高效管理程序启动参数(如 --flag=value)。

核心功能:

  • 定义参数:通过宏(如 DEFINE_int32DEFINE_string)声明参数名、默认值和帮助信息。
  • 解析参数:调用 ParseCommandLineFlags 解析 argc/argv,将命令行参数映射到全局变量(如 FLAGS_<name>)。
  • 快速访问:通过全局变量 FLAGS_<flag_name> 直接获取解析后的值。

特点:

  • 轻量高效:专为命令行参数设计,解析速度快,适合 C++ 项目。
  • 类型安全:支持多种数据类型(整型、字符串等),编译期检查。
  • 自动生成帮助:通过 --help 自动输出参数说明(依赖定义时的描述信息)。

对比其他工具:

类似功能库:Boost.Program_options(C++)、Python 的 argparse
gflags 优势:与 Google 生态兼容,API 简洁,适合高性能场景。

安装过程

用源码装gflags库步骤:

  1. 下载源码:git clone https://github.com/gflags/gflags.git
  2. 进入目录:cd gflags/
  3. 新建并进入build文件夹:mkdir build; cd build/
  4. 生成Makefile:cmake ..
  5. 编译:make
  6. 安装:make install
在这里插入图片描述
  • 可以看到对应安装在include目录里,以及动态库在/usr/lib/x86_64-linux-gnu目录下。

gflags简单使用

gflags支持的常见宏类型:

DEFINE_bool DEFINE_int32 DEFINE_int64 DEFINE_uint64 DEFINE_double DEFINE_string 

简单使用格式:

#include<gflags/gflags.h>//使用的时候需要动态链接gflags库#include<iostream>DEFINE_string(ip,"127.0.0.1","服务端ip");DEFINE_int32(port,8080,"服务端监听端口");DEFINE_bool(debug_enable,true,"是否启用调试模式, 格式:true/false");intmain(int argc,char*argv[]){ google::ParseCommandLineFlags(&argc,&argv,true);// ​解析完 flag 后,将 flag 从 argv 中移除,​​相应减少 argc 的值 std::cout << FLAGS_ip << std::endl; std::cout << FLAGS_port << std::endl; std::cout << FLAGS_debug_enable << std::endl;return0;}

google::ParseCommandLineFlags 介绍:

google::ParseCommandLineFlags 是 Google 工具库里的函数,用来处理程序启动时输入的命令行参数。

它接收 main 函数里的 argc(参数个数)和 argv(参数数组),还有个 remove_flags 参数控制后续行为:

  • remove_flagstrue,解析后会从 argv 里删除已识别的“标志和参数”,同时 argc 也会变小;
  • 若为 falseargc 不变,但会把所有“标志”移到 argv 最前面重新排好。

一句话:帮你自动解析命令行里的各种选项/开关,让程序能读懂用户输的参数。

过程大致概括:

  • 输入在对应gflags定义在宏里参数(通过命令行或者配置文件指定或者默认输进去的),此时gflags会内部把它解析出来,对用户提供对应的GFLAGS_变量名访问接口,方便用户获取。
  • 也就是这样做可以不用每次都手动修改代码对应参数变量等,而是通过程序参数方式进行输入,避免了重复编译带来的消耗。

使用方式

1·直接使用默认的参数
在这里插入图片描述
  • 这里就直接拿宏函数中默认的值进行输入到main的参数中解析(这里会把- -将会终止标识的处理)。
2·使用命令行参数
在这里插入图片描述
  • 在命令行启动程序的时候按照对应格式输入。
3·使用配置文件输入
在这里插入图片描述


在这里插入图片描述
  • 这里直接让它从配置文件中读取对应参数即可。

使用参考

gflags提供了一些特殊参数标识:

  • –help:显示文件中所有标识的帮助信息。
  • –helpfull:与–help功能类似,但帮助信息更全面。
  • –helpshort:仅显示当前执行文件里的标志。
  • –helpxml:以xml格式打印,便于处理。
  • –version:打印版本信息,由google::SetVersionString()设定。
  • –flagfile:从指定的文件f中读取命令行参数。

比如这里可以使用对应- -help选项查看对应的gflags设置的对应输入参数如何使用:

二.Spdlog组件介绍及简单使用

简单介绍

spdlog 是一个高性能、超快速、零配置的 C++ 日志库,旨在提供简洁 API 和丰富功能,同时保持高性能的日志记录,支持多种输出目标、格式化选项、线程安全及异步日志记录。(也就是类似之前实现的日志系统项目,同时对比glog是功能更加多的,比如前者支持异步模式但后者不支持等等)

特点介绍:

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

安装过程

对应命令进行安装即可(保证apt或者yum源是最新的):

sudoapt-getinstall libspdlogdev 

可以看到对应安装好后的头文件集合:

在这里插入图片描述
  • 以及动态库在/usr/lib/x86_64-linux-gnu目录下。

spdlog 简单使用

下面认识下常见的基础的使用方法:

包含的头文件:

#include<spdlog/spdlog.h>//必须包含的#include<spdlog/sinks/basic_file_sink.h>//文件输出模式#include<spdlog/sinks/stdout_color_sinks.h>//控制台输出模式

下面看个同步模式的例子:

#include<spdlog/spdlog.h>#include<spdlog/sinks/basic_file_sink.h>#include<spdlog/sinks/stdout_color_sinks.h>#include<iostream>#include<chrono>intmain(){usingnamespace std::literals;// 设置刷新时间间隔 spdlog::flush_every(1s);// 刷新策略等级 spdlog::flush_on(spdlog::level::level_enum ::trace);// 设置日志输出等级(全局,但后面可以根据日志器输出的时候进行修改) spdlog::set_level(spdlog::level::level_enum ::debug);// 启动对应日志器:auto logger1 = spdlog::stdout_color_mt("test_log1");// 多线程,模版默认同步打印auto logger2 = spdlog::basic_logger_mt("test_log2","sync.log",true);// 多线程,模版默认同步打印,覆盖式追加文件(truncate)// 自定义输出格式:  logger1->set_pattern("[%n][%H:%M:%S][%t][%-8l] %v"); logger2->set_pattern("[%n][%H:%M:%S][%t][%-8l] %v");//{}用来占位// stdout: logger1->trace("halo {}",1111); logger1->debug("halo {}",1111); logger1->info("halo {}",1111); logger1->warn("halo {}",1111); logger1->error("halo {}",1111); logger1->critical("halo {}",1111);// 文件 logger2->trace("halo {}",1111); logger2->debug("halo {}",1111); logger2->info("halo {}",1111); logger2->warn("halo {}",1111); logger2->error("halo {}",1111); logger2->critical("halo {}",1111); std::cout <<"完成日志输出"<< std::endl;return0;}
  • 上面包含了对应同步日志器的控制条输出与文件输出日志器,还有对应的初始化设置。

对应的异步模式仅仅是修改对应的创建日志器的模版函数的模版即可(默认模版是同步日志器):

如下:

#include<spdlog/spdlog.h>#include<spdlog/sinks/basic_file_sink.h>#include<spdlog/sinks/stdout_color_sinks.h>#include<spdlog/async.h>#include<iostream>#include<chrono>intmain(){usingnamespace std::literals;//设置刷新时间间隔 spdlog::flush_every(1s);// 刷新策略等级 spdlog::flush_on(spdlog::level::level_enum ::trace);//设置日志输出等级(全局,但后面可以根据日志器输出的时候进行修改) spdlog::set_level(spdlog::level::level_enum ::debug);//启动对应日志器:auto logger1= spdlog::stdout_color_mt<spdlog::async_factory>("test_log1");//多线程auto logger2= spdlog::basic_logger_mt<spdlog::async_factory>("test_log2","async.log",true);//多线程//自定义输出格式: logger1->set_pattern("[%n][%H:%M:%S][%t][%-8l] %v"); logger2->set_pattern("[%n][%H:%M:%S][%t][%-8l] %v");//{}用来占位// stdout: logger1->trace("halo {}",1111); logger1->debug("halo {}",1111); logger1->info("halo {}",1111); logger1->warn("halo {}",1111); logger1->error("halo {}",1111); logger1->critical("halo {}",1111);//文件 logger2->trace("halo {}",1111); logger2->debug("halo {}",1111); logger2->info("halo {}",1111); logger2->warn("halo {}",1111); logger2->error("halo {}",1111); logger2->critical("halo {}",1111); std::cout<<"完成日志输出"<<std::endl;return0;}

下面来对比下对应打印出来的效果差异:

1·同步模式:

在这里插入图片描述
  • 可以看出这里只有一个线程完成的,打印日志的时候需要阻塞。

2·异步模式:

在这里插入图片描述
  • 明显这里就是多线程了,日志工作线程打印对应日志信息明显要慢一些。

注:

  • 这里使用的时候除了手动链接对应的spdlog库还需要链接对应的格式库即fmt。

基于spdlog使用的二次封装(默认同步日志器)

  • 为什么还需要进行封装,不直接拿来用?

原因:

  • 避免单例锁冲突,创建全局线程安全日志器。
  • 日志输出无文件名行号,用宏二次封装输出相关信息。
  • 封装初始化接口,调试模式输出到标准输出,否则输出到文件。

封装思想:

封装全局接口供用户创建与初始化日志器,用户只需要对对应函数根据输入参数的不同模式完成调用即可,包含初始化接口接收运行模式、输出文件名、输出日志等级等参数,用宏封装日志输出接口,加入文件名行号输出

代码实现:

#pragmaonce #include<spdlog/spdlog.h>#include<spdlog/sinks/basic_file_sink.h>#include<spdlog/sinks/stdout_color_sinks.h>#include<spdlog/async.h>#include<iostream>#include<chrono>#include<string>//使用的时候一定要在开头调用init_logger这个函数完成日志器初始化,否则段错误!!!!!!!!!!! std::shared_ptr<spdlog::logger> logger;// 暂时默认都是同步日志器// mode - 运行模式: true-发布模式(等级自定义); false调试模式(默认全都是最低等级如刷新策略等级,日志等级)voidinit_logger(bool mode,const std::string &filename,int32_t level){if(mode ==1){ logger = spdlog::basic_logger_mt("default_logger",filename.c_str());//下面也可以全局设置,但是每个日志器对象都可以单独给自己设置模式(全局设置的话默认每种日志器的模式都是一样的,如果单独不修改的话) logger->flush_on((spdlog::level::level_enum) level);//都是32位整型,直接强转 logger->set_level((spdlog::level::level_enum) level);//设置日志输出等级(全局,但后面可以根据日志器输出的时候进行修改)}else{//下面也可以全局设置,但是每个日志器对象都可以单独给自己设置模式(全局设置的话默认每种日志器的模式都是一样的,如果单独不修改的话) logger = spdlog::stdout_color_mt("default_logger"); logger->flush_on(spdlog::level::level_enum ::trace); logger->set_level(spdlog::level::level_enum ::trace);//设置日志输出等级(全局,但后面可以根据日志器输出的时候进行修改)} logger->set_pattern("[%n][%H:%M:%S][%t][%-8l] %v");}//使用{}进行占位#defineLOG_TRACE(fmt,...) logger->trace(std::string("[{}:{}] -->")+fmt,__FILE__,__LINE__,##__VA_ARGS__)#defineLOG_DEBUG(fmt,...) logger->debug(std::string("[{}:{}] -->")+ fmt,__FILE__,__LINE__,##__VA_ARGS__)#defineLOG_INFO(fmt ,...) logger->info(std::string("[{}:{}] -->")+ fmt,__FILE__,__LINE__,##__VA_ARGS__)#defineLOG_WARN(fmt ,...) logger->warn(std::string("[{}:{}] -->")+ fmt,__FILE__,__LINE__,##__VA_ARGS__)#defineLOG_ERROR(fmt,...) logger->error(std::string("[{}:{}] -->")+ fmt,__FILE__,__LINE__,##__VA_ARGS__)#defineLOG_FATAL(fmt,...) logger->critical(std::string("[{}:{}] -->")+ fmt,__FILE__,__LINE__,##__VA_ARGS__)
  • 这里不难发现和之前对应实现的日志系统项目模式是相似的。
在这里插入图片描述


在这里插入图片描述
  • 这里可以看到同步的控制台输出和文件输出都是没问题的。

此时可以配合对应的gflags一起使用,让用户能灵活在运行程序时候通过参数进行选择:

#include"log.hpp"#include<gflags/gflags.h>DEFINE_bool(run_mode,false,"程序的运行模式,false-调试; true-发布;");DEFINE_string(log_file,"","发布模式下,用于指定日志的输出文件");DEFINE_int32(log_level,0,"发布模式下,用于指定日志输出等级");intmain(int argc,char*argv[]){ google::ParseCommandLineFlags(&argc,&argv,1);init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level);LOG_DEBUG("你好:{}","1");LOG_INFO("你好:{}","2");LOG_WARN("你好:{}","3");LOG_ERROR("你好:{}","4");LOG_FATAL("你好:{}","5");LOG_DEBUG("这是一个测试");}

测试:

在这里插入图片描述


在这里插入图片描述
  • 对应两种模式输出都是没问题的。

基于spdlog总结

  1. 功能强大:提供丰富日志功能,满足多样需求。
  2. 使用简单:API 简单,开发者易上手,快速实现日志记录。
  3. 性能高效:具备高性能日志记录能力。
  4. 代码友好:保持代码清晰,利于维护。
  5. 环境适用:开发和生产环境都能稳定高效服务。

总结文本查询小技巧

grep -R '文本内容' 路径/文件 
  • ​​grep​​: 是一个强大的文本搜索工具,用于在文件中查找匹配指定模式的行。
  • -R​​: 是 grep命令的一个选项,代表“递归(recursive)”。使用该选项时,grep会递归地搜索指定目录及其所有子目录中的文件内容。
  • 要查的文本内容: 是你要搜索的模式,意味着 grep会在文件/目录中查找包含 这个文本内容的行。
  • 路径/文件: 要查找的路径或者文件范围。

如下:

  • 这里会按照指定的当前目录进行查询,然后递归式的把当前目录的文件指定包含'sync'这个文本的行打印出来(包括文件名+对应行,然后把对应文本标记出来)。

三.gtest介绍使用

可见这篇博文第三段:

传送门

四.本篇小结

本文带你了解了gflags(解析命令行参数,支持多类型、自动生成帮助)与spdlog(高性能日志,支持同步/异步、多输出目标),通过安装、基础用法及封装示例,展示两者在C++项目中的价值。

Read more

融资3000万美元,服务2000+团队!听Dify专家拆解如何把AI从Demo变生产力

融资3000万美元,服务2000+团队!听Dify专家拆解如何把AI从Demo变生产力

整理 | 梦依丹      出品 | ZEEKLOG(ID:ZEEKLOGnews) 近日,开源 AI 应用开发平台 Dify 宣布完成 3000 万美元 Pre-A 轮融资,由红杉领投,GL Ventures、Alt-Alpha Capital、五源资本、瑞穗力合投资和 NYX Ventures 跟投,目前公司估值已达 1.8 亿美元。 这家从“原型工具”杀出来的黑马,如今已服务全球超 140 万台设备、2000+ 团队和 280 家企业(包括马士基、安克创新等),正朝着“全球 AI 应用工作流标准定义者”狂奔。 那么,Dify 是如何用“

By Ne0inhk

CentOS7 安装配置 MySQL5.7 完整教程(本地虚拟机学习版)

⚠️ 【全文适用重要声明】本文所有操作仅适用于 VMware/VirtualBox 本地私有虚拟机学习测试,严禁在云服务器、生产服务器、对外网开放的服务器上直接照搬执行: 1. 生产环境禁止直接关闭防火墙,应仅开放指定端口(3306 等) 2. 禁止使用弱密码、禁止关闭数据库密码安全策略 3. 禁止无限制开放 root 远程权限,应限制来源 IP 4. 所有软件请通过官方源 / 官方网站获取,支持正版版权 一、环境准备 1.1 关闭防火墙 为了避免安装过程中出现网络问题,我们先关闭防火墙并禁止其开机自启: ⚠️ 仅本地虚拟机学习环境使用,生产环境禁止直接关闭防火墙,仅需放行 3306 端口 # 停止firewall systemctl stop firewalld.service # 禁止开机自启 systemctl disable firewalld.service # 查看防火墙状态 systemctl

By Ne0inhk
Java安全开发实战:从代码防护到架构安全

Java安全开发实战:从代码防护到架构安全

第二十二章 Java安全开发实战:从代码防护到架构安全 一、章节学习目标与重点 1.1 学习目标 * 理解Java应用面临的核心安全威胁(注入攻击、跨站脚本、权限漏洞等),掌握安全开发的核心原则与防护体系。 * 熟练运用代码级安全防护技巧,解决SQL注入、XSS、CSRF、文件上传漏洞等常见安全问题。 * 掌握认证授权机制的安全设计(密码加密、JWT安全、OAuth2.0实战),避免权限越界与身份伪造。 * 实现微服务架构下的安全防护(API网关安全、服务间通信加密、配置中心安全),构建端到端安全体系。 * 能够独立完成Java应用的安全审计与漏洞排查,结合实际场景制定安全加固方案并落地。 1.2 学习重点 * Java应用常见安全漏洞(SQL注入、XSS、CSRF等)的原理与代码级防护。 * 认证授权安全:密码加密存储、JWT令牌安全、RBAC权限模型实战。 * 微服务安全:网关安全防护、服务间HTTPS通信、配置与敏感数据加密。 * 安全审计与漏洞排查工具(SonarQube、OWASP

By Ne0inhk
MySQL SQL注入防御全攻略:原理、攻击与防护实践

MySQL SQL注入防御全攻略:原理、攻击与防护实践

MySQL SQL注入防御全攻略:原理、攻击与防护实践 * 一、SQL注入基础概念 * 1.1 什么是SQL注入? * 1.2 注入攻击的危害等级 * 二、SQL注入攻击原理剖析 * 2.1 典型注入场景分析 * 2.1.1 登录绕过攻击 * 2.1.2 数据泄露攻击 * 2.2 注入类型分类 * 三、防御技术深度解析 * 3.1 参数化查询(Prepared Statements) * 3.1.1 PHP实现示例 * 3.1.2 Java实现示例 * 3.2 输入验证与过滤 * 3.2.1 白名单验证

By Ne0inhk