现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

VC日志类:调试信息的输出

2013-12-13 11:32 工业·编程 ⁄ 共 8457字 ⁄ 字号 暂无评论

功能:输出日志信息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

正常摘用

******************************************************************************/

给我留言

留言无头像?