请移步:http://blog.csdn.net/cstringw/article/details/79605143
- 异步写入日志———-基于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 wcout
typedef wstringstream _stream;
typedef wstringstream* p_stream;
#else
#define degug_out cout
typedef stringstream _stream;
typedef stringstream* p_stream;
#endif
//C++映射(map)对象,unsigned long 用于线程的ID 类型为DWORD
typedef map<unsigned long , p_stream > ThreadMap;
//定义在名称空间中,为了避免与其他函数混淆 如:time、endl
namespace _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();
}
}
好了 基本就是这样了,欢迎各位交流指证,交流才会进步不是么?