MFC下的日志系统开发

MFC实现log日志信息显示,程序运行状态显示 前言 在界面程序运行过程中,很多时候我们都希望知道程序运行到哪一步,或者程序的中间输出结果希望打印到界面上,这就是log信息显示功能。 控件:Edit Control 控件属性设置 这里使用Edit Control显示log信息。 首先,拖拽一个Edit Control控件以及一个Text Control控件到界面上,对其属性进行一些修改,其中Mutiline是最重要的,否则Edit Control不支持多行,会直接忽视换行符。 为控件添加变量 之后需要为新建的Edit Control控件关联两个变量。 阅读详情

1.前言

中大型软件项目中,日志系统是不可或缺的组成部分。尤其随着软件规模越来越大,内部结构越来越复杂,日志调试成为一种重要的调试方法。本文介绍了一个通用,高效,简洁的日志系统的设计思路。

文章分两部分,一部分是负责日志记录的CTLog类,另一部分是负责日志显示CTLogEdit类。

2.记录类CTLog

谈到通用日志系统,首先要考虑到三个问题:第一个是日志本身面向的对象要广泛,也就是日志要给谁看,都要记录什么样的信息。第二就是要考虑如何减少记录操作,将日志记录对整个系统的占用降到最低。最后,还要考虑如何管理日志,减少因为记录信息过多而造成对硬盘空间的浪费。

2.1日志分级

面对第一个问题,最好的解决方案就是提供日志分级系统。针对不同人的需求记录不同等级的信息。在这里我提供的建议是四级系统,我把它们写入一个enum中,如下所示:

enum  TLOGPRIORITY

{

    TLP_DEBUG = 0,

    TLP_DETAIL,

    TLP_NORMAL,

    TLP_ERROR

};

 

其中,TLP_ERROR是最高等级,代表记录软件的各种异常信息,这也是最应该被反映在你的日志当中的。

TLP_NORMAL记录的是软件中的关键信息,类似于记录开始,初始化,连接握手开始等等。这些信息也是应该被你的软件用户所知情的。

TLP_DETAIL则记录了软件后台各种更加详细的信息,比如何时开启工作线程,何时销毁数据对象,通信握手具体进行到哪一步之类。此类信息,可以帮助你很好验证软件的功能是否实现,提供debug依据。

TLP_DEBUG是最低的等级,我个人不建议没事使用这一等级。它提供了在某些特殊情况下的动态调试,可以作为安插在代码中的记录标签,用于那些静态跟踪无能为力的场合。这是调试的终极手段。

2.2记录线程

提高日志效率的本质就是尽量减少对硬盘的操作次数。为了达到这个目的,我们可以使用开辟缓冲区的方法,并且用另外一个线程定时从缓冲中将信息写入文件中。在CTLog中,负责提取数据的是线程函数TLogThread,代码如下:

UINT CTLog::TLogThread(LPVOID wParam)

{

    CTLog* pLog = (CTLog*)wParam;

    while(pLog->m_bRun)

    {

       Sleep(3000);

       EnterCriticalSection(&pLog->m_csTLog);

       if (pLog->m_strTLogBuffer.GetLength())

       {

           pLog->m_pFile->WriteString(pLog->m_strTLogBuffer);

           pLog->m_pFile->Flush();

           pLog->m_strTLogBuffer.Empty();

       }

       LeaveCriticalSection(&pLog->m_csTLog);

       if (pLog->m_pFile->m_hFile != INVALID_HANDLE_VALUE)

       {

if (pLog->m_pFile->GetLength() >= pLog->m_ullMaxFileSize)

           {

              pLog->OpenNewFile();

           }

       }

    }

    EnterCriticalSection(&pLog->m_csTLog);

    if (pLog->m_pFile->m_hFile != INVALID_HANDLE_VALUE)

    {

pLog->m_pFile->WriteString(pLog->m_strTLogBuffer + _T("/r/n"));

       pLog->m_pFile->Flush();

    }

    LeaveCriticalSection(&pLog->m_csTLog);

    return 0;

}

 

该线程每三秒钟会唤醒一次,从CTLog中的缓冲区m_strTLogBuffer中提取字符流写入硬盘,然后将缓冲清空。需要注意的是,所有这些操作都应该在临界段中完成。

