Index: src/libchcore/log.cpp =================================================================== diff -u -N --- src/libchcore/log.cpp (revision 8068e0c351055554340ac9755d1bc846893bf2b8) +++ src/libchcore/log.cpp (revision 0) @@ -1,671 +0,0 @@ -/*************************************************************************** -* Copyright (C) 2001-2008 by J�zef Starosczyk * -* ixen@copyhandler.com * -* * -* This program is free software; you can redistribute it and/or modify * -* it under the terms of the GNU Library General Public License * -* (version 2) as published by the Free Software Foundation; * -* * -* This program is distributed in the hope that it will be useful, * -* but WITHOUT ANY WARRANTY; without even the implied warranty of * -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -* GNU General Public License for more details. * -* * -* You should have received a copy of the GNU Library General Public * -* License along with this program; if not, write to the * -* Free Software Foundation, Inc., * -* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * -***************************************************************************/ -/** \file log.cpp - * \brief Contains the implamentation of a log class. - */ -#include "stdafx.h" -#include "log.h" -#include -#include -#include -#include -#include - -#include -#include -#include -#include "TCoreException.h" -#include "ErrorCodes.h" -#include - -namespace chcore -{ - /// Table of strings representing the log message types - const wchar_t* __logtype_str[] = { _T("debug"), _T("info"), _T("warning"), _T("error") }; - - /** Constructs a log_file object. - * \param[in] bGlobal - states if this should be treates as a global instance of the log_file. - * Only one global log_file instance could exist in the application. - */ - log_file::log_file() : - m_pszPath(nullptr), - m_iMaxSize(262144), - m_bLogStd(false), - m_iLogLevel(level_debug), - m_lock() - { - _set_fmode(_O_BINARY); - } - - /** Standard destructor - */ - log_file::~log_file() - { - delete[] m_pszPath; - } - - /** Initializes the constructed log file. - * \param[in] pszPath - path to a log file to write to - * \param[in] iMaxSize - maximum size of a log file - * \param[in] iLogLevel - minimum log level of the messages to log - * \param[in] bLogStd - log the messages also to stdout/stderr - * \param[in] bClean - cleans the log file upon opening - */ - void log_file::init(const wchar_t* pszPath, int iMaxSize, int iLogLevel, bool bLogStd, bool bClean) - { - // store the path and other params - delete[] m_pszPath; - size_t stInLen = _tcslen(pszPath); - m_pszPath = new wchar_t[stInLen + 1]; - _tcsncpy_s(m_pszPath, stInLen + 1, pszPath, _TRUNCATE); - - m_iMaxSize = iMaxSize; - m_bLogStd = bLogStd; - m_iLogLevel = iLogLevel; - - // try to open a file - FILE* pFile = _tfopen(pszPath, bClean ? _T("w") : _T("a")); - if (pFile == nullptr) - throw TCoreException(eErr_CannotOpenFile, L"Could not open the specified file", LOCATION); - - fclose(pFile); - } - - // ============================================================================ - /// log_file::is_initialized - /// @date 2009/05/19 - /// - /// @brief Checks is the log_file object has been initialized. - /// @return True if it has been initialized, false otherwise. - // ============================================================================ - bool log_file::is_initialized() const throw() - { - return m_pszPath != 0; - } - - // ============================================================================ - /// log_file::set_log_level - /// @date 2009/05/23 - /// - /// @brief Changes the log level for this class. - /// @param[in] iLogLevel New log level. - // ============================================================================ - void log_file::set_log_level(int iLogLevel) throw() - { - m_iLogLevel = iLogLevel; - } - - // ============================================================================ - /// log_file::set_max_size - /// @date 2009/05/23 - /// - /// @brief Sets the max size of the log file. - /// @param[in] iMaxSize Max size of the log file. - // ============================================================================ - void log_file::set_max_size(int iMaxSize) throw() - { - BOOST_ASSERT(iMaxSize > 0); - if (iMaxSize > 0) - m_iMaxSize = iMaxSize; - } - - /** Retrieves the current size of a log file. - * Quite slow function - have to access the file by opening and closing it. - * \return Current file size. - */ - int log_file::size() const - { - assert(m_pszPath); - if (!m_pszPath) - return -1; - - int iSize = -1; - FILE* pFile = _tfopen(m_pszPath, _T("r")); - if (pFile != nullptr) - { - if (fseek(pFile, 0, SEEK_END) == 0) - iSize = ftell(pFile); - - fclose(pFile); - } - - return iSize; - } - - /** Truncates the current log file content so when adding some new text the - * file size won't exceed the maximum size specified in init(). - * \param[in] iAdd - size of the new string to be added to the log file - * \return True if truncate succeeded or false if not. - */ - bool log_file::truncate(int iAdd) const - { - assert(m_pszPath); - if (!m_pszPath) - return false; - - // if we doesn't need to truncate anything - if (m_iMaxSize <= 0) - return true; - - // make some checks - int iSize = size(); - if (iSize <= 0 || iSize + iAdd < m_iMaxSize) - return false; - - // establish the new file size (1/3rd of the current size or max_size-add_size) - int iNewSize = std::min((int)(iSize*0.66), m_iMaxSize - iAdd) & ~1; - -#ifdef _WIN32 - // win32 does not have the ftruncate function, so we have to make some API calls - HANDLE hFile = CreateFile(m_pszPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); - if (hFile != INVALID_HANDLE_VALUE) - { - // seek - if (SetFilePointer(hFile, iSize - iNewSize, nullptr, FILE_BEGIN) != INVALID_SET_FILE_POINTER) - { - // read the string to the eol - DWORD dwRD; - wchar_t szBuffer[4096 / sizeof(wchar_t)]; - if (ReadFile(hFile, szBuffer, 4096, &dwRD, nullptr)) - { - dwRD /= sizeof(wchar_t); - szBuffer[(dwRD > 0) ? dwRD - 1 : 0] = _T('\0'); - - // replace the /r and /n in the log to the \0 - for (DWORD i = 0; i < dwRD; i++) - { - if (szBuffer[i] == _T('\r') || szBuffer[i] == _T('\n')) - { - szBuffer[i] = _T('\0'); - break; - } - } - - iNewSize -= (int)(_tcslen(szBuffer) + 1)*sizeof(wchar_t); // new size correction - - if (SetFilePointer(hFile, iSize - iNewSize, nullptr, FILE_BEGIN) != INVALID_SET_FILE_POINTER) - { - long lSrc = (long)SetFilePointer(hFile, 0, nullptr, FILE_CURRENT); - long lDst = 0; - DWORD tRD, tWR; - - do - { - // seek to src - SetFilePointer(hFile, lSrc, nullptr, FILE_BEGIN); - - // read 4k chars from source offset - if (ReadFile(hFile, szBuffer, 4096, &tRD, nullptr)) - { - // seek to the dst - SetFilePointer(hFile, lDst, nullptr, FILE_BEGIN); - - FlushFileBuffers(hFile); - // write the buffer to the dest offset - WriteFile(hFile, szBuffer, tRD, &tWR, nullptr); - lDst += (long)tWR; - } - - lSrc += (long)tRD; - } while (tRD != 0); - - // now truncate the file to the needed size - SetEndOfFile(hFile); - } - } - - CloseHandle(hFile); - return true; - } - - CloseHandle(hFile); - } -#else - FILE* pFile = fopen(m_pszPath, _T("r+")); - if (pFile) - { - // seek - if (fseek(pFile, iSize - iNewSize, SEEK_SET) == 0) - { - // read the string to the eol - wchar_t szBuffer[4096]; - fgets(szBuffer, 4096, pFile); - - int iSrc = ftell(pFile); - int iDst = 0; - size_t tRD, tWR; - - do - { - // seek to src - fseek(pFile, iSrc, SEEK_SET); - - // read 4k chars from source offset - tRD = fread(szBuffer, 1, 4096, pFile); - if (tRD > 0) - { - // seek to the dst - fseek(pFile, iDst, SEEK_SET); - - fflush(pFile); - // write the buffer to the dest offset - tWR = fwrite(szBuffer, 1, tRD, pFile); - iDst += tWR; - } - - iSrc += tRD; - } while (tRD != 0); - - // now truncate the file to the needed size - ftruncate(fileno(pFile), iDst); - - fclose(pFile); - return true; - } - - fclose(pFile); - } -#endif - - return false; - } - - /** Logs a formatted message to a log file. - * \param[in] iType - type of the log message (LT_*) - * \param[in] bStd - log also to stdout/stderr if true - * \param[in] pszStr - format string for the following parameters - */ - void log_file::log(int iType, bool bStd, const wchar_t* pszStr, ...) - { - if (iType < m_iLogLevel) - return; - - va_list va; - va_start(va, pszStr); - logv(iType, bStd, pszStr, va); - va_end(va); - } - - /** Logs a formatted message to a log file. - * \param[in] iType - type of the log message (LT_*) - * \param[in] bStd - log also to stdout/stderr if true - * \param[in] pszStr - format string for the following parameters - * \param[in] va - variable argument list - */ - void log_file::logv(int iType, bool bStd, const wchar_t* pszStr, va_list va) - { - if (iType < m_iLogLevel) - return; - - wchar_t szBuf1[2048]; - _vsntprintf(szBuf1, 2048, pszStr, va); // user passed stuff - - logs(iType, bStd, szBuf1); - } - - /** Logs an unformatted message to a log file. - * \param[in] iType - type of the log message (LT_*) - * \param[in] bStd - log also to stdout/stderr if true - * \param[in] pszStr - message string - */ - void log_file::logs(int iType, bool bStd, const wchar_t* pszStr) - { - assert(m_pszPath); - if (!m_pszPath) - return; - - if (iType < m_iLogLevel || iType < 0 || iType >= (sizeof(__logtype_str) / sizeof(wchar_t*))) - return; - - // log time - time_t t = time(nullptr); - std::wstring strTime = _tctime(&t); - boost::trim_right_if(strTime, boost::is_any_of(L"\n")); - - m_lock.lock(); - - // check the size constraints - truncate((int)(_tcslen(pszStr) + 1)); -#if defined(UNICODE) && (defined(_WIN32) || defined(_WIN64)) - FILE* pFile = _tfopen(m_pszPath, _T("ab")); -#else - FILE* pFile = _tfopen(m_pszPath, _T("at")); -#endif - bool bFailed = false; - if (pFile) - { - if (_ftprintf(pFile, _T("[%s] [%s] %s\r\n"), strTime.c_str(), __logtype_str[iType], pszStr) < 0) - bFailed = true; - fclose(pFile); - } - else - bFailed = true; - if (bFailed || (m_bLogStd && !bStd)) - { - switch (iType) - { - case level_error: - _ftprintf(stderr, _T("[%s] [%s] %s\r\n"), strTime.c_str(), __logtype_str[iType], pszStr); - break; - default: - _ftprintf(stdout, _T("[%s] [%s] %s\r\n"), strTime.c_str(), __logtype_str[iType], pszStr); - } - } - else if (bStd) - { - switch (iType) - { - case level_error: - _ftprintf(stderr, _T("%s: %s\r\n"), __logtype_str[iType], pszStr); - break; - case level_info: - _ftprintf(stdout, _T("%s\r\n"), pszStr); - break; - default: - _ftprintf(stdout, _T("%s: %s\r\n"), __logtype_str[iType], pszStr); - } - } - - m_lock.unlock(); - } - -#ifndef SKIP_LEVEL_DEBUG - /** Logs a formatted debug message to a log file. - * \param[in] pszStr - format string for the given parameters - */ - void log_file::logd(const wchar_t* pszStr) - { - if (m_iLogLevel > level_debug) - return; - - logs(level_debug, false, pszStr); - } - - /** Logs a formatted debug message to a log file. - * \param[in] pszStr - format string for the given parameters - */ - void log_file::logdv(const wchar_t* pszStr, ...) - { - if (m_iLogLevel > level_debug) - return; - - va_list va; - va_start(va, pszStr); - logv(level_debug, false, pszStr, va); - va_end(va); - } - - /** Logs a formatted debug message to a log file(also outputs to stdout). - * \param[in] pszStr - format string for the given parameters - */ - void log_file::logds(const wchar_t* pszStr, ...) - { - if (m_iLogLevel > level_debug) - return; - - va_list va; - va_start(va, pszStr); - logv(level_debug, true, pszStr, va); - va_end(va); - } - -#else - void log_file::logd(const wchar_t* /*pszStr*/) - { - } - - void log_file::logdv(const wchar_t* /*pszStr*/, ...) - { - } - - void log_file::logds(const wchar_t* /*pszStr*/, ...) - { - } -#endif - -#ifndef SKIP_LEVEL_INFO - /** Logs a formatted informational message to a log file. - * \param[in] pszStr - format string for the given parameters - */ - void log_file::logi(const wchar_t* pszStr) - { - if (m_iLogLevel > level_info) - return; - - logs(level_info, false, pszStr); - } - - /** Logs a formatted informational message to a log file. - * \param[in] pszStr - format string for the given parameters - */ - void log_file::logiv(const wchar_t* pszStr, ...) - { - if (m_iLogLevel > level_info) - return; - - va_list va; - va_start(va, pszStr); - logv(level_info, false, pszStr, va); - va_end(va); - } - - /** Logs a formatted informational message to a log file(also outputs to stdout). - * \param[in] pszStr - format string for the given parameters - */ - void log_file::logis(const wchar_t* pszStr, ...) - { - if (m_iLogLevel > level_info) - return; - - va_list va; - va_start(va, pszStr); - logv(level_info, true, pszStr, va); - va_end(va); - } -#else - void log_file::logi(const wchar_t* /*pszStr*/) - { - } - - void log_file::logiv(const wchar_t* /*pszStr*/, ...) - { - } - - void log_file::logis(const wchar_t* /*pszStr*/, ...) - { - } - -#endif - -#ifndef SKIP_LEVEL_WARNING - /** Logs a formatted warning message to a log file. - * \param[in] pszStr - format string for the given parameters - */ - void log_file::logw(const wchar_t* pszStr) - { - if (m_iLogLevel > level_warning) - return; - - logs(level_warning, false, pszStr); - } - - /** Logs a formatted warning message to a log file. - * \param[in] pszStr - format string for the given parameters - */ - void log_file::logwv(const wchar_t* pszStr, ...) - { - if (m_iLogLevel > level_warning) - return; - - va_list va; - va_start(va, pszStr); - logv(level_warning, false, pszStr, va); - va_end(va); - } - - /** Logs a formatted warning message to a log file(also outputs to stdout). - * \param[in] pszStr - format string for the given parameters - */ - void log_file::logws(const wchar_t* pszStr, ...) - { - if (m_iLogLevel > level_warning) - return; - va_list va; - va_start(va, pszStr); - logv(level_warning, true, pszStr, va); - va_end(va); - } - -#else - void log_file::logw(const wchar_t* /*pszStr*/) - { - } - - void log_file::logwv(const wchar_t* /*pszStr*/, ...) - { - } - - void log_file::logws(const wchar_t* /*pszStr*/, ...) - { - } - -#endif - - /** Logs a formatted error message to a log file. - * \param[in] pszStr - format string for the given parameters - */ - void log_file::loge(const wchar_t* pszStr) - { - logs(level_error, false, pszStr); - } - - /** Logs a formatted error message to a log file. - * \param[in] pszStr - format string for the given parameters - */ - void log_file::logev(const wchar_t* pszStr, ...) - { - va_list va; - va_start(va, pszStr); - logv(level_error, false, pszStr, va); - va_end(va); - } - - /** Logs a formatted error message to a log file(also outputs to stderr). - * \param[in] pszStr - format string for the given parameters - */ - void log_file::loges(const wchar_t* pszStr, ...) - { - va_list va; - va_start(va, pszStr); - logv(level_error, true, pszStr, va); - va_end(va); - } - - /** Logs a formatted error message to a log file(also outputs to stderr). - * As an addition the first string %err is replaced with a given error - * followed by the error description (system-based). - * \param[in] pszStr - format string for the given parameters - * \param[in] iSysErr - system error to be shown - */ - void log_file::logerr(const wchar_t* pszStr, int iSysErr, ...) - { - wchar_t szNewFmt[2048]; - if (prepare_fmt(pszStr, iSysErr, szNewFmt)) - { - va_list va; - va_start(va, iSysErr); - logv(level_error, false, szNewFmt, va); - va_end(va); - } - else - { - va_list va; - va_start(va, iSysErr); - logv(level_error, false, pszStr, va); - va_end(va); - } - } - - /** Logs a formatted error message to a log file(also outputs to stderr). - * As an addition the first string %err is replaced with a given error - * followed by the error description (system-based). - * This function differ from logerr() with logging the output string - * also to the stderr. - * \param[in] pszStr - format string for the given parameters - * \param[in] iSysErr - system error to be shown - */ - void log_file::logerrs(const wchar_t* pszStr, int iSysErr, ...) - { - wchar_t szNewFmt[2048]; - if (prepare_fmt(pszStr, iSysErr, szNewFmt)) - { - va_list va; - va_start(va, iSysErr); - logv(level_error, true, szNewFmt, va); - va_end(va); - } - else - { - va_list va; - va_start(va, iSysErr); - logv(level_error, true, pszStr, va); - va_end(va); - } - } - - /** Function prepares a format string with error number and an error message - * for use with logerr() and logerrs() functions. - * \param[in] pszStr - input format string (%err will be replaced with a 0x%lx (error message) - * \param[in] iSysError - system error to parse - * \param[out] pszOut - pointer to a buffer that will receive the data (must be 2048 bytes in size) - * \return If the %err string was found and replaced within a given format string. - */ - bool log_file::prepare_fmt(const wchar_t* pszStr, int iSysErr, wchar_t* pszOut) const - { - // find the %err in pszStr - const wchar_t* pszFnd = _tcsstr(pszStr, _T("%err")); - if (pszFnd) - { - // find an error description for the error - wchar_t* pszErrDesc = nullptr; -#ifdef _WIN32 - wchar_t szErrDesc[512]; - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, (DWORD)iSysErr, 0, szErrDesc, 512, nullptr); - pszErrDesc = szErrDesc; -#else - pszErrDesc = strerror(iSysErr); -#endif - - // format a string with err no and desc - wchar_t szError[1024]; - _sntprintf(szError, 1023, _T("0x%lx (%s)"), iSysErr, pszErrDesc); - szError[1023] = _T('\0'); - - // replace %err with the new data - pszOut[0] = _T('\0'); - _tcsncat(pszOut, pszStr, (size_t)(pszFnd - pszStr)); - _tcscat(pszOut, szError); - _tcscat(pszOut, pszFnd + 4); - - return true; - } - else - return false; - } -}