- 浏览: 3416298 次
- 性别:
- 来自: 苏州
文章分类
最新评论
-
sonichy:
Qt5改动很多,要改改了。
基于QItemDelegate的例子1 SpinBoxDelegate -
我的主页6:
楼主,2.2子查询的分页方式:SELECT * FROM ar ...
Mysql 分页语句Limit用法 -
liguoqi:
非常感谢楼主的用心指导,工具以及图片例子都提供了 赞!
两款免费DCIOM 图像浏览软件介绍和DICOM图像例子供下载 -
liguoqi:
问下这个图片怎么解压损坏呀
两款免费DCIOM 图像浏览软件介绍和DICOM图像例子供下载 -
liguoqi:
楼主讲解的非常详细,还附带工具和图片例子,非常感谢
两款免费DCIOM 图像浏览软件介绍和DICOM图像例子供下载
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。
转:http://blog.csdn.net/vector03/article/details/6419199
发表评论
-
更换博客地址告示
2012-12-03 18:56 2523更换博客地址告示 ... -
收款主页设计
2012-11-17 11:01 0文档信息 版权声明:自由转载-非商用-非衍生- ... -
10个精选国外免费空间
2012-11-12 16:06 6256最近想申请一个免费空间,发现国内基本没有。。。。唉。 ... -
长光所考博信息
2012-09-24 14:25 2627总结一下,以后有个奔头。。搞科研的收入太微薄了。唉。。。 ... -
The file contains a character that cannot be represented in the current code pag
2012-08-17 10:17 6241vs2008 打开网上下载的Qt项目,结果编译时无法通过,出 ... -
64位操作系统,编译程序出现X86和X64的冲突
2012-08-16 20:10 0问题1:1>QtGuid4.lib(QtGuid4.d ... -
U盘制作WinPE系统【转】
2012-07-27 11:36 2443不带光驱的笔记本的网友和没有光驱的台式机的网友如何安 ... -
博客文章标题横线框
2012-06-28 11:47 0分析 ... -
触摸屏介绍
2012-05-30 16:03 1928有一个项目,需要购买一台触摸屏设备。 了解一下触摸屏的基础知 ... -
有谁知道高速大数据量无线传输技术
2012-05-28 11:53 2425有谁知道高速大数据量无线传输技术? 想了解一下无线传输 ... -
自由在博客里插入广告,有钱你不赚吗???
2012-05-25 18:37 01、一直在ITeye里写技 ... -
自由在博客里插入广告,有钱你不赚吗???
2012-05-25 15:22 6546其实这些广告也没啥意 ... -
支持javascript的博客汇总
2012-05-25 09:27 2418写这篇文章的目的主要是为想在公共博客上布置广告的朋友提供一些参 ... -
支持js代码的博客有哪些?
2012-05-25 09:20 32141、支持js代码的博客谷歌博客, 雅虎奇摩,博客大巴,jimd ... -
VS2008无法下断点调试的原因
2012-05-02 11:18 19271VS2008单步调试时出错,原来的断点处显示一个空心圆加 ... -
VS2008无法下断点调试的若干解决办法
2012-05-02 09:19 3986我使用了第一种方法,暂时恢复了断点调试。 调试程 ... -
【转】串口通讯—通信协议
2012-02-29 00:11 2596转:http://cs.nju.edu.cn/y ... -
Ankhsvn 改名出错
2012-02-16 18:32 3124总结只改变文件名字大小写的最终解决方法。 这种只需要改 ... -
C++接口实现总结
2012-02-04 14:51 32811网上的例子,稍微有点错误。我给更改一下,附件上有源码!如有错误 ... -
visio2010 用键盘上的上下键为什么移动不了选中的部分,而是整个画布都在移动?
2011-12-23 14:56 10492visio2010画的流程图,选中框图后,用键盘上的上下键为什 ...
相关推荐
一个通用的二进制日志数据分析工具系统源码。它能做什么? 分析任意格式的二进制数据,还能同时查看协议文档 逐字节、逐位分析 手动、自动分析 对分析结果建透视图,发现规律,学习协议 怎么做到的 工具以插件化...
系统采用了基于角色的访问控制,角色和菜单关联,一个角色可以配置多个菜单权限;然后再将用户和角色关联,一位用户可以赋予多个角色。这样用户就可以根据角色拿到该有的菜单权限,更方便管理者进行权限管控。 本...
毕业设计,基于SpringBoot+Vue+MySql开发的前后端分离的通用管理系统开发模板,内含Java完整源代码,数据库脚本 本软件是基于 Vue 和 SpringBoot 的前后端分离开发模板,包含了登陆注册、用户管理、部门管理、文件...
Visual C# 2005数据库通用模块开发与系统移植 配书目录及代码,这样看代码就方便了 1of 3 环境: Visual Studio .NET 2005 And Microsoft SQL Server 2005 内容简介 Visual C# 2005是Microsoft公司开发的新一代...
基于SpringBoot、Vue开发的通用后台管理系统,做到开箱即用,为新项目开发省去了基础功能开发的步骤。 基于SpringBoot、Vue开发的通用后台管理系统,做到开箱即用,为新项目开发省去了基础功能开发的步骤。此系统...
cOffice通用OA系统源代码 Asp.net通用OA系统源代码,也名飞思协同办公管理系统 除了具有传统OA的邮件、工作流、文档等功能外,还引进了项目管理和知识管理的思想,更加注重工作任务的分解、协同和监督;知识的...
基于SpringBoot+Vue 的企业级 智能通用报表 调度平台 管理系统 项目经过严格测试,确保可以运行! 报表是所有企业都必要的分析决策工具,传统的展示报表的方式特别麻烦,步骤大概要经历 - 1、数据库中创建目的表,...
本软件是基于 Vue 和 SpringBoot 的通用管理系统,是一个很好的前后端分离开发模板,可用于开发毕业设计,包含了登陆注册、用户管理、部门管理、文件管理、权限管理、日志管理、个人中心、数据字典和代码生成这九个...
SpringBoot+Mybaits+vue3+elementplus通用管理系统实例(前端+后端),搭建通用管理系统后台,实现管理系统常用的功能,比如日志管理、登录、找回密码、权限管理、用户管理、角色管理、系统配置、数据字典、系统消息...
品达通用权限系统基于SpringCloud(Hoxton.SR1) +SpringBoot(2.2.2.RELEASE) 的微服务框架,具备通用的用户管理、资源权限管理、网关统一鉴权、XSS防跨站攻击等多个模块,支持多业务系统并行开发,支持多服务并行开发...
项目是一款简单高效的通用后台管理系统,是一套基于Spring Boot2.1.5 + Mybatis1.3.2+miniui 开发出来的通用后台管理系统框架,支持上传图片视频等文件。 比其他前端节省代码30%,代码和页面都具有可复用性,比较适合...
该系统是一个简单实用的二次开发框架,内置了完整的权限架构,包括菜单、角色、用户、字典、日志和代码生成等系统常规模块。该框架旨在帮助一般管理系统避免重复造轮子,开发者只需关注新增功能的form界面和业务逻辑...
系统日志管理 菜单管理 系统权限维护 角色权限管理 部门管理 用户管理 角色管理 1.3修改部分: 1.系统权限维护数据(控制器和动作不对应,主要是tbPermission录入数据的问题)出现大量的录入出错,已修正,请...
MVC通用权限系统1.3源码 功能介绍: 前端采用LigerUI开发!利用asp.net MVC3做的权限管理,采用EF数据访问,传统的N层架构 管理页面包括: 系统日志管理 菜单管理 系统权限维护 角色权限管理 部门管理 用户...
asp+txt成绩查询系统采用简单的asp+txt进行开发,软件极为简单却是个非常通用、非常方便的成绩查询系统,通用于几乎所有Excel单二维数据表查询。使用用途:适合修改不频繁、保密性不高的成绩、工资、物业水电费等...
后台功能模块: 系统设置:网站信息设置 网站公告管理 网站广告管理 友情链接管理 网站留言管理 网站评论管理 文章心情管理 关键词管理 文章作者管理 文章来源...系统相关:清除系统缓存 后台日志管理 前台日志管理
我也想做一个适用于大...日志管理:登录日志、操作日志 系统监控:资源监控、redis监控 接口限制:停用、限流 通知公告:富文本编辑器 功能测试:测试邮件发送、短信发送、钉钉发送 表单构建:拖拽完成页面导出vue文件
8.6.3 事件日志管理 231 8.6.4 系统配置管理 233 第9章 个人博客系统 236 9.1 系统总体设计 236 9.1.1 系统功能描述 236 9.1.2 系统功能模块划分 237 9.2 数据库设计 238 9.3 系统底层设计 242 ...
#资源达人分享计划#