2.3日志管理

前面提到的最后一个问题就是如果日志数量太多了该怎么办?很多信息都是没用的了。答案很简单,把它删掉!为了方便删除,我们可以限制每个日志文件的大小,超过一定大小就重新开启一个新文件记录,并为旧日志打上时间戳。这样,你就可以根据文件建立时间很方便的删掉不需要的记录了。

前文中日志线程TLogThread会在每次唤醒时检查文件大小,如果超过限制,就会调用OpenNewFile重新开启新记录,该函数代码如下:

void CTLog::OpenNewFile()

{

    EnterCriticalSection(&m_csTLog);

    m_pFile->Close();

    CFileStatus fs;

  CFile::GetStatus(m_strTLogFilePath + _T("//debug.log"), fs);

CFile::Rename(m_strTLogFilePath + _T("//debug.log"), m_strTLogFilePath + _T("//debug_") + fs.m_ctime.Format(_T("%Y%m%d_%H%M%S")) + _T(".log"));

m_pFile->Open(m_strTLogFilePath + _T("//debug.log"), CFile::modeCreate | CFile::modeReadWrite | CFile::typeBinary | CFile::shareDenyNone, NULL);

    m_pFile->Write("/377/376", 2);

    LeaveCriticalSection(&m_csTLog);

}

 

这个函数会在后台将当前的文件按照建立时间改名,并重新建立一个名为debug.log的文件,作为当前日志的写入文件。

2.4记录信息

记录信息和线程函数的操作正相反,只是向缓冲区中添加字符串。为了增加灵活性,我没有在记录函数中增加时间戳和自动换行符,这些可以根据你自己的需要安排。

需要注意的一点是,插入动作同样需要在临界段中完成。

BOOL CTLog::TLogLine(LPCTSTR lpTLogLine, TLOGPRIORITY tlp)

{

    if (tlp < m_TLogPriority)

    {

       return FALSE;

    }

    if (_tcslen(lpTLogLine))

    {

       EnterCriticalSection(&m_csTLog);

       m_strTLogBuffer += lpTLogLine;

       LeaveCriticalSection(&m_csTLog);

       return TRUE;

    }

    else

    {

       return FALSE;

    }

}

 

3.日志显示类CTLogEdit

第二节讲述的记录类,往往我们的程序中还需要一个特定的窗体来同步显示日志记录,这样我们就需要一个经过重载的文本框来提供显示支持。在这里CTLogEdit类重载自CRichEditCtrl。

作为一个日志显示的控件,我们同样需要首先考虑三个问题:第一,该控件最好支持按照日志分级来给记录着色。第二,尽可能的减小显示开销,避免闪烁,而且最好能限制显示的行数。最后,可以支持自动滚屏,显示最新的数据。

3.1文字着色

关于文字着色,我们可以利用父类中CRichEditCtrl的setsel函数和SetSelectionCharFormat函数。在CHARFORMAT2中记录字符的颜色信息,通过setsel选定某一行字符,然后调用SetSelectionCharFormat来替换颜色值。

我在这里将着色部分封装成函数SetLogLineColor,代码如下:

void CTLogEdit::SetLogLineColor(long lPos, TLOGPRIORITY tlp)

{

    SetSel(lPos, lPos);

    CHARFORMAT2 cf;

    memset(&cf, 0, sizeof(CHARFORMAT2));

    cf.dwMask |= CFM_COLOR;

    cf.dwEffects &= ~CFE_AUTOCOLOR;

    switch (tlp)

    {

    case TLP_DEBUG:

       {

           cf.crTextColor = TLP_DEBUG_COLOR;

           break;

       }

    case TLP_DETAIL:

       {

           cf.crTextColor = TLP_DETAIL_COLOR;

           break;

       }

    case TLP_NORMAL:

       {

           cf.crTextColor = TLP_NORMAL_COLOR;

           break;

       }

    case TLP_ERROR:

       {

           cf.crTextColor = TLP_ERROR_COLOR;

           break;

       }

    }

    SetSelectionCharFormat(cf);

}

3.2提高显示效率

