Windows Mobile下使用Native C++开发日志类

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介:

背景

这段业余时间一直都在开发iToday。在iToday中加入日志管理。关于iToday,可以参考那些一些文章。

开源(Open Source)那些事儿 (一)

开源那些事儿 (二) - iToday开源项目计划

开源那些事儿(三)-iToday的总体设计

开源那些事儿(四)-如何使用CodePlex进行项目管理

 

简介

日志管理是程序不可以缺少的一个重要组成部分,对于长期运行的后台程序尤为重要,尽管经过了大量的测试,但是在实际运行环境下,程序未免有出错的时候。有时候由于第三方原因导致的,例如电信网络质量下载,掉包等等。在一些看似莫名其妙的问题下,日志文件很多时候就成了救命绳。bug free是我们一直追求的目标,但是我永远不能保证bug free,每次我在面试中说这句话,做销售出生的人会翻白眼,做技术的人会会心一笑。我能保证的是如何尽快的troubleshooting,提高质量,日志文件在这过程中又是最重要的手段之一。下面文章讲述使用Native C++对Windows Embedded CE和Windows Mobile日志文件类的封装。

 

代码

先上代码,下面分析。需要iToday全部代码也可以到codeplex上去下载。

类定义文件

typedef enum tagLOG_LEVEL
{
LOG_TRACE,
LOG_INFO,
LOG_WARNING,
LOG_ERROR,
LOG_FATAL,
LOG_NONE = 10,
}LOG_LEVEL;

class Logger
{
public:
static Logger& Instance();

static void SetLogFilePath( const std::string& strFilePath);
static void SetLogLevel( const LOG_LEVEL enLogLevel);
static void Initialise();
static void Dispose();

//void Log( LOG_TRACE const TCHAR *format, ... );
//void LogInfo( const TCHAR *format, ... );
//void LogWarning( const TCHAR *format, ... );
//void LogError( const TCHAR *format, ... );
//void LogFatal( const TCHAR *format, ... );

void Log( LOG_LEVEL logLevel ,const TCHAR *format, ... );

private:
/* more (non-static) functions here */
Logger(); // ctor hidden
Logger(Logger const&); // copy ctor hidden
Logger& operator=(Logger const&); // assign op. hidden
~Logger(); // dtor hidden

static FILE* m_hLogFile;
static std::string m_strFilePath;
static LOG_LEVEL m_enLogLevel;
};

类实现文件

FILE* Logger::m_hLogFile = NULL;
LOG_LEVEL Logger::m_enLogLevel = LOG_TRACE;
std::string Logger::m_strFilePath = "\\Storage Card\\DebugInfo.log";

TCHAR * LogLevelStr[]=
{
TEXT("TRACE"),
TEXT("INFO"),
TEXT("WARN"),
TEXT("ERROR"),
TEXT("FATAL"),
};

Logger& Logger::Instance()
{
static Logger oLogger;
return oLogger;
}

void Logger::SetLogFilePath( const std::string& strFilePath)
{
m_strFilePath = strFilePath;
Dispose();
Initialise();
}

void Logger::SetLogLevel( const LOG_LEVEL enLogLevel)
{
m_enLogLevel = enLogLevel;
}


Logger::Logger()
{
Initialise();
}

//never use
Logger::~Logger()
{
Dispose();
}

void Logger::Initialise()
{
if( m_strFilePath.length() > 0 )
{
m_hLogFile = fopen(m_strFilePath.c_str(), "a+");
}
}

void Logger::Dispose()
{
if( NULL != m_hLogFile )
{
fflush( m_hLogFile );
fclose( m_hLogFile );
m_hLogFile = NULL;
}
}

void Logger::Log( LOG_LEVEL enLogLevel ,const TCHAR *format, ... )
{
if( m_enLogLevel > enLogLevel)
{
return;
}
#ifndef DEBUG
if ( NULL == m_hLogFile )
{
return;
}
#endif

TCHAR szBuffer[1024];

va_list args;
va_start(args, format);
vswprintf(szBuffer, format, args);
va_end(args);

#ifdef DEBUG
wprintf(_T("%S THR:%8.8x %s\t%s\n"), GetCurrentTime(), GetCurrentThread(), LogLevelStr[enLogLevel], szBuffer);
#else
//combine time stamp, thread number and log level together.
if( 0 > fwprintf(m_hLogFile, _T("%S THR:%8.8x %s\t%s\n"), GetCurrentTime(), GetCurrentThreadId(), LogLevelStr[enLogLevel], szBuffer) )
{
Dispose();
}
else
{
fflush(m_hLogFile);
}
#endif

}

 

