一个简单而又不影响运行的日志函数

简单易用的Qt日志模块 引言项目中需求一日志模块,主要实现两大功能:1.自动打印信息至日志文件;2.软件意外退出时保留信息以便跟踪问题。 本文结合了 Qt 自定义日志工具 和 让程序在崩溃时体面的退出之CallStack 提供的方法,补充实现了文章中未具体给出的管理日志文件大小和数量的功能。环境:vs2012+Qt5.2(注:Qt5.5之后引入qInfo(),影响不大)一、日志实现方法基本原理是使用 qInstallMe 阅读详情

I/O操作比较耗时,在硬盘频繁读写时尤其耗时,比如杀毒软件扫描,磁盘碎片整理,索引服务之类的。如果在运行效率有要求的环境下有大量I/O的话就不得不考虑如何让I/O不影响运行。

I/O操作比如说日志,可以输出到DbgView,也可以输出到文件。当有bug时,可以告诉我们问题出在哪儿。输出到DbgView与输出到文件各有利弊。输出到DbgView,好处是不影响程序运行,坏处是需要开DbgView(有时客户不方便开),要考虑DbgView内存开销(这样的话,无法看到时间靠前的日志),不能动DbgView(否则会堵塞程序,拖动进度条,保存文件,清除log之类的操作)。输出到文件,好处是要求少,坏处是有大量I/O操作,必须合理的设计保证不影响程序运行。

最先想到也是最容易想到的解决方案是,将Log丢到缓冲,创建一个子线程专门将这些Log输出到文件。我问自己能不能在现有的文件日志基础上做最少的改动以实现目的。

现有的日志函数

