Click here to Skip to main content
15,885,366 members
Articles / Web Development / HTML

Catch All Bugs with BugTrap!

Rate me:
Please Sign up or sign in to vote.
4.34/5 (84 votes)
31 Jan 2009MIT5 min read 1.8M   9.1K   292  
A tool that can catch unhandled errors and exceptions, and deliver error reports to remote support servers
/*
 * This is a part of the BugTrap package.
 * Copyright (c) 2005-2007 IntelleSoft.
 * All rights reserved.
 *
 * Description: Text log file.
 * Author: Maksim Pyatkovskiy.
 *
 * This source code is only intended as a supplement to the
 * BugTrap package reference and related electronic documentation
 * provided with the product. See these sources for detailed
 * information regarding the BugTrap package.
 */

#include "StdAfx.h"
#include "TextLogFile.h"
#include "TextFormat.h"
#include "Globals.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

/**
 * @return true if the log was loaded successfully.
 */
BOOL CTextLogFile::LoadEntries(void)
{
#ifdef _DEBUG
	DWORD dwStartTime = GetTickCount();
#endif
	BOOL bResult = FALSE;
	PCTSTR pszLogFileName = GetLogFileName();
	HANDLE hFile = CreateFile(pszLogFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile != INVALID_HANDLE_VALUE)
	{
		DWORD dwFileSize = GetFileSize(hFile, NULL);
		if (dwFileSize == 0)
		{
			// ignore empty files
			CloseHandle(hFile);
			return TRUE;
		}
		DWORD dwBufferSize = min(dwFileSize, g_dwMaxBufferSize);
		PBYTE pFileBuffer = new BYTE[dwBufferSize];
		if (pFileBuffer)
		{
			BYTE arrUTF8Preamble[sizeof(g_arrUTF8Preamble)];
			DWORD dwWritten = 0;
			if (ReadFile(hFile, arrUTF8Preamble, sizeof(arrUTF8Preamble), &dwWritten, NULL) &&
				dwWritten == sizeof(arrUTF8Preamble) && memcmp(arrUTF8Preamble, g_arrUTF8Preamble, sizeof(arrUTF8Preamble)) == 0)
			{
				bResult = TRUE;
				DWORD dwCurrentPos = 0, dwLineStart = 0;
				for (;;)
				{
					dwWritten = 0;
					DWORD dwFreeSize = dwBufferSize - dwCurrentPos;
					if (! ReadFile(hFile, pFileBuffer + dwCurrentPos, dwFreeSize, &dwWritten, NULL))
						goto end;
					BOOL bEndOfFile = dwWritten < dwFreeSize;
					dwWritten += dwCurrentPos;
					dwCurrentPos = 0;
					if (dwWritten == 0)
						goto end;
					for (;;)
					{
						if (dwCurrentPos >= dwWritten && ! bEndOfFile)
						{
							if (dwLineStart > 0)
							{
								dwCurrentPos -= dwLineStart;
								MoveMemory(pFileBuffer, pFileBuffer + dwLineStart, dwCurrentPos);
								dwLineStart = 0;
							}
							else
							{
								dwBufferSize *= 2;
								PBYTE pNewFileBuffer = new BYTE[dwBufferSize];
								if (! pNewFileBuffer)
								{
									bResult = FALSE;
									goto end;
								}
								CopyMemory(pNewFileBuffer, pFileBuffer, dwCurrentPos);
								delete[] pFileBuffer;
								pFileBuffer = pNewFileBuffer;
							}
							break;
						}
						if (dwCurrentPos < dwWritten ? pFileBuffer[dwCurrentPos] == '\r' || pFileBuffer[dwCurrentPos] == '\n' : bEndOfFile)
						{
							if (dwLineStart < dwCurrentPos && ! AddToTail(pFileBuffer + dwLineStart, dwCurrentPos - dwLineStart, true))
							{
								bResult = FALSE;
								goto end;
							}
							if (dwCurrentPos == dwWritten) // bEndOfFile == TRUE, see condition above
							{
								bResult = TRUE;
								goto end;
							}
							dwLineStart = dwCurrentPos + 1;
						}
						++dwCurrentPos;
					}
				}
			}
end:
			if (! bResult)
				FreeEntries();
			delete[] pFileBuffer;
		}
		CloseHandle(hFile);
	}
	else
	{
		DWORD dwLastError = GetLastError();
		if (dwLastError == ERROR_FILE_NOT_FOUND ||
			dwLastError == ERROR_PATH_NOT_FOUND ||
			GetFileAttributes(pszLogFileName) == INVALID_FILE_ATTRIBUTES)
		{
			bResult = TRUE; // ignore missing files
		}
	}
#ifdef _DEBUG
	DWORD dwEndTime = GetTickCount();
	TCHAR szMessage[128];
	_stprintf_s(szMessage, countof(szMessage), _T("CTextLogFile::LoadEntries(): %lu entries, %lu bytes, %lu milliseconds\r\n"), GetNumEntries(), GetNumBytes(), dwEndTime - dwStartTime);
	OutputDebugString(szMessage);
#endif
	return bResult;
}

/**
 * @param bCrash - true if crash has occurred.
 * @return true if the log was saved successfully.
 */