Singleton模式

这个Logger类使用Singleton模式来实现,不知道什么时候开始博客园已经不再流行设计模式了,一方面说明设计模式不再是阳春白雪,已经深入人间。另一方面又兴起了反模式热潮。在反模式的风潮中,Singleton是给人批评最多的模式,Singleton有点像变相的全局变量,破坏了封装,混乱了各个类的依赖关系。

我还是那句话,模式本身没有错,看用的人是否把特定的模式用在特定的场景下。Singleton我还是会用到,如果某个资源类有且只有一份,我就使用Singleton。没有必要产生多个对象,而且多个对象访问独占资源会有同步问题。在Logger类,我还是使用Singleton,因为我只写一个文件。

Singleton的具体实现一般关心三个问题: 1. 有且只有一个对象实例化。 2.多线程的控制。其实第二个问题也是为了保证第一个问题。3. 按需实例化。

private:
/* more (non-static) functions here */
Logger(); // ctor hidden
Logger(Logger const&); // copy ctor hidden
Logger& operator=(Logger const&); // assign op. hidden
~Logger(); // dtor hidden

上面的代码用于保证只有一个对象的实例化,很多做C#的开发人员会忽略上面的代码,因为C#没有深拷贝的概念,也没有运算符重载的概念。

 

Logger& Logger::Instance() 
{
static Logger oLogger;
return oLogger;
}

上面的代码保证线程安全以及按需实例化。我觉得这个实现模式很好,同时满足三个愿望。

 

日志分级管理

打印日志的时候,分级管理很重要,不同时期需要显示不同级别的日志,开发时期,可能需要Trace级别的日志,到了运行时可能只需要Error以上级别的日志了,日志分级管理能均衡时间与空间的合理利用。

通过级别管理,打印级别高于需要显示级别的日志。

if( m_enLogLevel > enLogLevel)
{
return;
}

 

在打印过程中,显示级别,我在找问题的时候都是从高级往低级找。

if( 0 > fwprintf(m_hLogFile, _T("%S THR:%8.8x %s\t%s\n"), GetCurrentTime(), GetCurrentThreadId(), LogLevelStr[enLogLevel], szBuffer) )
{
Dispose();
}

由于这是在Windows Embedded CE和Windows Mobile平台下的实现,所以都是有Unicode的API。下面是打印的日志,包含了级别。

2010-02-24T09:17:38 THR:de428d7e TRACE    FILE=[.\iToday.cpp], LINE=[44]
2010-02-24T09:17:39 THR:de428d7e INFO FILE=[.\iToday.cpp], LINE=[47]
2010-02-24T09:17:39 THR:de428d7e WARN FILE=[.\iToday.cpp], LINE=[50]
2010-02-24T09:17:40 THR:de428d7e ERROR FILE=[.\iToday.cpp], LINE=[53]
2010-02-24T09:17:40 THR:de428d7e FATAL FILE=[.\iToday.cpp], LINE=[56]
2010-02-24T09:17:41 THR:de428d7e WARN FILE=[.\iToday.cpp], LINE=[67]
2010-02-24T09:17:42 THR:de428d7e ERROR FILE=[.\iToday.cpp], LINE=[70]
2010-02-24T09:17:42 THR:de428d7e FATAL FILE=[.\iToday.cpp], LINE=[73]


时间与线程号

在多线程环境下,打印时间和线程十分重要,这样能查线程同步问题。时间和线程号见上面的日志。

 

使用Logger

void LoggerTest()
{
Logger::Instance().Log(LOG_TRACE, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);
Sleep(500);

Logger::Instance().Log(LOG_INFO, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);
Sleep(500);

Logger::Instance().Log(LOG_WARNING, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);
Sleep(500);

Logger::Instance().Log(LOG_ERROR, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);
Sleep(500);

Logger::Instance().Log(LOG_FATAL, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);
Sleep(500);

Logger::SetLogLevel(LOG_WARNING);

Logger::Instance().Log(LOG_TRACE, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);
Sleep(500);

Logger::Instance().Log(LOG_INFO, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);
Sleep(500);

Logger::Instance().Log(LOG_WARNING, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);
Sleep(500);

Logger::Instance().Log(LOG_ERROR, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);
Sleep(500);

Logger::Instance().Log(LOG_FATAL, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);
Sleep(500);

Logger::SetLogLevel(LOG_INFO);
Logger::SetLogFilePath("\\Storage Card\\DebugInfo2.log");

Logger::Instance().Log(LOG_TRACE, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);
Sleep(500);

Logger::Instance().Log(LOG_INFO, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);
Sleep(500);

Logger::Instance().Log(LOG_WARNING, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);
Sleep(500);

Logger::Instance().Log(LOG_ERROR, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);
Sleep(500);

Logger::Instance().Log(LOG_FATAL, _T("FILE=[%S], LINE=[%d]"), __FILE__, __LINE__);
Sleep(500);

}