日志显示跟记录一样同样存在效率问题,尽可能减少显示次数也是我们要做的工作。这里思路和CTLog是一致的,都是向缓冲区写入数据,然后依靠另外的线程将信息提取出来显示。所不同的是,这里缓冲区就不能单单记录字符,还要将着色信息一并保存。我的做法是将日志字符跟日志等级信息封装在一起,放入一个缓冲队列中。数据结构如下:

typedef struct _TLOGITEM

{

    CString line;

    TLOGPRIORITY tlp;

} TLOGITEM, *PTLOGITEM;

 

记录函数TLogLine会将信息存入一个双向队列m_TLogItemQue,该成员对象属于CPtrList类型。同样,这个操作也是在临界段中完成的。

void CTLogEdit::TLogLine(LPCTSTR lpTLogLine, TLOGPRIORITY tlp)

{

    EnterCriticalSection(&m_csTLogEdit);

    PTLOGITEM pItem = new TLOGITEM;

    pItem->line = lpTLogLine;

    pItem->tlp = tlp;

    m_TLogItemQue.AddTail(pItem);

    LeaveCriticalSection(&m_csTLogEdit);

}

 

剩下来所有的显示操作都在单独的线程TLogEditThread中完成,这个线程会每隔一秒种从缓冲队列中提取出记录信息,并将之着色显示出来。代码如下:

UINT CTLogEdit::TLogEditThread(LPVOID wParam)

{

    CTLogEdit* pEdit = (CTLogEdit*)wParam;

    PTLOGITEM pItem;

    long pos;

    while(pEdit->m_bRun)

    {

       Sleep(1000);

       while(!pEdit->m_TLogItemQue.IsEmpty())

       {

           EnterCriticalSection(&pEdit->m_csTLogEdit);

           pItem = (PTLOGITEM)pEdit->m_TLogItemQue.RemoveHead();

           LeaveCriticalSection(&pEdit->m_csTLogEdit);

           pos = pEdit->GetTextLength();

           pEdit->SetLogLineColor(pos, pItem->tlp);

           pEdit->ReplaceSel(pItem->line);

           pEdit->LimitLine();

           delete pItem;

           if (pEdit->m_bAutoScroll)

           {

              pEdit->SendMessage(WM_VSCROLL, SB_BOTTOM);

           }

       }

    }

    return 0; 

}

这里还有一个加快显示的技巧。由于每次插入的数据在最后,因此,我们只要用setsel选中当前对话框文本的最后,并且调用ReplaceSel替换成新的字符串就可以了。记住,不要使用SetWindowText之类的函数,那样只会使你的程序效率降低。

另外出于减小内存消耗的考虑,我们不可能无休止的让日志显示在文本框中,因此有必要对整个文本框的显示行数进行控制。这里我将之封装成函数LimitLine,代码如下:

void CTLogEdit::LimitLine()

{

    if (GetLineCount() - 1 > m_nLineLimit)

    {

       SetSel(0, LineLength(0) + 1);

       Clear();

    }

}

 

这个函数会通过调用GetLineCount得到当前行数,如果超过限制,则选中最上面一行,并调用Clear清除掉。

3.3自动卷动

相比之下,实现这个功能是比较简单的。在前面的TLogEditThread中,通过向滚动条发送WM_VSCROLL消息,并将之定位SB_BOTTOM完成了这个效果。

4.总结

总体来说,一个高效日志系统需要我们提供分级,多线程写入和显示功能。将这两个类配和起来使用,能很好实现设计要求。

另外,为了方便使用,还可以在程序中建立一个全局或者静态函数,将CTLog和CTLogEdit的写入函数封装起来加以调用。你甚至还可以再定义若干个宏,可以达到更加方便的效果J。

MFC 打印日志 阅读详情

相关推荐

GetTickCount()的隐藏玩法:MFC程序性能监控与运行日志分析

本文深入探讨了GetTickCount()在MFC程序中的高级应用,包括性能监控、运行日志分析和多线程环境下的实践技巧。通过实战代码示例,展示了如何利用这一Windows API实现精确的代码段计时、帧率计算以及构建高效的日志分析系统,帮助开发者优化MFC应用程序性能。