BOOL CTextLogFile::SaveEntries(bool /*bCrash*/)
{
#ifdef _DEBUG
	DWORD dwStartTime = GetTickCount();
#endif
	PCTSTR pszLogFileName = GetLogFileName();
	HANDLE hFile = CreateFile(pszLogFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
		return FALSE;
	DWORD dwWritten;
	WriteFile(hFile, g_arrUTF8Preamble, sizeof(g_arrUTF8Preamble), &dwWritten, NULL);
	CLogEntry* pLogEntry = GetFirstEntry();
	while (pLogEntry)
	{
		CTextLogEntry* pTextLogEntry = (CTextLogEntry*)pLogEntry;
		WriteFile(hFile, pTextLogEntry->m_pbData, pTextLogEntry->m_dwSize, &dwWritten, NULL);
		pLogEntry = pLogEntry->m_pNextEntry;
	}
#ifdef _DEBUG
	DWORD dwEndTime = GetTickCount();
	TCHAR szMessage[128];
	_stprintf_s(szMessage, countof(szMessage), _T("CTextLogFile::SaveEntries(): %lu entries, %lu bytes, %lu milliseconds\r\n"), GetNumEntries(), GetNumBytes(), dwEndTime - dwStartTime);
	OutputDebugString(szMessage);
#endif
	CloseHandle(hFile);
	return TRUE;
}

/**
 * @param pbData - entry data.
 * @param dwSize - data size.
 * @param bAddCrLf - true if CR/LF must be added.
 * @return pointer to the new entry.
 */
CTextLogFile::CTextLogEntry* CTextLogFile::AllocLogEntry(const BYTE* pbData, DWORD dwSize, bool bAddCrLf)
{
	DWORD dwFullSize = bAddCrLf ? dwSize + 2 : dwSize;
	CTextLogEntry* pLogEntry = (CTextLogEntry*)new BYTE[sizeof(CTextLogEntry) + dwFullSize];
	if (pLogEntry)
	{
		pLogEntry->m_dwSize = dwFullSize;
		PBYTE pDstData = pLogEntry->m_pbData;
		CopyMemory(pDstData, pbData, dwSize);
		if (bAddCrLf)
		{
			pDstData[dwSize++] = _T('\r');
			pDstData[dwSize++] = _T('\n');
		}
	}
	return pLogEntry;
}

/**
 * @param pbData - entry data.
 * @param dwSize - data size.
 * @param bAddCrLf - true if CR/LF must be added.
 * @return true if entry was added.
 */
BOOL CTextLogFile::AddToHead(const BYTE* pbData, DWORD dwSize, bool bAddCrLf)
{
	CTextLogEntry* pLogEntry = AllocLogEntry(pbData, dwSize, bAddCrLf);
	if (pLogEntry)
	{
		CLogFile::AddToHead(pLogEntry);
		return TRUE;
	}
	else
		return FALSE;
}

/**
 * @param pbData - entry data.
 * @param dwSize - data size.
 * @param bAddCrLf - true if CR/LF must be added.
 * @return true if entry was added.
 */
BOOL CTextLogFile::AddToTail(const BYTE* pbData, DWORD dwSize, bool bAddCrLf)
{
	CTextLogEntry* pLogEntry = AllocLogEntry(pbData, dwSize, bAddCrLf);
	if (pLogEntry)
	{
		CLogFile::AddToTail(pLogEntry);
		return TRUE;
	}
	else
		return FALSE;
}

/**
 * @param bAddCrLf - true if CR/LF must be added.
 * @return true if entry was added.
 */
BOOL CTextLogFile::AddToHead(bool bAddCrLf)
{
	const BYTE* pBuffer = m_MemStream.GetBuffer();
	if (pBuffer == NULL)
		return FALSE;
	DWORD dwLength = m_MemStream.GetLength();
	_ASSERTE(dwLength > 0);
	return AddToHead(pBuffer, dwLength, bAddCrLf);
}

/**
 * @param bAddCrLf - true if CR/LF must be added.
 * @return true if entry was added.
 */
BOOL CTextLogFile::AddToTail(bool bAddCrLf)
{
	const BYTE* pBuffer = m_MemStream.GetBuffer();
	if (pBuffer == NULL)
		return FALSE;
	DWORD dwLength = m_MemStream.GetLength();
	_ASSERTE(dwLength > 0);
	return AddToTail(pBuffer, dwLength, bAddCrLf);
}

/**
 * @param eLogLevel - log level number.
 * @param eEntryMode - entry mode.
 * @param rcsConsoleAccess - provides synchronous access to the console.
 * @param pszEntry - log entry text.
 */
void CTextLogFile::WriteLogEntry(BUGTRAP_LOGLEVEL eLogLevel, ENTRY_MODE eEntryMode, CRITICAL_SECTION& rcsConsoleAccess, PCTSTR pszEntry)
{
	BUGTRAP_LOGLEVEL eLogFileLevel = GetLogLevel();
	if (eLogLevel <= eLogFileLevel)
	{
		DWORD dwLogEchoMode = GetLogEchoMode();
		HANDLE hConsole = GetConsoleHandle();
		BOOL bLogEcho = (dwLogEchoMode & BTLE_DBGOUT) != 0;
		BOOL bConsoleOutput = hConsole || bLogEcho;
		if (bConsoleOutput)
			EnterCriticalSection(&rcsConsoleAccess);
		SYSTEMTIME st;
		GetLocalTime(&st);
		FillEntryText(eLogLevel, &st, pszEntry);
		if (hConsole)
			WriteTextToConsole(hConsole);
		if (bLogEcho)
			WriteTextToDebugConsole();
		EncodeEntryText();
		switch (eEntryMode)
		{
		case EM_APPEND:
			AddToTail(false);
			break;
		case EM_INSERT:
			AddToHead(false);
			break;
		default:
			_ASSERT(FALSE);
		}
		if (bConsoleOutput)
			LeaveCriticalSection(&rcsConsoleAccess);
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions