在 C++ 中实现调试日志输出
在 C++ 编程中,调试日志对于定位问题和优化代码至关重要。有效的调试日志不仅能帮助我们快速定位错误,还能提供有关程序运行状态的有价值的信息。本文将介绍几种常用的调试日志输出方法,并教你如何在日志中添加时间戳。
1. 使用 #ifdef _DEBUG
宏
在 C++ 中,常用的方式之一是使用条件编译宏,控制日志输出仅在调试模式下启用。这种方法非常简单,且不会影响发布版的性能,因为在发布版本中,日志宏会被去除。
#include <iostream>#ifdef _DEBUG
#define LOG_ERROR(msg) \
std::cerr << "[ERROR] " << __FILE__ << ":" << __LINE__ << " (" << __FUNCTION__ << ") - " << msg << std::endl;
#define LOG_DEBUG(msg) \
std::cout << "[DEBUG] " << __FILE__ << ":" << __LINE__ << " (" << __FUNCTION__ << ") - " << msg << std::endl;
#else
#define LOG_ERROR(msg)
#define LOG_DEBUG(msg)
#endif
解释:
_DEBUG 宏:这个宏是在调试模式下自动定义的,通过它,我们可以控制日志输出只在调试时启用。
LOG_DEBUG 宏:它会打印当前文件名、行号、函数名以及传入的调试信息。如果是发布版本,这个宏会被忽略。
优点:
- 调试时能提供详细的信息。
- 不会影响发布版的性能,因为宏在发布时会被完全去除。
缺点:
- 宏在复杂的项目中使用可能会导致调试信息过多,尤其是在日志量大的时候,可能会影响性能。
- 宏不能捕获异常或提供高级日志功能(如日志等级、异步处理等)。
2. 加入时间戳:精确到毫秒
为了进一步提升日志的有用性,我们可以在日志中加入时间戳,这对于调试复杂的异步操作、性能瓶颈等问题非常有帮助。C++11 引入了 库,允许我们精确到毫秒地记录时间。
#include <iostream>
#include <chrono>
#include <iomanip>#ifdef _DEBUG
#define LOG_DEBUG(msg) { \auto now = std::chrono::system_clock::now(); \auto duration = now.time_since_epoch(); \auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count(); \std::time_t time_now = std::chrono::system_clock::to_time_t(now); \std::tm time_tm = *std::localtime(&time_now); \std::cout << "[" << std::put_time(&time_tm, "%Y-%m-%d %H:%M:%S") << "." << std::setw(3) << std::setfill('0') << (milliseconds % 1000) << "] " \<< "[DEBUG] " << __FILE__ << ":" << __LINE__ << " (" << __FUNCTION__ << ") - " << msg << std::endl; \
}
#else
#define LOG_DEBUG(msg)
#endif
解释:
-
获取当前时间:
- 使用
std::chrono::system_clock::now()
获取当前的系统时间。 - 使用
std::chrono::duration_cast
将时间精确到毫秒,并计算出自纪元以来的毫秒数。
- 使用
-
格式化时间戳:
- 将时间转换为
std::time_t
类型,再通过std::localtime
转换为std::tm
结构体。 - 使用
std::put_time
将std::tm
格式化为HH:MM:SS
格式。 - 毫秒部分通过
milliseconds % 1000
计算并格式化为三位数字。
- 将时间转换为
-
输出格式:
- 时间戳格式为
[%Y-%m-%d %H:%M:%S]
,例如2025-01-18 17:52:59.489
。 - 日志中会显示文件名、行号、函数名以及调试信息。
- 时间戳格式为
例子:
int main() {LOG_DEBUG("This is a debug message with timestamp!");return 0;
}
输出(假设当前时间是 14:30:45.123):
[2025-01-18 17:52:59.489] [DEBUG] main.cpp:10 (main) - This is a debug message with timestamp!
Windows 和 MFC 中的调试日志方法
除了标准的 C++ 方法外,Windows 和 MFC 也提供了一些内置的调试日志工具,这些工具可以帮助开发者在调试过程中获取更丰富的信息。
MFC 调试宏
在 MFC 中,有几个常用的宏可以帮助我们进行调试日志输出:
TRACE
:用于向输出窗口打印调试信息,类似于 printf,但输出到 Visual Studio 的调试输出窗口。
TRACE("Code:%d\n", nCode);
ASSERT
:用于验证条件,如果条件为假,会弹出断言对话框,显示出错的文件和行号。
ASSERT(n > 0); // 如果 n <= 0,会弹出断言对话框
AfxMessageBox
:弹出一个消息框,显示调试信息,通常用于调试时向用户展示错误或提示信息。
AfxMessageBox(_T("This is a message box"));
Windows API 调试函数
OutputDebugString
:这个函数可以将调试信息输出到调试器的输出窗口。
OutputDebugString(_T("This is a debug string"));
DbgPrint
:在 Windows 驱动开发中,DbgPrint 用于向调试输出发送信息,适用于驱动程序开发。
DbgPrint("This is a debug message\n");
ASSERT 宏
Windows API 也提供了 ASSERT 宏,它和 MFC 中的 ASSERT 类似,用于检查条件并在条件失败时中断程序。
ASSERT(n > 0); // 如果条件不成立,会弹出一个调试对话框
日志类 (Logger Class)
可以创建一个日志类来封装日志的输出。通过这种方式,你可以集中管理日志的格式、日志级别以及输出目的地(控制台、文件等)。
#include <iostream>
#include <fstream>
#include <string>class Logger {
public:enum LogLevel { INFO, WARNING, ERROR, DEBUG };Logger(LogLevel level = INFO) : logLevel(level) {}void log(LogLevel level, const std::string& msg) {if (level >= logLevel) {std::cout << "[" << levelToString(level) << "] " << msg << std::endl;}}private:LogLevel logLevel;std::string levelToString(LogLevel level) {switch (level) {case INFO: return "INFO";case WARNING: return "WARNING";case ERROR: return "ERROR";case DEBUG: return "DEBUG";default: return "UNKNOWN";}}
};#define LOG(level, msg) Logger().log(level, msg)
优点:
- 支持多级别的日志记录(如 INFO, WARNING, ERROR, DEBUG)。
- 更易于扩展,可以将日志输出到文件、数据库等。
- 方便控制日志输出的内容和级别。
缺点:
- 需要创建对象或静态方法,可能会影响性能。
- 配置和管理较复杂。
第三方日志库:spdlog
对于更复杂的日志需求,第三方库如 spdlog 提供了丰富的功能,例如支持多级别日志、异步日志、文件轮转等。以下是一个使用 spdlog 输出带有时间戳的日志的简单例子:
#include <spdlog/spdlog.h>#define LOG_DEBUG(msg) spdlog::debug("[DEBUG] {}:{} ({}) - {}", __FILE__, __LINE__, __FUNCTION__, msg)
#define LOG_ERROR(msg) spdlog::error("[ERROR] {}:{} ({}) - {}", __FILE__, __LINE__, __FUNCTION__, msg)int main() {spdlog::set_level(spdlog::level::debug); // Set global log levelLOG_DEBUG("This is a debug message.");LOG_ERROR("This is an error message.");
}
spdlog
会自动为每条日志加上时间戳,并支持丰富的输出格式和多种输出方式(如文件、终端、日志服务器等)。
日志文件输出
如果需要将日志写入文件,直接重定向输出流是一个简单的方法。可以结合条件编译、日志类或者外部库。
#include <iostream>
#include <fstream>#define LOG_TO_FILE(msg) { \std::ofstream logFile("log.txt", std::ios::app); \logFile << "[INFO] " << __FILE__ << ":" << __LINE__ << " (" << __FUNCTION__ << ") - " << msg << std::endl; \
}int main() {LOG_TO_FILE("This is a log message.");
}
优点:
- 可以持久化日志数据,便于后期查看和分析。
- 控制台和文件输出灵活配置。
缺点:
- 对性能有一定影响,尤其是写入文件时。
- 没有日志级别、过滤和格式化等高级功能。
日志文件轮转
如果日志文件过大,可以实现文件轮转的功能,即超过一定大小后自动切换到新文件。这通常通过日志库(如 spdlog)或者自行实现。
#include <iostream>
#include <fstream>#define LOG_ROTATE_FILE(msg) { \static int count = 0; \std::ofstream logFile("log_" + std::to_string(count) + ".txt", std::ios::app); \logFile << "[INFO] " << msg << std::endl; \if (++count >= 10) count = 0; \
}int main() {for (int i = 0; i < 15; ++i) {LOG_ROTATE_FILE("Log message number " + std::to_string(i));}
}
优点:
- 自动管理日志文件的大小,避免日志文件过大。
- 文件轮转能有效管理日志。
缺点:
- 需要额外的逻辑来处理日志切换和命名。
总结
在 C++ 开发中,调试日志是调试和优化代码的重要工具。通过使用条件编译宏、std::chrono 来精确记录时间戳,我们可以在调试日志中添加有用的上下文信息,帮助我们快速定位问题。在 Windows 和 MFC 环境下,内置的调试工具如 TRACE、ASSERT 以及 OutputDebugString 也能为我们提供方便的调试信息。此外,第三方日志库如 spdlog 提供了更多的功能,适用于需要高效、异步日志记录的复杂项目。
相关文章:
在 C++ 中实现调试日志输出
在 C 编程中,调试日志对于定位问题和优化代码至关重要。有效的调试日志不仅能帮助我们快速定位错误,还能提供有关程序运行状态的有价值的信息。本文将介绍几种常用的调试日志输出方法,并教你如何在日志中添加时间戳。 1. 使用 #ifdef _DEBUG…...

从零搭建一套远程手机的桌面操控和文件传输的小工具
从零搭建一套远程手机的桌面操控和文件传输的小工具 --ADB连接专题 一、前言 前面的篇章中,我们确定了通过基于TCP连接的ADB控制远程手机的操作思路。本篇中我们将进行实际的ADB桥接的具体链路搭建工作,从原理和实际部署和操作层面上,从零…...
Python中的静态方法
目录 什么是静态方法?静态方法的特点 定义和调用静态方法示例:定义一个简单的静态方法 静态方法 vs 类方法 vs 实例方法示例对比 静态方法的应用场景1. 🔧 工具函数2. 🏭 工厂方法3. ✅ 数据验证 静态方法的限制总结 静态方法是 P…...

【C++】面试题整理(未完待续)
【C】面试题整理 文章目录 一、概述二、C基础2.1 - 指针在 32 位和 64 位系统中的长度2.2 - 数组和指针2.3 - 结构体对齐补齐2.4 - 头文件包含2.5 - 堆和栈的区别2.6 - 宏函数比较两个数值的大小2.7 - 冒泡排序2.8 - 菱形继承的内存布局2.9 - 继承重写2.10 - 如何禁止类在栈上分…...
每日一题 403. 青蛙过河
403. 青蛙过河 动态规划,状态转移 和 上一步步长 和 当前位置点 有关系 class Solution { public:bool canCross(vector<int>& stones) {int n stones.size();unordered_map<int,unordered_set<int>> dp;unordered_map<int,int> mp;…...
Spring Boot 集成 MongoDB:启动即注入的便捷实践
引言 在现代后端开发中,Spring Boot 凭借其快速开发、自动配置等特性深受开发者喜爱,而 MongoDB 以其灵活的文档存储结构和出色的扩展性,成为处理非结构化数据的首选数据库之一。将两者结合,利用 Spring Boot 的自动配置功能&…...

【电视盒子】HI3798MV300刷机教程笔记/备份遥控码修复遥控器/ADB/线刷卡刷/电视盒子安装第三方应用软件
心血来潮,看到电视机顶盒满天飞的广告,想改造一下家里的电视盒子,学一下网上的人刷机,但是一切都不知道怎么开始,虽然折腾了一天,以失败告终,还是做点刷机笔记。 0.我的机器 年少不会甄别&…...
R语言的文件操作
R语言的文件操作 引言 在数据科学和分析的过程中,文件操作是不可或缺的一部分。R语言作为一种强大的统计计算和图形作图的编程语言,提供了丰富的文件操作函数,使得用户能够方便地读取和保存数据。本文将详细介绍R语言中的文件操作ÿ…...
锐捷路由器网关RG-NBR6135-E和锐捷交换机 Ruijie Reyee RG-ES224GC 电脑登录web方法
2025年1月17日22:29:35 最近淘了点东西,准备在家里搞一套深度学习的服务器,先把网关和交换机搞到了 锐捷路由器网关RG-NBR6135-E 电脑登录web方法 在拿到机器的时候,如果不是全新建议拿根牙签,差入reset 5-10秒,灯光会全部闪几下…...

论文速读|NoteLLM: A Retrievable Large Language Model for Note Recommendation.WWW24
论文地址:https://arxiv.org/abs/2403.01744 bib引用: misc{zhang2024notellmretrievablelargelanguage,title{NoteLLM: A Retrievable Large Language Model for Note Recommendation}, author{Chao Zhang and Shiwei Wu and Haoxin Zhang and Tong Xu…...

在线图片转为excel工具
在线图片转为excel工具,无需登录,无需成本,用完就走。 包括中文和英文版本。 官网地址: https://img2excel.openai2025.com 效果:...
深度学习篇---数据集分类
文章目录 前言第一部分:VOC数据集标签、COCO数据集格式1.VOC数据集标签的特点及优缺点特点优点缺点 2.COCO数据集标签的特点及优缺点特点优点缺点 3.YOLO数据集标签的特点及优缺点特点优点缺点 第二部分:VOC格式和YOLO格式1.VOC格式3.YOLO格式3.区别(1)文…...

Unity3D仿星露谷物语开发23之拿起道具的动画
1、目标 当点击库存栏上可以carry的道具时,首先arms替换为carry状态,同时手上拿着被点击的道具。当再次点击同一个道具时,ams替换为idle状态,手上放下之前的道具。 这个最主要的是要学会使用AnimatorOverrideController类。 2、…...

素描风格渲染
素描风格渲染(Hatching Style Rendering),是一种非真实感渲染(NPR),主要目的是使3D模型看起来像 手绘素描的视觉效果。这种风格的渲染常用于游戏、动画和电影中,用来创造一种独特的艺术风格 1、…...

STM32使用DSP库 Keil方式添加
文章目录 前言一、添加DSP库二、使能FPU及配置1. 使能FPU2. 增加编译的宏3.增加头文件的检索路径三. 验证1. 源码中添加2.代码测试前言 添加DSP有两种方案,本文采用的是是Keil 中添加。 一、添加DSP库 在创建好的工程中添加DSP库:步骤如下: 步骤1:选择运行环境管理; 步…...

【机器学习实战入门项目】MNIST数字分类机器学习项目
Python 深度学习项目:手写数字识别 为了使机器更加智能,开发者们正在深入研究机器学习和深度学习技术。人类通过不断练习和重复来学习执行某项任务,从而记住如何完成这些任务。然后,大脑中的神经元会自动触发,他们能够…...

利用 LNMP 实现 WordPress 站点搭建
部署MySQL数据库 在主机192.168.138.139主机部署数据库服务 包安装数据库 apt-get install mysql-server 创建wordpress数据库和用户并授权 mysql> create database wordpress;#MySQL8.0要求指定插件 mysql> create user wordpress192.168.138.% identified with mys…...

模块化架构与微服务架构,哪种更适合桌面软件开发?
前言 在现代软件开发中,架构设计扮演着至关重要的角色。两种常见的架构设计方法是模块化架构与微服务架构。它们各自有独特的优势和适用场景,尤其在C#桌面软件开发领域,模块化架构往往更加具有实践性。本文将对这两种架构进行对比࿰…...
2025.1.17——1200
2025.1.17——1200 Q1. 1200 Jellyfish has n n n green apples with values a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,…,an and Gellyfish has m m m green apples with values b 1 , b 2 , … , b m b_1,b_2,\ldots,b_m b1,b2,…,bm. They will …...
vite工程化
Vite 通过直接利用浏览器的模块加载能力、将 CommonJS 模块转换为 ES 模块并缓存结果、基于原生 ES 模块的 HMR 以及对 TypeScript 的直接支持,提供了更快的开发体验和更高的开发效率。 1.直接利用浏览器模块加载功能 更快加载速度:不需要打包…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...

Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...

Vue ③-生命周期 || 脚手架
生命周期 思考:什么时候可以发送初始化渲染请求?(越早越好) 什么时候可以开始操作dom?(至少dom得渲染出来) Vue生命周期: 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...

阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)
cd /home 进入home盘 安装虚拟环境: 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境: virtualenv myenv 3、激活虚拟环境(激活环境可以在当前环境下安装包) source myenv/bin/activate 此时,终端…...

react菜单,动态绑定点击事件,菜单分离出去单独的js文件,Ant框架
1、菜单文件treeTop.js // 顶部菜单 import { AppstoreOutlined, SettingOutlined } from ant-design/icons; // 定义菜单项数据 const treeTop [{label: Docker管理,key: 1,icon: <AppstoreOutlined />,url:"/docker/index"},{label: 权限管理,key: 2,icon:…...
Easy Excel
Easy Excel 一、依赖引入二、基本使用1. 定义实体类(导入/导出共用)2. 写 Excel3. 读 Excel 三、常用注解说明(完整列表)四、进阶:自定义转换器(Converter) 其它自定义转换器没生效 Easy Excel在…...

性能优化中,多面体模型基本原理
1)多面体编译技术是一种基于多面体模型的程序分析和优化技术,它将程序 中的语句实例、访问关系、依赖关系和调度等信息映射到多维空间中的几何对 象,通过对这些几何对象进行几何操作和线性代数计算来进行程序的分析和优 化。 其中࿰…...
SpringCloud优势
目录 完善的微服务支持 高可用性和容错性 灵活的配置管理 强大的服务网关 分布式追踪能力 丰富的社区生态 易于与其他技术栈集成 完善的微服务支持 Spring Cloud 提供了一整套工具和组件来支持微服务架构的开发,包括服务注册与发现、负载均衡、断路器、配置管理等功能…...

WinUI3开发_使用mica效果
简介 Mica(云母)是Windows10/11上的一种现代化效果,是Windows10/11上所使用的Fluent Design(设计语言)里的一个效果,Windows10/11上所使用的Fluent Design皆旨在于打造一个人类、通用和真正感觉与 Windows 一样的设计。 WinUI3就是Windows10/11上的一个…...