文档章节

简单好用的C++日志组件代码

g
 gzstyxb
发布于 2013/11/19 17:36
字数 1283
阅读 615
收藏 11
点赞 0
评论 0

1、特点:

  • 无需打开、关闭日志文件操作,直接存储。
  • 同时支持UNICODE、多字节类型以及二进制格式存储。
  • 文件大小超过限制,自动备份;备份文件数超过限制,自动删除。
  • 超过3秒没日志写入,文件缓存自动写盘。确保日志尽量不因其它原因导致丢失。

2、代码:

头文件代码如下:

#pragma once
#ifndef _STRSTREAM_
#include <sstream>
#endif

#define WRITE_SIMPLE_LOGGER

namespace SIMPLE_LOGGER
{

#ifdef WRITE_SIMPLE_LOGGER

#define LOGGER_WRITE_W(Value) { \
	std::wostringstream logstream; logstream << Value; std::wstring logstring = logstream.str(); \
	SIMPLE_LOGGER::S_WriteW(0, logstring.c_str(), __FILE__, __LINE__); }

#define LOGGER_WRITE_W2(Value) { \
	std::wostringstream logstream; logstream << Value; std::wstring logstring = logstream.str(); \
	SIMPLE_LOGGER::S_WriteW(0, logstring.c_str(), __FILE__, __LINE__, TRUE); }

#define LOGGER_WRITE_A(Value) { \
	std::ostringstream logstream; logstream << Value; std::string logstring = logstream.str(); \
	SIMPLE_LOGGER::S_WriteA(0, logstring.c_str(), __FILE__, __LINE__); }

#define LOGGER_WRITE_A2(Value) { \
	std::ostringstream logstream; logstream << Value; std::string logstring = logstream.str(); \
	SIMPLE_LOGGER::S_WriteA(0, logstring.c_str(), __FILE__, __LINE__, TRUE); }

#define LOGGER_WRITE_MEM(Value, Size) { \
	SIMPLE_LOGGER::S_WriteB(0, Value, Size, __FILE__, __LINE__); }
#else

#define LOGGER_WRITE_W(Value)					NULL;
#define LOGGER_WRITE_W2(Value)					NULL;
#define LOGGER_WRITE_A(Value)					NULL;
#define LOGGER_WRITE_A2(Value)					NULL;
#define LOGGER_WRITE_MEM(Value, Size)			NULL;

#endif

	void S_WriteA(int Index, const CHAR *Value, const CHAR* File, int Line, BOOL Flush = FALSE);
	void S_WriteW(int Index, const WCHAR *Value, const CHAR* File, int Line, BOOL Flush = FALSE);
	void S_WriteB(int Index, const BYTE *Value, int Size, const CHAR* File, int Line, BOOL Flush = FALSE);
};

实现文件如下:

#include "stdafx.h"
#include "SLogger.h"

#include <STDIO.H>
#include <SHARE.H>
#include <WTYPES.H>
#include <afxmt.h>

#define SL_MAX_LOG_LENGTH           800							// 日志记录最大存储字符数
#define SL_LAST_LOG_LENGTH          100							// 日志超长最大尾部存储字符
#define SL_MAX_FILE_SIZE            (1024 * 1024 * 5)			// 日志文件大小
#define SL_MAX_BACKUP_COUNT         2							// 最大备份几个日志文件(包括当前正在存储文件)
#define SL_FILEWRITER_COUNT         10							// 最大有十个日志对象存在

namespace SIMPLE_LOGGER {