使用Logger类很简单,直接调用Log()函数就可以了,可以参考printf的模式来使用,也就是C#的String.Format()的模式。

 

这是我封装的Logger类,欢迎拍板,这样可以让我不断改进这个类的实现。 





    本文转自Jake Lin博客园博客,原文链接:http://www.cnblogs.com/procoder/archive/2010/02/24/Windows-Mobile-Logger.html,如需转载请自行联系原作者

相关实践学习
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
相关文章
|
1月前
|
Ubuntu API C++
C++标准库、Windows API及Ubuntu API的综合应用
总之,C++标准库、Windows API和Ubuntu API的综合应用是一项挑战性较大的任务,需要开发者具备跨平台编程的深入知识和丰富经验。通过合理的架构设计和有效的工具选择,可以在不同的操作系统平台上高效地开发和部署应用程序。
100 11
|
7月前
|
存储 算法 C++
Windows共享文件:探秘C++实现的B树索引算法奇境
在数字化时代,Windows共享文件的高效管理至关重要。B树算法以其自平衡多路搜索特性,在文件索引与存储优化中表现出色。本文探讨B树在Windows共享文件中的应用,通过C++实现具体代码,展示其构建文件索引、优化数据存储的能力,提升文件检索效率。B树通过减少磁盘I/O操作,确保查询高效,为企业和个人提供流畅的文件共享体验。
|
8月前
|
数据库连接 测试技术 Windows
【YashanDB知识库】windows配置ODBC跟踪日志, 使用日志定位问题
【YashanDB知识库】windows配置ODBC跟踪日志, 使用日志定位问题
|
11月前
|
缓存 安全 网络协议
使用事件日志识别常见 Windows 错误
事件查看器是Windows操作系统中的标准诊断工具,用于记录系统事件,包括硬件问题、软件中断和系统行为等详细信息。通过分析这些日志,管理员能够追踪和解决系统错误。访问方法包括使用快捷键Win + R输入eventvwr.msc,或通过控制面板进入。事件查看器中的每条记录包含事件ID、来源和描述,帮助识别和解决问题。常见错误如蓝屏死机、DLL错误、驱动程序错误等,可通过更新驱动程序、运行系统诊断、使用恢复功能等方式解决。
811 4
|
监控 安全 网络安全
使用EventLog Analyzer日志分析工具监测 Windows Server 安全威胁
Windows服务器面临多重威胁,包括勒索软件、DoS攻击、内部威胁、恶意软件感染、网络钓鱼、暴力破解、漏洞利用、Web应用攻击及配置错误等。这些威胁严重威胁服务器安全与业务连续性。EventLog Analyzer通过日志管理和威胁分析,有效检测并应对上述威胁,提升服务器安全性,确保服务稳定运行。
383 2
|
缓存 NoSQL Linux
【Azure Redis 缓存】Windows和Linux系统本地安装Redis, 加载dump.rdb中数据以及通过AOF日志文件追加数据
【Azure Redis 缓存】Windows和Linux系统本地安装Redis, 加载dump.rdb中数据以及通过AOF日志文件追加数据
320 1
【Azure Redis 缓存】Windows和Linux系统本地安装Redis, 加载dump.rdb中数据以及通过AOF日志文件追加数据
|
XML 数据格式 Windows
【Azure 云服务】Azure Cloud Service (Extended Support) 云服务开启诊断日志插件 WAD Extension (Windows Azure Diagnostic) 无法正常工作的原因
【Azure 云服务】Azure Cloud Service (Extended Support) 云服务开启诊断日志插件 WAD Extension (Windows Azure Diagnostic) 无法正常工作的原因
140 0
|
开发框架 JavaScript .NET
【Azure 应用服务】Azure Mobile App (NodeJS) 的服务端部署在App Service for Windows中出现404 Not Found
【Azure 应用服务】Azure Mobile App (NodeJS) 的服务端部署在App Service for Windows中出现404 Not Found
191 0
|
安全 C++ Windows
Windows下C++使用gRPC(Qt和VS,含文件包和使用方法)
Windows下C++使用gRPC(Qt和VS,含文件包和使用方法)
|
安全 API C++
逆向学习Windows篇:C++中多线程的使用和回调函数的实现
逆向学习Windows篇:C++中多线程的使用和回调函数的实现
634 0

热门文章

最新文章