weixin_42522167的博客 176

C语言 C++ MFC 写日志到文件

【代码】C语言 写日志到文件。

chenhao0568的专栏 959

奇安信Windows客户端开发面试指南:从C++基础到终端安全实战

在网络安全领域,终端安全产品的客户端开发与普通应用开发有着本质区别。这类程序常驻用户系统,需要在对抗环境下长期稳定运行,对Windows底层原理、C++内存模型及安全对抗技术提出了更高要求。理解进程线程通信、虚拟内存布局、消息机制等系统知识,掌握智能指针、并发同步等语言特性,是构建可靠客户端的基础。调试能力与安全视角同样关键,从Windbg分析崩溃转储到自保护机制设计,都体现出安全工程师的核心竞争力。以奇安信天擎、奇安信代码卫士等产品为代表的终端安全工具,正是这些技术的典型应用场景。此外,跨平台适配如麒麟系

weixin_34353714的博客 544

MFC 写日志类

在编程中,日志的记录是很重要的,这是自己经常使用的一个日志记录类,给大家分享一下: 效果图:能够支持多个进程同时写一个LOG文件 格式如下:时间+方法名+行号+\n+[前缀]+错误原因 前缀主要用于多个进程写LOG的情况, 日志的名称:1、直接指定;2、如果不指定,则默认和程序同名。 给出类的下载地址:点击打开链接

dubuzherui的专栏 2278

MFC开发实战:CStringArray高效字符串管理技巧(附完整示例代码)

本文深入探讨了MFC开发中CStringArray的高效字符串管理技巧,包括预分配内存、批量操作和内存碎片管理等优化策略。通过实战示例和性能对比,帮助开发者提升应用程序的响应速度和内存使用效率,特别适合处理大量字符串集合的场景。

weixin_30415113的博客 355

MFC 日志类

只有在程序出问题以后才会知道打一个好的日志有多么重。该日志类是程序exe目录下,以年月日时命名的txt 存储。 //log.h 如下: class Log { public: static Log* instance; static Log* GetInstance(); Log(); ~Log(); bool WriteLog(CString strFilePath, CString str); bool WriteLog(CString str); ...

Zhengzhigang_ZBL的博客 1253

MFC USB调试助手开发实战:从设备枚举到数据收发的完整流程(附源码)

本文详细介绍了使用MFC框架开发工业级USB调试助手的完整流程。从项目架构设计、环境搭建,到核心的设备枚举、连接初始化,再到关键的异步数据收发与缓冲区管理,提供了基于WinUSB API的实战代码示例。文章深入探讨了如何构建一个稳定、高效的USB调试工具,涵盖错误处理、日志系统及用户体验优化,是嵌入式开发和硬件调试领域的实用指南。

i8j9k0l1的博客 439

基于MFC的WiFi检测软件设计与实现

你看,一个看似简单的WiFi检测软件,背后竟然藏着这么多门道:底层API调用 → 数据获取多线程架构 → 响应性保障数据解析 → 信息提炼图形界面 → 用户体验安全存储 → 长期可用日志分析 → 价值延伸它早已不是一个“玩具级”小程序,而是一个完整的无线网络诊断平台雏形。未来还能加什么功能?📊 实时信号强度曲线图🧭 信道干扰热力图(找出最拥挤的频段)🔍 MAC地址厂商识别(“这个设备是谁家的摄像头?”)

weixin_42613018的博客 427

html 出现省略号,html 超出出现省略号

GIS公交查询-flex&sol;java开发语言是flex.java,开发平台是myeclise.eclise,开发接口是arcgis api for flex,提供以下的功能: 1.站名-站名查询: 2.站点查询: 3.路线查询: 备注: ...计算ffff&colon;0~ffff&colon;b数据的和,结果存在dx中代码: assume cs:sad sad se...

weixin_36451610的博客 296

VC6环境下可直接编译的TCP/IP底层通信测试工具(含Raw Socket封装与日志记录)

