C++封装的高性能异步日志,cout实现方式
来源:互联网 发布:我的少女时代 知乎 编辑:程序博客网 时间:2024/06/06 07:13
- 异步写入日志———-基于windows对文件投递“重叠IO”操作实现的 异步写入。
- 多线程安全————-对每个“主调线程”创建一个“字符串流对象”,所以多线程并行操作互不影响。
- 无线程同步的开销—–因为每个线程都有自己的“流对象”所以不需要线程同步所造成的CPU时钟周期浪费。
- 支持UNICODE
- 支持ANSI
- 支持C/C++常用类型、对象直接写入
- 操作简单就像使用C++ std::cout
使用方法如下:
//包含头文件 #include "_MyLog.h" //使用名称空间 using namespace _Log; //定义对象 _MyLog LogOut; //打开文件 LogOut.Open(); //异步输出到日志 LogOut << time << table << id << TEXT("这是一条日志消息") << endl; //关闭日志文件 LogOut.Close();
这是输出效果
2016-06-25 14:58:56 Test Output:00000000000
2016-06-25 14:58:56 1C6C 这是一条日志消息
就是这么简单,由于异步写入以及无同步的开销所以具有相当良好的性能。
这个类我已经用在了我的服务器上。
哈哈,是不是心动啦,是不是在看那儿有下载连接,好了不废话先上代码,技术上的部分基本上都在注释里了。
文件:_MyLog.h
/*包含头文件...略*//继承重叠机构的IO结构,用于投递异步写入重叠操作typedef struct _LogIO : public OVERLAPPED{ string m_str; //构造函数初始化OVERLAPPED结构 _LogIO(){ memset( this, 0, sizeof(OVERLAPPED) ); }}*P_LogIO;//定义一些类型别名 管理不同字符集的对象版本//UNICODE(宽字节字符集)、ANSI(多字节字符集)//如stringstream C++ 字符串流等对象//定义了UNICODE表示当前编译环境是UNICODE#ifdef UNICODE#define degug_out wcouttypedef wstringstream _stream;typedef wstringstream* p_stream;#else#define degug_out couttypedef stringstream _stream;typedef stringstream* p_stream;#endif//C++映射(map)对象,unsigned long 用于线程的ID 类型为DWORDtypedef map<unsigned long , p_stream > ThreadMap;//定义在名称空间中,为了避免与其他函数混淆 如:time、endlnamespace _Log{ class _MyLog { private: //语言环境名 CString m_LocaleName; //日期格式 CString m_DateFormat; //日志文件句柄 HANDLE m_LogFile; //模块路径 CString m_ModulePath; //日志文件路径 CString m_LogFilePath; //文件下一次的偏移量 LARGE_INTEGER m_liFileNextOffset; //完成端口类实例 CIOCP m_ciop; ThreadMap m_ThreadMap; public: _MyLog(void); ~_MyLog(void); void Init( CString ModulePath, CString LocaleName, CString DateFormat ); void Open( void ); void Close( void ); template<typename T> _MyLog & operator << ( T info ); _MyLog & operator << ( _MyLog &(__cdecl * pfn)( _MyLog & ) ); //定义友元函数 friend _MyLog & time( _MyLog & Log ); friend _MyLog & id( _MyLog & Log ); friend _MyLog & table( _MyLog & Log ); friend _MyLog & space( _MyLog & Log ); friend _MyLog & endl( _MyLog & Log ); private: bool IsValid(); template<typename T> bool WriteLog( T info ); bool CompleteHandle(); p_stream GetThreadStream(); _MyLog & _endl(); }; //引用在CPP文件中定义的全局对象,引用该对象需要_Log::OutLog extern _MyLog OutLog;}
文件:_MyLog.cpp
namespace _Log{ //全局对象output log _MyLog OutLog; _MyLog::_MyLog(void) { TCHAR pTempBuff[MAX_PATH]; //获得当前模块的绝对路径 GetModuleFileName( NULL, (PTCHAR)pTempBuff, MAX_PATH); m_ModulePath.Format( TEXT("%s"), pTempBuff ); //去除文件名只留下路径 int nIndex = m_ModulePath.ReverseFind( TEXT('\\') ); if ( nIndex != -1 ) { m_ModulePath.Format( TEXT("%s"), m_ModulePath.Left( nIndex + 1 ) ); } //日志文件名 m_LogFilePath = m_ModulePath + TEXT("ServiceLog.log"); //日期格式 m_DateFormat = TEXT("%Y-%m-%d %H:%M:%S"); //初始化语言环境为 中文 m_LocaleName = TEXT("chinese"); //文件句柄初始为 无效句柄值 m_LogFile = INVALID_HANDLE_VALUE; //下一次文件偏移量 m_liFileNextOffset.QuadPart = 0; //创建完成端口,设置最大并行数量为1 m_ciop.Create(1); } _MyLog::~_MyLog(void) { } void _MyLog::Init( CString LogFilePath, CString LocaleName, CString DateFormat ) { m_LogFilePath = LogFilePath; m_LocaleName = LocaleName; m_DateFormat = DateFormat; } void _MyLog::Open( void ) { m_LogFile = CreateFile( m_LogFilePath, GENERIC_WRITE, FILE_SHARE_READ, //共享读取 NULL, OPEN_ALWAYS, //打开文件若不存在则创建 FILE_FLAG_OVERLAPPED, //使用重叠IO 0); //将文件关联到完成端口 m_ciop.AssociateDevice(m_LogFile, 0 ); //获得文件的大小 GetFileSizeEx( m_LogFile, &m_liFileNextOffset ); //千万要加入这一句否则将导致无法输出带中文的字符串 //调试了几个小时 void* pBuff;#ifdef UNICODE pBuff = _UnicodeToANSI( m_LocaleName.GetBuffer() );#else pBuff = m_LocaleName.GetBuffer();#endif //语言本地化 degug_out.imbue( std::locale( (char*)pBuff ) );#ifdef UNICODE if(pBuff){free(pBuff); pBuff = NULL;}#endif //可以说是测试输出,也可以说是让编译器生成 泛型模版与调用参数对应的方法, //否则在外部文件调用可能会出现连接错误 *this << time << table << TEXT("Test Output:") << (int)0 << (short)0 << (long)0 << (unsigned int)0 << (unsigned short)0 << (unsigned long)0 << (float)0 << (double)0 #ifdef UNICODE << (wchar_t*)L"0" << (const wchar_t*)L"0" << (wchar_t)0x30#else << (char*)"0" << (const char*)"0" << (char)0x30 #endif << endl; } //关闭并释放相关资源 void _MyLog::Close( void ) { //关闭文件释放资源,这样会导致完成端口中所有未决的 //操作全部返回 if( m_LogFile != INVALID_HANDLE_VALUE ) { CloseHandle(m_LogFile); m_LogFile = INVALID_HANDLE_VALUE; } //关闭完成端口,这样做同上面一样,完成端口返回所有操作 m_ciop.Close(); //调用完成处理释放资源 CompleteHandle(); //释放所有资源 ThreadMap::iterator itera = m_ThreadMap.begin(); for( ; itera != m_ThreadMap.end(); ) { //释放创建的流 if( itera->second ){ delete itera->second; itera->second = NULL; } //删除映射中的对象 itera = m_ThreadMap.erase( itera ); } } //获得与线程相关联的流 p_stream _MyLog::GetThreadStream() { p_stream p_str = NULL; unsigned long id = GetCurrentThreadId(); ThreadMap::iterator itera = m_ThreadMap.find( id ); if ( itera == m_ThreadMap.end() ) { //该线程id无记录,创建一个流插入 p_str = new _stream; m_ThreadMap.insert( ThreadMap::value_type( id, p_str ) ); }else //查找到,返回相对应的流 { p_str = itera->second; } return p_str; } //写入日志 template<typename T> bool _MyLog::WriteLog( T info ) { //类型T必须支持<<操作符 if( IsValid() ) { p_stream p_str = GetThreadStream(); //向流中写入信息 *p_str << info; return true; } return false; } //完成处理、释放资源 bool _MyLog::CompleteHandle() { DWORD wNumBytes = 0; ULONG_PTR CompKey = 0; P_LogIO pIO = NULL; BOOL bResult = false; while(1) { //获得完成队列,如果pio不为null那么就将其释放 bResult = m_ciop.GetStatus( &wNumBytes, &CompKey, (OVERLAPPED**)&pIO, 0 ); if( pIO != NULL ) { delete pIO; pIO = NULL; } else { break; } } return true; } _MyLog& _MyLog::_endl() { if( this->IsValid() ) { //处理已完成的IO请求,主要用来释放资源 this->CompleteHandle(); //获得当前线程的流 p_stream p_str = GetThreadStream(); //插入换行符 *p_str << TEXT("\r\n");#ifdef UNICODE //将流从宽字节转换为多字节 char * pBuff = _UnicodeToANSI( p_str->str().c_str() );#else char * pBuff = (char *)malloc( p_str->str().length() + 1 ); strcpy_s( pBuff, p_str->str().length() + 1, p_str->str().c_str() );#endif //将流清空 p_str->str( TEXT("") );#ifdef DEBUG_OUT //是否输出到控制台 degug_out << pBuff;#endif //新建IO结构 P_LogIO pIO = new _LogIO; pIO->m_str = pBuff; //释放资源 free( pBuff ); //设置重叠结构的文件偏移量 pIO->Offset = this->m_liFileNextOffset.LowPart; pIO->OffsetHigh = this->m_liFileNextOffset.HighPart; //设置下一次文件偏移量 this->m_liFileNextOffset.QuadPart += pIO->m_str.length(); //投递重叠IO ::WriteFile( this->m_LogFile, pIO->m_str.c_str(), pIO->m_str.length(), NULL, pIO ); } return *this; } //文件是否有效 bool _MyLog::IsValid() { return ( m_LogFile == INVALID_HANDLE_VALUE ) ? false : true; } //重载操作符模版接口 template<typename T> _MyLog & _MyLog::operator << ( T info ) { WriteLog( info ); return *this; } //重载的操作符函数结构 _MyLog & _MyLog::operator << ( _MyLog &(__cdecl * pfn)( _MyLog & ) ) { return ((*pfn)(*this)); } //当前时间格式由m_DateFormat 指定 _MyLog & time( _MyLog & Log ) { //获得当前的时间,并写入日志文件 CString currenttime = CTime::GetCurrentTime().Format( Log.m_DateFormat ); Log.WriteLog( (LPCTSTR)currenttime ); return Log; } //线程id _MyLog & id( _MyLog & Log ) { //格式化线程ID#ifdef UNICODE wchar_t pBuff[12] = { 0 }; swprintf( pBuff, sizeof(pBuff), TEXT("%- 8X"), GetCurrentThreadId() );#else char pBuff[12] = { 0 }; sprintf( pBuff, TEXT("%- 8X"), GetCurrentThreadId() );#endif Log.WriteLog( pBuff ); return Log; } //制表符 _MyLog & table( _MyLog & Log ) { Log.WriteLog( TEXT("\t") ); return Log; } //空格符 _MyLog & space( _MyLog & Log ) { if(Log.IsValid()) { Log.WriteLog( TEXT(" ") ); } return Log; } //换行并刷新缓冲区所有数据写入对象中 _MyLog & endl( _MyLog & Log ) { return Log._endl(); }}
好了 基本就是这样了,欢迎各位交流指证,交流才会进步不是么?
链接:http://download.csdn.net/detail/cstringw/9559463
2 0
- C++封装的高性能异步日志,cout实现方式
- 采用libuv的epoll方式实现的异步高性能libcurl发送数据的方法
- libcurl采用curl_multi_perform() + curl_multi_wait()方式实现异步高性能l发送数据的方法
- C语言实现简单的日志封装
- DataSet高性能数据传输方式的实现
- 高性能的智能日志
- jdk实现高性能异步线程开启
- 封装一个cout方法,能实现如此调用:cout(a)(b)(c)(d)(e)… 并且返回的值为参数连剩的结果,即a*b*c*d*e*…。如cout(1)(3)(7) 得到21
- 日志记录、性能监控的三种实现方式
- C#高性能TCP服务的多种实现方式
- C#高性能TCP服务的多种实现方式
- 封装多种方式的ajax异步方法
- Java异步NIO框架Netty实现高性能高并发
- Java异步NIO框架Netty实现高性能高并发
- C语言编程常用数值计算的高性能实现
- 高性能的贪吃蛇C语言实现
- 基于 IOCP 的通用异步 Windows Socket TCP 高性能服务端组件的设计与实现
- 基于 IOCP 的通用异步 Windows Socket TCP 高性能服务端组件的设计与实现
- redis之zipmap
- C/C++中volatile关键字详解
- C语言基础 计算三维数组的页,行,列
- C++运算符重载
- CornerStone —— 医学影像显示的JavaScript库简介
- C++封装的高性能异步日志,cout实现方式
- 各大HotFix热补丁方案分析和比较
- SPFA的构图加判定 SGU 103
- “Bus Pass(公交车通票),ZOJ2913”的一种解法
- 整数中1出现的次数(从1到n整数中1出现的次数)
- <<matlab>>matlab高数实验准备工作
- 三维重建学习之旅(一)
- android中handler和looper的工作原理
- 极光推送