	CCriticalSection m_oCrtSection;
	HANDLE m_hTimerHandle = NULL, m_hTimerQueue = NULL;
	class CFileWriter *m_oFileWriter[SL_FILEWRITER_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
	int m_iWriteCount[SL_FILEWRITER_COUNT] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
	VOID CALLBACK TimerAPCProc(PVOID lpParameter, BOOLEAN TimerOrWaitFired);

	void CreateTimer()
	{
		m_oCrtSection.Lock();
		if (NULL == m_hTimerQueue) {
			if (NULL != (m_hTimerQueue = CreateTimerQueue())) {
				CreateTimerQueueTimer(&m_hTimerHandle, m_hTimerQueue, (WAITORTIMERCALLBACK)TimerAPCProc, 
					NULL, 5000, 800, 0); // 800 *3 毫秒没有日志记录, 文件缓存写入磁盘。
			}
		}
		m_oCrtSection.Unlock();
	}

	void DeleteTimer()
	{
		m_oCrtSection.Lock();
		if (NULL != m_hTimerQueue) {
			DeleteTimerQueueTimer(m_hTimerQueue, m_hTimerHandle, INVALID_HANDLE_VALUE);
			DeleteTimerQueue(m_hTimerQueue);
			m_hTimerHandle = m_hTimerQueue = NULL;
		}
		m_oCrtSection.Unlock();
	}

	class CFileWriter
	{
	public:
		CFileWriter(int Index) : FileSize(0), hFileHandle(NULL), BkFileCount(SL_MAX_BACKUP_COUNT)
		{
#pragma warning(push)
#pragma warning(disable:4996)
			InitializeCriticalSection(&CritSection);
			WCHAR PathBuffer[MAX_PATH * 2] = { 0 };
			::GetModuleFileNameW(GetModuleHandle(NULL), PathBuffer, MAX_PATH);
			WCHAR *Temp = wcsrchr(PathBuffer, L'.');
			if (0 == Index) wmemcpy(Temp, L"_Log.txt", 8);
			else { wmemcpy(Temp, L"__Log.txt", 9); Temp[1] = L'0' + Index; }			
			wmemcpy(FullPathName, PathBuffer, MAX_PATH);
			Temp = wcsrchr(PathBuffer, L'\\');
			wcscpy(FileName, Temp + 1); Temp[1] = 0;
			wcscpy(FolderName, PathBuffer);
			do { swprintf_s(PathBuffer, L"%s%d_%s", FolderName, BkFileCount - 1, FileName); }
			while(!FileExisted(PathBuffer) && --BkFileCount > 0);
			Open();
#pragma warning(pop)
			m_oFileWriter[Index] = this;
			CreateTimer();
		}

		~CFileWriter(void) 
		{
			DeleteTimer();
			DeleteCriticalSection(&CritSection);
			Close();
		}

		bool FileExisted(WCHAR *FileName) 
		{
			DWORD dwAttricutes = ::GetFileAttributesW(FileName);
			return dwAttricutes != DWORD(-1) && ((dwAttricutes & FILE_ATTRIBUTE_DIRECTORY) == 0) ;
		}

		void Open()
		{
			if (NULL == hFileHandle) {
				if (NULL != (hFileHandle = _wfsopen(FullPathName, L"ab", _SH_DENYNO))) {
					fseek(hFileHandle, 0, SEEK_END);
					FileSize = ftell(hFileHandle);
				}
			}
		}

		void Close()
		{
			if (NULL != hFileHandle) {
				fclose(hFileHandle);
				hFileHandle = NULL;
			}			
		}

		void Write(const char *Value, const char* File, int Line, BOOL Flush)
		{
			if (SL_MAX_FILE_SIZE < FileSize) FileBackup();
			if (NULL != hFileHandle) {
				SYSTEMTIME Time;
				GetLocalTime(&Time);
				FileSize += fprintf_s(hFileHandle,
					"%04d/%02d/%02d %02d:%02d:%02d %03d %s [%s %d]\r\n", 
					Time.wYear, Time.wMonth, Time.wDay, 
					Time.wHour, Time.wMinute, Time.wSecond, Time.wMilliseconds,
					Value, File, Line);
				if (Flush) fflush(hFileHandle);
			}
		}

		void FileBackup()
		{
			WCHAR BkFileName[MAX_PATH];
			if (SL_MAX_BACKUP_COUNT == BkFileCount) {
				WCHAR BkFileName2[MAX_PATH];
				swprintf_s(BkFileName2, L"%s%d_%s", FolderName, 0, FileName);
				DeleteFileW(BkFileName2);
				for (int i = 1; i < SL_MAX_BACKUP_COUNT; i++) {
					swprintf_s(BkFileName,  L"%s%d_%s", FolderName, i - 1, FileName);
					swprintf_s(BkFileName2, L"%s%d_%s", FolderName, i, FileName);
					_wrename(BkFileName2, BkFileName);
				}
				BkFileCount--;
			}
			Close();
			swprintf_s(BkFileName, L"%s%d_%s", FolderName, BkFileCount++, FileName);
			_wrename(FullPathName, BkFileName);
			Open();
		}

		LONG FileSize;
		UINT BkFileCount;
		FILE* hFileHandle;
		WCHAR FolderName[MAX_PATH];
		WCHAR FullPathName[MAX_PATH];
		WCHAR FileName[MAX_PATH];
		CRITICAL_SECTION CritSection;
	};

	VOID CALLBACK TimerAPCProc(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
	{
		static int WriteCount[SL_FILEWRITER_COUNT] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
		static int CheckCount[SL_FILEWRITER_COUNT] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
		for (int i = SL_FILEWRITER_COUNT - 1; i >= 0; i--) {
			if (NULL != m_oFileWriter[i] && 0 != m_iWriteCount[i]) {
				if (WriteCount[i] != m_iWriteCount[i]) {
					WriteCount[i] = m_iWriteCount[i];
					CheckCount[i] = 0;
				}
				else if (3 == ++CheckCount[i]) {
					EnterCriticalSection(&m_oFileWriter[i]->CritSection);
					fflush(m_oFileWriter[i]->hFileHandle);
					WriteCount[i] = m_iWriteCount[i] = CheckCount[i] = 0;
					LeaveCriticalSection(&m_oFileWriter[i]->CritSection);
				}
			}
		}
	}

	void WriteToInternal(int Index, const CHAR *Value, const CHAR* File, int Line, BOOL Flush)
	{
#define CREATE_LOG_OBJECT(type) {  static CFileWriter LogWriter(type); Writer = &LogWriter; }
		CFileWriter *Writer = NULL;
		switch (Index) {
		case 0: CREATE_LOG_OBJECT(Index); break;
		case 1:	CREATE_LOG_OBJECT(Index); break;
		case 2:	CREATE_LOG_OBJECT(Index); break;
		case 3:	CREATE_LOG_OBJECT(Index); break;
		case 4:	CREATE_LOG_OBJECT(Index); break;
		case 5:	CREATE_LOG_OBJECT(Index); break;
		case 6:	CREATE_LOG_OBJECT(Index); break;
		case 7:	CREATE_LOG_OBJECT(Index); break;
		case 8:	CREATE_LOG_OBJECT(Index); break;
		case 9:	CREATE_LOG_OBJECT(Index); break;
		default: ASSERT(FALSE);           return;
		}
		::EnterCriticalSection(&Writer->CritSection);
		try { 
			Writer->Write(Value, File, Line, Flush); 
		}
		catch (...) {}
		if (Flush) m_iWriteCount[Index] = 0;
		else m_iWriteCount[Index]++;
		::LeaveCriticalSection(&Writer->CritSection);
	}

	bool Format(const BYTE *Value, UINT Length, BYTE *HexBuff, UINT BuffLen)
	{
#define HextoAsc0(Value, Result) { if (Value < 0x0a) Result = Value + 0x30; else Result = Value + 0x37; }
#define HextoAsc1(Value, Result) { BYTE Temp = (Value >> 4) & 0x0f; HextoAsc0(Temp, Result[0]); Temp = Value & 0x0f; HextoAsc0(Temp, Result[1]); }
#define HextoAsc2 { HextoAsc1(Value[Read], (HexBuff + Write)); Write += 2; HexBuff[Write++] = ' '; }

		UINT Write = 0, Read = 0;
		const UINT LineCount = 75, HexCount = 8;
		while (Read < Length) {
			UINT Count = Read + HexCount;
			if (Count > Length) Count = Length;
			if (INT(BuffLen - Write - LineCount) <= 0)
				break;

			HexBuff[Write++] = '\r'; HexBuff[Write++] = '\n';
			Write += sprintf_s((char*)HexBuff + Write, 9, "0x%04X  ", Read);
			UINT Begin = Write, Begin2 = Read;
			for (Read; Read < Count; Read++)
				HextoAsc2(Value[Read], (HexBuff + Write));

			HexBuff[Write++] = ' ';
			Count = Read + HexCount;
			if (Count > Length) Count = Length;
			for (Read; Read < Count; Read++)
				HextoAsc2(Value[Read], (HexBuff + Write));

			UINT Fill = Begin + HexCount * 6 + 2 - Write;
			for (UINT j = 0; j < Fill; j++)
				HexBuff[Write++] = ' ';

			for (Begin2; Begin2 < Read; Begin2++) {
				BYTE Data = Value[Begin2];
				if (Data >= 32 && Data <= 126) HexBuff[Write++] = Data;
				else HexBuff[Write++] = '.';
			}
		}

		return true;
	} 

	void S_WriteA(int Index, const CHAR *Value, const CHAR* File, int Line, BOOL Flush)
	{
		if (NULL != Value) {
			size_t StrLen = strlen(Value);
			if (SL_MAX_LOG_LENGTH <= StrLen) {
				char Buffer[SL_MAX_LOG_LENGTH] = { 0 };
				int Second = SL_LAST_LOG_LENGTH, First = SL_MAX_LOG_LENGTH  - Second - 6;
				memcpy(Buffer, Value, First);
				Buffer[First] = ' '; Buffer[First + 1] = '.'; Buffer[First + 2] = '.'; 
				Buffer[First + 3] = '.'; Buffer[First + 4] = ' ';
				memcpy(Buffer + First + 5, Value + (StrLen - Second), Second);
				WriteToInternal(Index, Buffer, File, Line, Flush);
			}
			else
				WriteToInternal(Index, Value, File, Line, Flush);
		  }
	}

	void S_WriteW(int Index, const WCHAR *Value, const CHAR* File, int Line, BOOL Flush)
	{
		if (NULL != Value) {
			char Buffer[SL_MAX_LOG_LENGTH] = { 0 };
			if (0 == WideCharToMultiByte(CP_ACP, 0, Value, -1, Buffer, SL_MAX_LOG_LENGTH - 1, NULL, NULL)) {
				char *Ellipsis = Buffer + (SL_MAX_LOG_LENGTH - 4);
				Ellipsis[0] = '.'; Ellipsis[1] = '.'; Ellipsis[2] = '.';
			}
			WriteToInternal(Index, Buffer, File, Line, Flush);
		}
	}

	void S_WriteB(int Index, const BYTE *Value, int Size, const CHAR* File, int Line, BOOL Flush)
	{
		if (NULL != Value) {
			char Buffer[SL_MAX_LOG_LENGTH] = { 0 };
			Format(Value, Size, (BYTE*)Buffer, SL_MAX_LOG_LENGTH - 1);
			S_WriteA(Index, Buffer, File, Line, Flush);
		}
	}
};

 

© 著作权归作者所有

共有 人打赏支持
g
粉丝 1
博文 1
码字总数 1283
作品 0
广州
C语言/C++编程新手学习常见问题

C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

小辰带你看世界 ⋅ 05/11 ⋅ 0

什么是 C 和 C ++ 标准库?

简要介绍编写C/C ++应用程序的领域,标准库的作用以及它是如何在各种操作系统中实现的。 我已经接触C++一段时间了,一开始就让我感到疑惑的是其内部结构:我所使用的内核函数和类从何而来? ...

oschina ⋅ 04/10 ⋅ 0

c语言编程软件有哪些 Win7下用哪种C语言编译器

C语言是一门历史很长的编程语言,其编译器和开发工具也多种多样,其开发工具包括编译器,现举几个开发工具供大家选择,当然也要根据自己的操作系统来选择适合自己的开发工具 好多刚开始接触c...

mini92 ⋅ 04/20 ⋅ 0

C语言/C++编程学习:程序结构:构思

C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

小辰带你看世界 ⋅ 05/11 ⋅ 0

WINDOWS.H already included. MFC apps must not #i

场景 在win32工程中,使用MFC,选择MFC的使用方式:在共享 DLL 中使用 MFC,在调用CString的时候,出现如下的错误: c:program files (x86)microsoft visual studio 14.0vcatlmfcincludeafxv...

fengyuzaitu ⋅ 04/13 ⋅ 0

C++雾中风景8:Lambda表达式

上一篇C++的博客是Long Long ago了,前文讲到在看Lambda表达式的内容。笔者首次接触Lambda表达式应该是学习Python语言的时候,当时也不太明白这种表达方式的精髓,后续接触了Scala与Java8的链...

LeeHappen ⋅ 04/30 ⋅ 0

C语言编程学习—宏定义的一些使用技巧

C语言是面向过程的,而C++是面向对象的 C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到...

小辰带你看世界 ⋅ 03/24 ⋅ 0

BOOST.ASIO源码剖析(一)

前言 源码之前,了无秘密。 ——侯捷 Boost库是一个可移植、提供源代码的C++库,作为标准库的后备,是C++标准化进程的开发引擎之一。Boost库由C++标准委员会库工作组成员发起,其中有些内容有...

moki_oschina ⋅ 04/11 ⋅ 0

Qt:Qt一种开跨平台C++图形用户界面应用程序开发框架简介、安装、使用方法详细攻略——Jason niu

Qt简介、安装、使用方法 Qt 是一个跨平台的 C++图形用户界面库,由挪威 TrollTech 公司于1995年底出品。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。Qt是面向对象...

qq_41185868 ⋅ 04/20 ⋅ 0

SWIG与JAVA 交互最全开发指南一

项目背景 最近开始研究做移动端项目,但是本人基本是做了五六年的c++的底层研发,对C++的研发可以说是驾轻就熟了,但是对于android还是属于刚入门阶段,虽然断断续续做移动端也做了一年,但是...

揽月凡尘 ⋅ 前天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Spring发展历程总结

转自与 https://www.cnblogs.com/RunForLove/p/4641672.html 目前很多公司的架构,从Struts2迁移到了SpringMVC。你有想过为什么不使用Servlet+JSP来构建Java web项目,而是采用SpringMVC呢?...

onedotdot ⋅ 24分钟前 ⋅ 0

Python模块/包/库安装(6种方法)

Python模块/包/库安装(6种方法) 冰颖机器人 2016-11-29 21:33:26 一、方法1: 单文件模块 直接把文件拷贝到 $python_dir/Lib 二、方法2: 多文件模块,带setup.py 下载模块包(压缩文件zip...

cswangyx ⋅ 43分钟前 ⋅ 0

零基础学习大数据人工智能,学习路线篇!系统规划大数据之路?

大数据处理技术怎么学习呢?首先我们要学习Python语言和Linux操作系统,这两个是学习大数据的基础,学习的顺序不分前后。 Python:Python 的排名从去年开始就借助人工智能持续上升,现在它已经...

董黎明 ⋅ 51分钟前 ⋅ 0

openJdk和sun jdk的区别

使用过LINUX的人都应该知道,在大多数LINUX发行版本里,内置或者通过软件源安装JDK的话,都是安装的OpenJDK, 那么到底什么是OpenJDK,它与SUN JDK有什么关系和区别呢? 历史上的原因是,Ope...

jason_kiss ⋅ 今天 ⋅ 0

梳理

Redux 是 JavaScript 状态容器,提供可预测化的状态管理。 它是JS的状态容器,是一种解决问题的方式,所以即可以用于 react 也可以用于 vue。 需要理解其思想及实现方式。 应用中所有的 stat...

分秒 ⋅ 今天 ⋅ 0

Java 后台判断是否为ajax请求

/** * 是否是Ajax请求 * @param request * @return */public static boolean isAjax(ServletRequest request){return "XMLHttpRequest".equalsIgnoreCase(((HttpServletReques......

JavaSon712 ⋅ 今天 ⋅ 0

Redis 单线程 为何却需要事务处理并发问题

Redis是单线程处理,也就是命令会顺序执行。那么为什么会存在并发问题呢? 个人理解是,虽然redis是单线程,但是可以同时有多个客户端访问,每个客户端会有 一个线程。客户端访问之间存在竞争...

码代码的小司机 ⋅ 今天 ⋅ 0

到底会改名吗?微软GVFS 改名之争

微软去年透露了 Git Virtual File System(GVFS)项目,GVFS 是 Git 版本控制系统的一个开源插件,允许 Git 处理 TB 规模的代码库,比如 270 GB 的 Windows 代码库。该项目公布之初就引发了争...

linux-tao ⋅ 今天 ⋅ 0

笔试题之Java基础部分【简】【二】

1.静态变量和实例变量的区别 在语法定义上的区别:静态变量前要加static关键字,而实例变量前则不加。在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变...

anlve ⋅ 今天 ⋅ 0

Lombok简单介绍及使用

官网 通过简单注解来精简代码达到消除冗长代码的目的 优点 提高编程效率 使代码更简洁 消除冗长代码 避免修改字段名字时忘记修改方法名 4.idea中安装lombnok pom.xml引入 <dependency> <grou...

to_ln ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部