一套面向Windows平台的轻量级网络协议测试工具源码,基于Visual C++ 6.0开发,无需第三方库即可编译运行。核心功能包括原始套接字(Raw Socket)操作支持,可手动构造并发送TCP、UDP、ICMP等IPv4数据包;内置连接状态监控模块,实时捕获收发行为;通过MySocket类封装底层socket调用,降低使用门槛;配置文件config.cfg支持参数持久化;日志系统自动写入rawlogfile.log,记录原始通信过程;配套资源完整,含对话框界面research1Dlg、配置读写模块My

2w3e4r5t6y的博客 165

MFC超市仓库管理系统:C++桌面开发教学实践指南

MFC(Microsoft Foundation Classes)是Windows平台下经典的C++ GUI开发框架,其消息机制、资源管理与Windows API封装逻辑,为初学者理解桌面应用底层原理提供了不可替代的教学入口。结合超市仓库这一典型业务场景,项目以Access数据库为数据存储载体,通过DAO模式、分层架构与工程化细节(配置文件、日志系统、编码适配),系统性串联C++语法、数据库操作、软件工程规范与UI交互设计四大核心能力。它不追求技术前沿,而聚焦‘可运行、可调试、可演进’的教学闭环,帮助学习者

weixin_33851604的博客 427

探索高效串口通讯新境界:基于MFC的串口助手深度解读

探索高效串口通讯新境界:基于MFC的串口助手深度解读 【下载地址】MFC实现的串口助手源码 本资源提供了使用Microsoft Foundation Classes (MFC)编写的串口助手的完整源代码。此工具旨在简化串口通信的开发和测试工作,对于需要进行串口操作的开发者来说是一个非常实用的辅助工具。作者亲力亲为,精心设...

gitblog_09806的博客 488

MFC日记管理软件:界面构建与功能实现深度剖析

本文还有配套的精品资源,点击获取 简介:MFC是微软提供的面向对象C++库,用于创建Windows应用程序。本文深入解析了一个基于MFC的日记管理软件,该软件通过CDialog类和dslite工具分别构建用户界面和视觉美化。软件利用CDialog创建对话框,通过资源编辑器定义布局和控件,使用dslite提升用户界面美观度。功能上,该软件支持日记的存储、查看、搜索、编辑、备份...

weixin_34064233的博客 813

MFC 实现轮流写两个文件

开发工具:vs2010 实现功能:用MFC实现轮流写2个文件,当一个文件超过500k,会清空另一个文件并重新写,支持软件下次启动时,从上次写的文件继续写,加入了锁支持线程安全 部分参考代码: void CLog::WriteLog( const char *pTemp ) { if (g_strLogFile[0].IsEmpty()) { CString strPath = Get

kevin的博客 512

C++日志记录实践:VC6 MFC环境下CLog类设计

本文还有配套的精品资源,点击获取 简介:本文深入探讨了在VC++和VC6 MFC框架下,如何实现一个C++的日志记录类。日志类的核心功能包括设置不同的日志等级、添加时间戳、格式化输出以及输出到多种目标。通过创建一个名为 CLog 的类,并详细描述了其成员函数的设计与实现。例如, WriteLog 函数负责将日志等级和消息格式化后写入到文件,而 ...

weixin_36197669的博客 1687

MFC 记录操作日志,使用Edit控件

MFC 中利用Edit控件记录操作日志,具体的使用方法可以看源码。

基于C++/MFC的Socket网络文件传输器开发实战

网络编程是构建分布式应用的核心技术,其基础在于理解Socket通信原理。Socket作为网络进程间通信的端点,通过TCP/IP协议栈实现可靠的数据传输。掌握Socket编程对于开发客户端/服务器应用具有重要价值,尤其在需要高性能、低延迟数据传输的场景中,如文件共享、实时通信等。多线程技术在此类应用中至关重要,它能有效防止耗时操作阻塞用户界面,提升程序的响应性和吞吐量。通过将网络I/O和文件操作置于独立的工作者线程,主线程得以保持流畅交互。本文以开发一个带图形界面的文件传输工具为例,深入探讨了如何结合MFC框

weixin_30718391的博客 358

MFC 获取windows系统日志

API获取windows系统日志,以及MFC的基本DEMO

上一篇: 以有序链表构成的开放式散列表
下一篇: 使用表驱动技术优化程序结构
vector03
博客等级 码龄16年 62粉丝 16原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值