功能:输出日志信息logfile.h VC日志类调试信息输出,是编程调试跟踪流程的日志输出好帮手,很有助于程序的排错调试.
1、使用简单方便。只有一个头文件logfile.h include后,直接调用函数即可
2、兼容VC6,VC7(VS系列,VS2008)。 兼容所有VC版本
3、支持源代码文件名及行号的输出。输出日志所在的源文件名和行数。
4、支持多线程应用。CriticalSection控制线程对日志文件的有序访问读写。
5、支持Debug版本输出,Release版本不输出。有效控制调式版本和发布版本的日志输出。
6、支持设置控制台。在MFC程序中,可以增加打开控制台方便查看日志log信息。
7、支持设置文件名。默认文件名为同目录下的log(xxx)YYYY-MM-DD.txt,可以自定义日志文件名。
8、支持设置等级。可以设置需求查看的最小和最大等级。当相等时,查看某一级别的日志。
9、支持按天轮询写日志。默认为一天一个日志文件。
代码:
/******************************************************************************
//功能: 输出日志信息 logfile.h
/******************************************************************************
1, 使用简单方便.只有一个头文件logfile.h include后,直接调用函数即可
2, VC6,VC7(VS2008) 兼容VC版本
3, 可输出文件名及行号
4, 支持多线程应用
例如:
在cpp源代码文件中只要#include "logfile.h"后,就可以直接调用以下函数输出日志信息
Logout("I am Logout \r\n");
Logflout(AT"I am LogfloutAT \r\n");
Loglevelout(3,"I am Loglevelout");
CString test = " i am wangxiaoding!";
int n = 8;
Logout("CString = %s \r\n",test);
Logout("Intnumber = %d \r\n",n);
******************************************************************************/
//防止多次include头文件
#if !defined(AFX_LOGFILE_H__EF4BC4B2_3BB6_46E8_B936_0F3A61E20BF0__INCLUDED_)
#define AFX_LOGFILE_H__EF4BC4B2_3BB6_46E8_B936_0F3A61E20BF0__INCLUDED_
#pragma once
//-----------------------------------------------------------------------------
// Debug版本宏1
#if _DEBUG
#ifndef _FLAG_OUTLOG_ENABLE
#define _FLAG_OUTLOG_ENABLE TRUE
#endif // _FLAG_OUTLOG_ENABLE
#endif // _DEBUG
// 设置控制台宏2
#define _DEBUGCONSOLE
// 设置文件名宏3
//#define _SETFILENAME
#ifdef _SETFILENAME
#define FILENAME "log.txt"
#endif //_SETFILENAME
// 设置等级宏4
#define _LOGLEVEL
#ifdef _LOGLEVEL
#define MIN_LEVEL 1
#define MAX_LEVEL 5
#endif // _LOGLEVEL
//-----------------------------------------------------------------------------
#include <windows.h>
#include <stdio.h>
#include <stdarg.h>
// 日志输出类,静态版
struct CLog
{
// 取进程执行文件名称
static void GetProcessFileName(char* lpName)
{
if ( ::GetModuleFileNameA(NULL, lpName, MAX_PATH) > 0)
{
char* pBegin = lpName;
char* pTemp = lpName;
while ( *pTemp != 0 )
{
if ( *pTemp == '\\' )
{
pBegin = pTemp + 1;
}
pTemp++;
}
memcpy(lpName, pBegin, strlen(pBegin)+1);
}
}
// 输出到文件
// lpFile : 源文件名
// nLine : 源文件行号
// lpFormat : 输出的内容
static void logout(LPCSTR lpFile, int nLine,LPCSTR lpFormat, ...)
{
static CRITICAL_SECTION m_crit;
if (m_crit.DebugInfo==NULL)
::InitializeCriticalSection(&m_crit);
/*-----------------------进入临界区(输出信息)------------------------------*/
::EnterCriticalSection(&m_crit);
if ( NULL == lpFormat )
return;
//当前时间
SYSTEMTIME st;
::GetLocalTime(&st);
//设置消息头
const DWORD BufSize = 2048;
char szMsg[BufSize];
if (nLine==0)
{
//当nLine==0 时,即Logout("xxx")只打印信息
sprintf(szMsg, "[%02d:%02d:%02d.%03d]:",
st.wHour, st.wMinute, st.wSecond,
st.wMilliseconds);
}
else
{
//当nLine不等于0 时,即Logflout(AT"xxx")打印文件名行号及信息
sprintf(szMsg, "[%02d:%02d:%02d.%03d]文件%s第%04d行:",
st.wHour, st.wMinute, st.wSecond,
st.wMilliseconds, lpFile, nLine);
}
//格式化消息,并完善整条消息
char* pTemp = szMsg;
pTemp += strlen(szMsg);
va_list args;
va_start(args, lpFormat);
wvsprintfA(pTemp, lpFormat, args); //vsprintf_s BufSize - strlen(szMsg),
va_end(args);
DWORD dwMsgLen = (DWORD)strlen(szMsg);
//获取日志文件名
char szFileName[MAX_PATH];
char szExeName[MAX_PATH];
GetProcessFileName(szExeName);
sprintf(szFileName, "Log(%s)%d-%d-%d.txt", szExeName, //sprintf_s MAX_PATH
st.wYear, st.wMonth, st.wDay);
// 判断文件名称是否相同,句柄是否有效.
// 如果不同或无效,则关闭当前文件,创建新文件
static char s_szFileName[MAX_PATH] = {0};
static HANDLE s_hFile = INVALID_HANDLE_VALUE;
//设置自定义日志文件名
#ifdef _SETFILENAME
strcpy(szFileName,FILENAME);
#endif //_SETFILENAME
BOOL bNew = ((strcmp(s_szFileName, szFileName) != 0) || (s_hFile == INVALID_HANDLE_VALUE));
#ifdef _DEBUGCONSOLE //控制台输出
static BOOL bOpenConsole = FALSE;
if (!bOpenConsole)
{
bOpenConsole = ::AllocConsole();
if (bOpenConsole)
{
freopen("CONOUT$","w+t",stdout);
freopen("CONIN$","r+t",stdin);
freopen("CONERR", "w", stderr);
HANDLE handle= GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTitle("DebugCosole");
SetConsoleTextAttribute((HANDLE)handle, FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
HWND hwnd=NULL;
while(NULL==hwnd)
hwnd=::FindWindow(NULL,(LPCTSTR)"DebugCosole");
HMENU hmenu = ::GetSystemMenu ( hwnd, FALSE );
DeleteMenu ( hmenu, SC_CLOSE, MF_BYCOMMAND );
}
}
#endif //_DEBUGCONSOLE
printf("%s", szMsg);
if ( bNew ) // 关闭旧文件,创建新文件
{
if ( s_hFile != INVALID_HANDLE_VALUE)
{
::CloseHandle(s_hFile);
s_hFile = INVALID_HANDLE_VALUE;
}
//创建日志文件. 有文件时追加方式打开,没有时创建.
s_hFile = ::CreateFileA( szFileName,
GENERIC_WRITE,
FILE_SHARE_WRITE | FILE_SHARE_READ,
0,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
0);
if ( s_hFile == INVALID_HANDLE_VALUE)
{
printf("::CreateFile Error: %d", ::GetLastError());
return;
}
}
//把消息写到文件
if ( s_hFile != INVALID_HANDLE_VALUE)
{
DWORD dwWrite = 0;
::SetFilePointer(s_hFile, 0, NULL, FILE_END);
::WriteFile(s_hFile, szMsg, dwMsgLen, &dwWrite, NULL);
//备份创建成功的新文件名
strcpy(s_szFileName,szFileName);
}
::LeaveCriticalSection(&m_crit);
/*----------------------------退出临界区---------------------------------*/
}
}; // CLog
//宏定义文件名和行号
#define AT __FILE__, __LINE__,
#if (_FLAG_OUTLOG_ENABLE)
//日志输出接口函数1
static void Logout(LPCSTR lpFormat, ...)
{
const DWORD BufSize = 2048;
char szMsg[BufSize];
va_list args; //格式化消息
va_start(args, lpFormat);
wvsprintfA(szMsg, lpFormat, args); //vsprintf_s BufSize - strlen(szMsg),
va_end(args);
//输出信息
CLog::logout("0",0,szMsg);
}
//日志输出接口函数2 使用于logflout(AT"xxxx")形式
//(LPCSTR lpFile, int nLine)有时适配这个函数名,所以修改函数名 fl = file and line
static void Logflout(LPCSTR lpFile, int nLine,LPCSTR lpFormat, ...)
{
const DWORD BufSize = 2048;
char szMsg[BufSize];
char* pTemp = szMsg;
va_list args; //格式化消息
va_start(args, lpFormat);
wvsprintfA(szMsg, lpFormat, args); //vsprintf_s BufSize - strlen(szMsg),
va_end(args);
//输出有文件名及行号的消息
CLog::logout(lpFile, nLine,szMsg);
}
#ifdef _LOGLEVEL
//日志输出接口函数3
static void Loglevelout(int nshowlevel,LPCSTR lpFormat, ...)
{
#ifdef _SETFILENAME
if (MIN_LEVEL<=nshowlevel && nshowlevel<= MAX_LEVEL)
#endif
{
const DWORD BufSize = 2048;
char szMsg[BufSize];
va_list args; //格式化消息
va_start(args, lpFormat);
wvsprintfA(szMsg, lpFormat, args); //vsprintf_s BufSize - strlen(szMsg),
va_end(args);
char buffer[20];
_itoa(nshowlevel, buffer, 10 );
strcat(szMsg,"......Level=");
strcat(szMsg,buffer);
strcat(szMsg,"\r\n");
//输出信息
CLog::logout("0",0,szMsg);
}
}
#endif //_LOGLEVEL
#ifdef _DEBUGCONSOLE
//关闭控制台接口函数4
static void Logconsole_close()
{
FreeConsole();
}
//隐藏或显示控制台接口函数5
static void Logcconsole_win(BOOL pSHWinConsole = FALSE)
{
static BOOL bGetWinConsole = FALSE;
HWND wincmd = NULL;
if (!bGetWinConsole)
{
typedef HWND (WINAPI *PROCGETCONSOLEWINDOW)();
PROCGETCONSOLEWINDOW GetConsoleWindow;
HMODULE hKernel32 = GetModuleHandle("kernel32");
GetConsoleWindow = (PROCGETCONSOLEWINDOW)GetProcAddress(hKernel32,"GetConsoleWindow");
wincmd=GetConsoleWindow();
}
if (pSHWinConsole)
{
ShowWindowAsync(wincmd, SW_SHOWNORMAL);
}
else
{
ShowWindowAsync(wincmd, SW_HIDE );
}
}
#endif //_DEBUGCONSOLE
#else //_FLAG_OUTLOG_ENABLE
//日志输出接口函数1 空 用于Release版本
static void Logout(LPCSTR lpFormat, ...)
{
}
//日志输出接口函数2 空 用于Release版本
static void Logflout(LPCSTR lpFile, int nLine,LPCSTR lpFormat, ...)
{
}
#ifdef _LOGLEVEL
//日志输出接口函数3 空 用于Release版本
static void Loglevelout(int nshowlevel,LPCSTR lpFormat, ...)
{
}
#endif //_LOGLEVEL
#ifdef _DEBUGCONSOLE
//关闭控制台接口函数4 空 用于Release版本
static void Logconsole_close()
{
}
//隐藏或显示控制台接口函数5 空 用于Release版本
static void Logcconsole_win(BOOL pSHWinConsole = FALSE)
{
}
#endif //_DEBUGCONSOLE
#endif //_FLAG_OUTLOG_ENABLE
#endif //!defined(AFX_LOGFILE_H__EF4BC4B2_3BB6_46E8_B936_0F3A61E20BF0__INCLUDED_)
/******************************************************************************
版本号: 3.
时间: 2013/5/16
为更方便书写,将函数名的首字母C去掉。如CLogout 更改为 Logout等
-----------------------------------------------------------------------------
版本号: 2.
时间: 2013/5/15
由于参数匹配有时混乱问题,所以修改函数名CLogout(LPCSTR lpFile, int nLine,LPCSTR lpFormat, ...)
为CLogflout(LPCSTR lpFile, int nLine,LPCSTR lpFormat, ...)
-----------------------------------------------------------------------------
版本号: 1.
时间: 2013/5/15
正常摘用
******************************************************************************/