static unsigned short  EnableLog = 1;	//允许写日志
static unsigned short  EnableDbgView = 0;	//允许写到debug view
TCHAR g_szFilePath[MAX_PATH];
int MyIO(LPCSTR fmt, ...)
{
	va_list ap;
	char MsgBuf[512] = {0};	

	static char	szDate[50] = {0};
	static char	szTime[50] = {0};

	clock_t tStart = clock();
	va_start( ap,fmt );
	vsprintf( MsgBuf,fmt,ap );
	va_end( ap );
	if(MsgBuf[strlen(MsgBuf)-1] == '\n')  
		MsgBuf[strlen(MsgBuf)-1] = '\0';

	if(EnableLog)
	{
		SYSTEMTIME st;  
		char szLog[1024] = {0}
		GetLocalTime(&st);              // 取当前系统时间
		sprintf_s(szTime, sizeof(szTime), "%02d:%02d:%02d.%03d", \
			st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
		
		sprintf_s(szDate, sizeof(szDate), "%04d%02d%02d", st.wYear, st.wMonth, st.wDay);
		
		sprintf_s();
		int nBufLen = strlen(szBufferPerSec);
		sprintf(szLog, "%s -> %s\r\n", szTime, MsgBuf);	

		TCHAR szFileName[MAX_PATH];
		sprintf_s(szFileName, sizeof(szFileName), "%sMyIODemo_%s.txt", \
			g_szFilePath, szDate);
		HANDLE hLogFile = CreateFile(szFileName, 
				GENERIC_WRITE,
				FILE_SHARE_READ,//|FILE_SHARE_DELETE,
				NULL,
				OPEN_ALWAYS,//if not exist,creat new //CREATE_ALWAYS,
				FILE_ATTRIBUTE_NORMAL,
				0);

		if(hLogFile  == INVALID_HANDLE_VALUE)
		{
			TRACE("Error!!!can't open %s to write", szFileName);
			return -1;
		}
		SetFilePointer(hLogFile , NULL, NULL, FILE_END);		

		BOOL bRet = WriteFile(hLogFile , szLog, strlen(szLog), 
			NULL, NULL);

		if(!bRet)
		{
			TRACE( "Error!Could not write to file (error %d)\n", GetLastError());
		}

		if(clock()-tStart>2)
		{				
			OutputDebugString("Error!log time-consumingly to file");
		}
		if(hLogFile )
		{
			CloseHandle(hLogFile );
			hLogFile  = NULL;
		}
	}

	if(EnableDbgView)
		OutputDebugString(MsgBuf);

	return 0;
}

我想到了采用异步I/O的方式,或许能解决这个问题。按照Windows核心编程的说法

Consider a thread that issues an asynchronous I/O request to a device. This I/O request is passed to a device driver,which assumes the responsibility of actually performing the I/O.While the device driver waits for the device to respond, the application's thread is not suspended as it waits for the I/O request to complete. Instead, this thread continues executing and performs other usefull tasks.

但是采用了异步I/O之后,实际测试过程中,CreateFile有时候需要16ms,而WriteFile经常超过2ms,甚至超过100ms。因此我考虑文件句柄放到全局,一开始的时候创建,退出程序前销毁,同时将日志先丢到缓冲,每隔1s再一次性写到文件中。

改进后的日志函数

static unsigned short  EnableLog = 1;		//允许写日志
static unsigned short  EnableDbgView = 0;	//允许写到debug view
static HANDLE g_hLogFile = NULL;
static OVERLAPPED g_laped = {0};
TCHAR g_szFilePath[MAX_PATH];

int MyIO(LPCSTR fmt, ...)
{
	va_list ap;
	SYSTEMTIME _SysTime;


	char MsgBuf[512] = {0};	


	bool bFlush = false;
	static clock_t tLastLog = clock()-1;
	static clock_t tLast = clock()-1;
	clock_t tStart = clock();


	char szToday[50] = {0};
	static char	szDate[50] = {0};
	static char	szTime[50] = {0};
	static char szBufferPerSec[1024*1024] = {0};
	static int nCurFileSize = 1024*1024;


	va_start( ap,fmt );
	vsprintf( MsgBuf,fmt,ap );
	va_end( ap );
	if(MsgBuf[strlen(MsgBuf)-1] == '\n')  
		MsgBuf[strlen(MsgBuf)-1] = '\0';


	if(EnableLog)
	{
		SYSTEMTIME st;  
		if(tStart>tLast)
		{
			GetLocalTime(&st);              // 取当前系统时间
			sprintf_s(szTime, sizeof(szTime), "%02d:%02d:%02d.%03d", \
					st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);


			if(tStart-tLastLog>1000)
			{
				bFlush = true;	
				tLastLog = tStart;
			}		
			tLast = tStart;
		}
		
		sprintf_s(szToday, sizeof(szToday), "%04d%02d%02d", st.wYear, st.wMonth, st.wDay);
		if(strcmp(szToday, szDate))
		{
			if(g_hLogFile!=NULL)
			{
				CloseHandle(g_hLogFile);
				g_hLogFile = NULL;
			}
			strcpy(szDate, szToday);
		}


		int nBufLen = strlen(szBufferPerSec);
		sprintf(szBufferPerSec+strlen(szBufferPerSec)/*, sizeof(szBufferPerSec)*/, "%s -> %s\r\n", szTime, MsgBuf);	


		if(bFlush)
		{
			if(g_hLogFile == NULL)
			{
				TCHAR szFileName[MAX_PATH];
				sprintf_s(szFileName, sizeof(szFileName), "%sMyIODemo_%s.txt", \
					g_szFilePath, szDate);
				g_hLogFile = CreateFile(szFileName, 
						GENERIC_WRITE,
						FILE_SHARE_READ,//|FILE_SHARE_DELETE,
						NULL,
						OPEN_ALWAYS,//if not exist,creat new //CREATE_ALWAYS,
						FILE_FLAG_OVERLAPPED/*FILE_ATTRIBUTE_NORMAL*/,
						0);


				if(g_hLogFile == INVALID_HANDLE_VALUE)
				{
					TRACE("Error!!!can't open %s to write", szFileName);
					return -1;
				}
				SetFilePointer(g_hLogFile, nCurFileSize, NULL, FILE_BEGIN);
				SetEndOfFile(g_hLogFile);
			}		
                        //!sld! 2011.9.13 快满的时候,文件尺寸翻倍。如果不加这个的话,一旦写入超过文件尺寸,最后文件Log将没有内容,全是NULL。
			if((int)(nCurFileSize - g_laped.Offset - strlen(szBufferPerSec))<=0)
			{
				nCurFileSize = nCurFileSize*2;
				SetFilePointer(g_hLogFile, nCurFileSize, NULL, FILE_BEGIN);
				SetEndOfFile(g_hLogFile);
			}
			BOOL bRet = WriteFile(g_hLogFile, szBufferPerSec, strlen(szBufferPerSec), 
				NULL, &g_laped);


			if(!bRet)
			{
				DWORD dwErrNo = GetLastError();
				if(dwErrNo!=ERROR_IO_PENDING)
				{
					char szErr[1024];
					sprintf_s(szErr, sizeof(szErr), "Error!Could not write to file (error %d)\n", dwErrNo);      
					OutputDebugString(szErr);
				}
			}


			if(clock()-tStart>2)
			{				
				OutputDebugString("Error!log time-consumingly to file");
			}
			g_laped.Offset+=strlen(szBufferPerSec);
			memset(szBufferPerSec, 0, sizeof(szBufferPerSec));
			
		}
	}


	if(EnableDbgView)
		OutputDebugString(MsgBuf);


	return 0;
}
现在经过测试WriteFile开销还是像之前一样,但是已经可以容忍了。没有在多线程环境下测试,但是料想应该能正常工作。

IAR日志输出配置:跟踪运行状态的方法 通过IAR环境下的日志输出设置,开发者能高效监控代码执行流程。结合iar调试功能,实现对运行状态的精准追踪,提升问题定位效率,是嵌入式开发中不可或缺的调试手段。 阅读详情

相关推荐

【IC】Redhawk-SC输入件配置

Redhawk sc

u011808788的博客 1777

软件运行过程中日志文件的书写

Imports System.IO Public Class WriteLog '创建日志文件 Public Shared Sub CreateLogFile() Try If Not File.Exists(AppDomain.CurrentDomain.BaseDirectory & "run.log") Then

程序猿修炼 3225

Doris Streamloader安装教程

Doris Streamloader 是一款用于将数据导入 Doris 数据库的专用客户端工具。本文介绍其在centos7环境下的安装过程

qq_44540985的博客 1090

C++ 输出日志到 DbgView

首先感谢木杉老哥对 OutputDebugString 的封装,解决了日志信息不能直接打印变量的问题,代码简单易懂,解决了我的困难。 新建一个头文件,log.h: #ifndef _DEBUGPRINTF_H_ #define _DEBUGPRINTF_H_ #include<Windows.h> #include <tchar.h> ...

LYSM 1963

保姆级教程:用MS建完分子模型,如何一键转成LAMMPS能用的data文件?

本文详细介绍了如何将Materials Studio(MS)构建的分子模型转换为LAMMPS可用的data文件的全流程。重点讲解了力场选择、环境配置、转换命令参数解析及常见问题排查,帮助初学者快速掌握MS与LAMMPS之间的数据转换技巧,特别适合计算材料学领域的研究人员。

weixin_42665255的博客 471

一种基于OpenPose和DNN的跌倒检测软件系统(中文&英文)

学习并实现一种跌倒检测模型,此模型基于OpenPose实现逐帧人体姿态检测,使用DNN识别人体动作。检测方式有在线与离线两种,本文主要测试了离线模式,测试效果较好。

Tongzhou_Li的博客 2645

win32 api应用如何输出日志log Windows程序设计 visualstudio vs无控制台如何输出

共两个代码,借鉴了《Windows程序设计(第五版)》P34页代码 第一步在项目中添加两个代码,LogUtil.cpp 和 LogUtil.h LogUtil.cpp 如下 #include &lt;windows.h&gt; #include &lt;tchar.h&gt; #include "LogUtil.h" #ifndef _DEBUG void CDECL logd(cons...

lala的博客 3891

Unity开源游戏源码精选:17个亲测可运行教学项目

Unity游戏开发中,掌握真实项目结构与工程实践远比熟记API更重要。本文围绕‘Unity开源项目’和‘游戏源码’两大核心概念,解析高质量开源资源的本质特征——结构清晰、逻辑可溯、修改即时反馈。从基础的MonoBehaviour生命周期组织,到进阶的ScriptableObject数据驱动、事件总线解耦,再到架构级的DOTS ECS设计与网络同步模式,技术价值在于将抽象原理落地为可调试、可验证、可迁移的代码范式。适用于新手建立开发直觉、中级开发者突破系统设计瓶颈,以及团队构建可维护框架。所有项目均基于Uni

weixin_31528001的博客 720

C语言简单写日志函数

#include #include #include /* debug level define */ int g_dbg_level; FILE *g_log_fp; /* debug level */ #define DBG_INFOR 0x01 // call information #define DBG_WARNING 0x02 // paramters

wangwenwen的专栏 6984

别再让充电器‘罢工’了!聊聊Charger里VIN-DPM这个不起眼但关键的功能

本文深入探讨了充电IC中的VIN-DPM(输入电压动态电源管理)功能,揭示其如何智能调节充电策略以避免电源适配器过载。通过实际案例和实验室数据,展示了VIN-DPM在提升充电稳定性和效率中的关键作用,并提供了选型、设计和故障排查的实用指南。

weixin_33738578的博客 416

把云端打印带回办公室:通信场景 SAP_COM_0466 打通 SAP BTP ABAP environment 与本地打印机的 Pull Integration 实战

摘要: 本文探讨云端SAP系统如何通过SAP_COM_0466通信场景实现与本地打印机的安全连接。核心方案采用Pull Integration模式,由本地组件(如SAP Cloud Print Manager或第三方OMS)主动从云端打印队列拉取任务,避免网络边界冲突。配置流程涵盖通信安排创建、打印队列绑定及权限管理,并强调队列类型必须选择"Pull Integration Queue"的关键细节。方案通过分离云端生产与本地打印环节,既保障安全性又便于运维监控,适用于SAP BTP AB

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 144

不影响正常运行的调试技巧 文件日志

确保日志级别设置为适当的级别,如`ERROR`、`WARNING`、`INFO`等。在生产环境中,通常建议将日志级别设置为`ERROR`或`WARNING`,以减少日志文件的大小并提高性能。的形式,可实现日志形式的调试,如接口服务端调试。通过以上措施,可以在生产环境中有效地进行文件日志调试,确保应用程序的稳定性和可靠性。- 使用条件日志记录,只在特定条件下记录日志,以减少日志文件的大小。- 在代码中添加日志语句,以便在出现问题时能够追踪问题的来源。- 使用日志分析工具来分析日志数据,以发现潜在的问题。

hudeyong926的专栏 300

YOLOv11-seg改进系列 | 引入CMTFNet(TGRS2023)+TransNeXt(CVPR2024)的C3k2_MSMHSA_CGLU模块,多尺度空洞注意力扩大感受野,增强复杂背景下

本文提出一种改进YOLOv11分割模型的C3k2_MSMHSA_CGLU模块,融合多尺度多头自注意力(MSMHSA)和卷积门控线性单元(CGLU)。MSMHSA通过多尺度空洞卷积提取不同感受野特征,生成跨尺度注意力,降低计算量同时保留全局上下文。CGLU采用卷积门控机制增强特征变换能力。实验表明,改进后模型参数量仅增加7.8%,GFLOPs增加4.2%,显著提升了多尺度目标检测性能,尤其适用于遥感图像等复杂场景。该模块有效解决了原始C3k2感受野有限、缺乏全局建模能力的问题。

qq_44224801的博客 200

【Linux】日志函数

在这个示例中,我们定义了一个MyPrintF函数,它接受一个缓冲区、缓冲区的大小、一个格式字符串和可变数量的参数。vsnprintf函数是C语言标准库中的一个函数,它的作用是将格式化的数据写入一个字符串缓冲区中,同时允许指定缓冲区的大小,以防止缓冲区溢出。通过为日志信息分配不同的级别,开发者可以更精细地控制日志的生成和输出,从而在不同的场景下获取最有价值的信息。可变参数列表,顾名思义,就是函数的参数个数不是固定的,可以根据需要传入任意数量的参数(但通常至少需要一个固定参数来指示后续可变参数的类型或数量)。

2301_79181030的博客 2078

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

如何查看mysql函数运行日志_如何查看mysql运行、访问记录等日志

如何查看mysql运行、访问记录等日志1.首先确认你日志是否启用了mysql>show variables like 'log_bin';mysql>show variables like 'log';2.如果启用了,即ON如果没有启用,即OFF,要修改my.cnf# The MySQL server[mysqld]# Uncomment the followi...

weixin_34365336的博客 894

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

c语言 日志模块,一个简单又高效的日志系统

下载源代码摘要:本文给出一个性能高,使用简单的日志解决方案。本模块实现日志信息的批量写入文件,定时自动flush到文件中,写入文件的日志级别可动态调整,单个日志文件大小可配置,循环对日志文件写入,这样不会造成机器空间被日志文件耗尽。关键字:日志 性能 日志级别一、程序日志是商品程序中必不可少的部分。在正式商用的程序中一般对于日志都会有一些类似的要求:性能要求运行时日志级别可调整日志文件空间使用安全...

weixin_32652107的博客 1257

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

查询日志中哪些SQL语句对性能影响最大?

涉及多个表的复杂连接,特别是当连接条件不清晰、索引使用不当或表的大小非常大时。:查询条件中涉及的字段如果没有适当的索引,MySQL可能需要进行全表扫描来找到匹配的行,这非常消耗资源。:即使没有缺少索引,某些查询(尤其是没有WHERE子句或WHERE子句条件不充分的查询)可能会导致全表扫描,这对于大型表来说是非常低效的。:嵌套在另一个查询中的子查询可能导致性能下降,尤其是当子查询需要处理大量数据时。:某些查询可能需要使用临时表来存储中间结果,这会增加I/O操作和内存使用。

wddblog的博客 1460

LOG日志详解

欢迎关注作者博客 简书传送门 文章目录Log的用途记录Log的基本原则日志的级别划分日志对性能的影响什么时候输出日志系统启动参数、环境变量异常捕获处函数获得期望之外的结果时关键操作日志输出的内容什么时候使用J2SE自带的日志典型问题分析该用日志的地方不用啰嗦重复、没有重点日志和异常处理的关系System.out方式的日志日志信息不明确忘记日志输出是多线程公用的多个参数的处理 参考文档 Log的用途...

阿祥小王子的博客 1万+

在Vue中如何不影响业务代码的情况下实现页面埋点

实现思路 我们的目的是在不引入外部SDK,业务代码方完全无感知的情况下实现页面的日志采集功能。由于在Vue中每一次的页面跳转都会进入路由的beforeEach和afterEach钩子函数,因此我们将借助路由实现业务代码无感知的埋点功能。 在此之前,需要保证项目中除了日志服务之外其他的请求都会经过一个入口方法,因为 我们会将日志信息进行聚合,避免发送过多的请求以减轻日志服务器的压力。 客户端交互日志...

我的小熊不见了 2741

log日志输出规范

目录 1、Log的用途 2、记录Log日志的基础原则 2.1、日志级别划分 2.2、日志对性能的影响 2.3、什么时候输出日志 2.3.1、系统启动参数、环境变量 2.3.2、异常捕获 2.3.3、函数获得期望之外的结果时 2.3.4、关键操作 2.4 日志输出的内容 3 日志API规范 3.1Log对象的声明和初始化 3.2不得使用System.out, Sy...

半生苦涩半生酸的博客 1万+

HiChatBox UART输出运行日志调试

本文介绍如何利用UART串口日志实现HiChatBox智能语音终端的高效调试,涵盖日志系统架构、分级机制、初始化代码及实际排错案例,展现嵌入式开发中串口日志在无屏设备故障定位中的关键作用。

weixin_42452483的博客 1001

怎么在mysql查看运行日志_如何查看mysql运行、访问记录等日志

如何查看mysql运行、访问记录等日志1.首先确认你日志是否启用了mysql>show variables like 'log_bin';mysql>show variables like 'log';2.如果启用了,即ON如果没有启用,即OFF,要修改my.cnf# The MySQL server[mysqld]# Uncomment the followi...

weixin_34221197的博客 5676

python如何添加函数库_Python如何给函数库增加日志功能

问题你想给某个函数库增加日志功能,但是又不能影响到那些不使用日志功能的程序。解决方案对于想要执行日志操作的函数库而已,你应该创建一个专属的 logger 对象,并且像下面这样初始化配置:# somelib.pyimport logginglog = logging.getLogger(__name__)log.addHandler(logging.NullHandler())# Example f...

weixin_39879522的博客 117

java log日志函数_java Log日志规范

一个在生产环境里运行的程序如果没有日志是很让维护者提心吊胆的,有太多杂乱又无意义的日志也是令人伤神。程序出现问题时候,从日志里如果发现不了问题可能的原因是很令人受挫的。本文想讨论的是如何在Java程序里写好日志。一般来说日志分为两种:业务日志和异常日志,使用日志我们希望能达到以下目标:1.对程序运行情况的记录和监控;2.在必要时可详细了解程序内部的运行状态;3.对系统性能的影响尽量小;Java日志...

weixin_30679293的博客 994

函数的参数个数是不固定_数据平滑迁移,如何不影响服务?

一、问题的提出互联网有很多“数据量较大,并发量较大,业务复杂度较高”的业务场景,其典型系统分层架构如下:(1)上游是业务层biz,实现个性化的业务逻辑(2)中游是服务层service,封装数据访问(3)下游是数据层db,存储固化的业务数据服务化分层架构的好处是,服务层屏蔽下游数据层的复杂性,例如缓存、分库分表、存储引擎等存储细节不需要向调用方暴露,而只向上游提供方便的RPC访问接口,当有一些数据层...

weixin_39923157的博客 247
上一篇: Resiprocate 如何使用STUN
下一篇: TortoiseGit(1.7.2.0) 无法提交
soliddream66
博客等级 码龄19年 93粉丝 65原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值