Index: src/liblogger/TLogFile.cpp =================================================================== diff -u -rde48778626a2e3f6a09a43f0211b2a04eba36fed -ra4635addad389b9e117679437a3e1b64a739ea96 --- src/liblogger/TLogFile.cpp (.../TLogFile.cpp) (revision de48778626a2e3f6a09a43f0211b2a04eba36fed) +++ src/liblogger/TLogFile.cpp (.../TLogFile.cpp) (revision a4635addad389b9e117679437a3e1b64a739ea96) @@ -1,200 +1,200 @@ -// ============================================================================ -// Copyright (C) 2001-2016 by Jozef 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. -// ============================================================================ -#include "stdafx.h" -#include "TLogFile.h" -#include -#include -#include -#include - -namespace logger -{ - namespace internal - { - TLogFile::TLogFile(PCTSTR pszPath, const TLoggerRotationInfoPtr& spRotationInfo) : - m_strLogPath(pszPath ? pszPath : L""), - m_spFileHandle(), - m_spRotationInfo(spRotationInfo) - { - if (!pszPath) - throw std::invalid_argument("pszPath"); - if (!spRotationInfo) - throw std::invalid_argument("spRotationInfo"); - - ScanForRotatedLogs(); - } - - void TLogFile::Write(std::list& rListEntries) - { - if (rListEntries.empty()) - return; - - try - { - std::wstring_convert> utf8Converter; - - for (const std::wstring& rstrEntry : rListEntries) - { - std::string strUtf8Line = utf8Converter.to_bytes(rstrEntry); - - size_t stEntryLen = strUtf8Line.length(); - if (NeedRotation(stEntryLen)) - RotateFile(); - - DWORD dwWritten = 0; - if (!WriteFile(GetFileHandle(), strUtf8Line.c_str(), boost::numeric_cast(stEntryLen), &dwWritten, nullptr)) - throw std::runtime_error("Cannot write to log, system error"); - } - } - catch (const std::exception&) - { - rListEntries.clear(); - return; - } - - m_timeLastWriteTime = time(nullptr); - rListEntries.clear(); - } - - void TLogFile::CloseIfUnused() - { - if (time(nullptr) - m_timeLastWriteTime > MaxHandleCacheTime) - CloseLogFile(); - } - - void TLogFile::CloseLogFile() - { - m_spFileHandle.reset(); - } - - const std::vector& TLogFile::GetRotatedLogs() const - { - return m_vRotatedFiles; - } - - std::wstring TLogFile::GetLogPath() const - { - return m_strLogPath; - } - - HANDLE TLogFile::GetFileHandle() - { - if (m_spFileHandle != nullptr) - return m_spFileHandle.get(); - - HANDLE hFile = CreateFile(m_strLogPath.c_str(), GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - if (hFile == INVALID_HANDLE_VALUE) - throw std::runtime_error("Cannot open log file"); - - m_spFileHandle.reset(hFile, CloseHandle); - - LARGE_INTEGER liSeek = { 0 }; - - BOOL bRes = SetFilePointerEx(hFile, liSeek, nullptr, SEEK_END); - if (!bRes) - throw std::runtime_error("Cannot seek to the end of log file"); - - return m_spFileHandle.get(); - } - - unsigned long long TLogFile::GetCurrentLogSize() - { - LARGE_INTEGER liSize = { 0 }; - if (!GetFileSizeEx(GetFileHandle(), &liSize)) - throw std::runtime_error("Cannot determine current log size"); - - return liSize.QuadPart; - } - - void TLogFile::RotateFile() - { - m_spFileHandle.reset(); - - std::wstring pathNew = m_strLogPath; - if (boost::iends_with(pathNew, L".log")) - pathNew.erase(pathNew.end() - 4, pathNew.end()); - - boost::posix_time::ptime timeNow = boost::posix_time::microsec_clock::local_time(); - boost::posix_time::wtime_facet* facet = new boost::posix_time::wtime_facet(); - facet->format(L"%Y%m%d%H%M%S%f"); - std::wstringstream stream; - stream.imbue(std::locale(std::locale::classic(), facet)); - stream << timeNow; - pathNew += L"."; - pathNew += stream.str().c_str(); - pathNew += L".log"; - - if (!MoveFile(m_strLogPath.c_str(), pathNew.c_str()) && GetLastError() != ERROR_FILE_NOT_FOUND) - throw std::runtime_error("Cannot rotate file"); - - m_vRotatedFiles.push_back(std::move(pathNew)); - RemoveObsoleteRotatedLogs(); - } - - void TLogFile::RemoveObsoleteRotatedLogs() - { - while (m_vRotatedFiles.size() > m_spRotationInfo->GetMaxRotatedCount()) - { - auto iterRotatedFile = m_vRotatedFiles.begin(); - if (!DeleteFile(iterRotatedFile->c_str())) - break; - - m_vRotatedFiles.erase(iterRotatedFile); - } - } - - void TLogFile::ScanForRotatedLogs() - { - std::wstring strSearchMask = m_strLogPath; - std::wstring strDir; - - size_t stDirPos = strSearchMask.find_last_of(L"\\/"); - if (stDirPos != std::wstring::npos) - strDir = strSearchMask.substr(0, stDirPos + 1); - - if (boost::iends_with(strSearchMask, L".log")) - strSearchMask.erase(strSearchMask.end() - 4, strSearchMask.end()); - strSearchMask += L".*.log"; - - std::vector vPaths; - WIN32_FIND_DATA wfd = { 0 }; - - HANDLE hFind = FindFirstFile(strSearchMask.c_str(), &wfd); - BOOL bFound = (hFind != INVALID_HANDLE_VALUE); - while (bFound) - { - std::wstring strLogFullPath = strDir + wfd.cFileName; - vPaths.push_back(strLogFullPath); - - bFound = FindNextFile(hFind, &wfd); - } - - std::sort(vPaths.begin(), vPaths.end(), [](const std::wstring& path1, const std::wstring& path2) { return boost::ilexicographical_compare(path1, path2); }); - std::swap(m_vRotatedFiles, vPaths); - } - - bool TLogFile::NeedRotation(size_t stDataSize) - { - unsigned long long ullCurrentSize = GetCurrentLogSize(); - unsigned long long ullMaxLogSize = m_spRotationInfo->GetMaxLogSize(); - - return ullCurrentSize + stDataSize > ullMaxLogSize; - } - } -} +// ============================================================================ +// Copyright (C) 2001-2016 by Jozef 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. +// ============================================================================ +#include "stdafx.h" +#include "TLogFile.h" +#include +#include +#include +#include + +namespace logger +{ + namespace internal + { + TLogFile::TLogFile(PCTSTR pszPath, const TLoggerRotationInfoPtr& spRotationInfo) : + m_strLogPath(pszPath ? pszPath : L""), + m_spFileHandle(), + m_spRotationInfo(spRotationInfo) + { + if (!pszPath) + throw std::invalid_argument("pszPath"); + if (!spRotationInfo) + throw std::invalid_argument("spRotationInfo"); + + ScanForRotatedLogs(); + } + + void TLogFile::Write(std::list& rListEntries) + { + if (rListEntries.empty()) + return; + + try + { + std::wstring_convert> utf8Converter; + + for (const std::wstring& rstrEntry : rListEntries) + { + std::string strUtf8Line = utf8Converter.to_bytes(rstrEntry); + + size_t stEntryLen = strUtf8Line.length(); + if (NeedRotation(stEntryLen)) + RotateFile(); + + DWORD dwWritten = 0; + if (!WriteFile(GetFileHandle(), strUtf8Line.c_str(), boost::numeric_cast(stEntryLen), &dwWritten, nullptr)) + throw std::runtime_error("Cannot write to log, system error"); + } + } + catch (const std::exception&) + { + rListEntries.clear(); + return; + } + + m_timeLastWriteTime = time(nullptr); + rListEntries.clear(); + } + + void TLogFile::CloseIfUnused() + { + if (time(nullptr) - m_timeLastWriteTime > MaxHandleCacheTime) + CloseLogFile(); + } + + void TLogFile::CloseLogFile() + { + m_spFileHandle.reset(); + } + + const std::vector& TLogFile::GetRotatedLogs() const + { + return m_vRotatedFiles; + } + + std::wstring TLogFile::GetLogPath() const + { + return m_strLogPath; + } + + HANDLE TLogFile::GetFileHandle() + { + if (m_spFileHandle != nullptr) + return m_spFileHandle.get(); + + HANDLE hFile = CreateFile(m_strLogPath.c_str(), GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hFile == INVALID_HANDLE_VALUE) + throw std::runtime_error("Cannot open log file"); + + m_spFileHandle.reset(hFile, CloseHandle); + + LARGE_INTEGER liSeek = { 0 }; + + BOOL bRes = SetFilePointerEx(hFile, liSeek, nullptr, SEEK_END); + if (!bRes) + throw std::runtime_error("Cannot seek to the end of log file"); + + return m_spFileHandle.get(); + } + + unsigned long long TLogFile::GetCurrentLogSize() + { + LARGE_INTEGER liSize = { 0 }; + if (!GetFileSizeEx(GetFileHandle(), &liSize)) + throw std::runtime_error("Cannot determine current log size"); + + return liSize.QuadPart; + } + + void TLogFile::RotateFile() + { + m_spFileHandle.reset(); + + std::wstring pathNew = m_strLogPath; + if (boost::iends_with(pathNew, L".log")) + pathNew.erase(pathNew.end() - 4, pathNew.end()); + + boost::posix_time::ptime timeNow = boost::posix_time::microsec_clock::local_time(); + boost::posix_time::wtime_facet* facet = new boost::posix_time::wtime_facet(); + facet->format(L"%Y%m%d%H%M%S%f"); + std::wstringstream stream; + stream.imbue(std::locale(std::locale::classic(), facet)); + stream << timeNow; + pathNew += L"."; + pathNew += stream.str().c_str(); + pathNew += L".log"; + + if (!MoveFile(m_strLogPath.c_str(), pathNew.c_str()) && GetLastError() != ERROR_FILE_NOT_FOUND) + throw std::runtime_error("Cannot rotate file"); + + m_vRotatedFiles.push_back(std::move(pathNew)); + RemoveObsoleteRotatedLogs(); + } + + void TLogFile::RemoveObsoleteRotatedLogs() + { + while (m_vRotatedFiles.size() > m_spRotationInfo->GetMaxRotatedCount()) + { + auto iterRotatedFile = m_vRotatedFiles.begin(); + if (!DeleteFile(iterRotatedFile->c_str())) + break; + + m_vRotatedFiles.erase(iterRotatedFile); + } + } + + void TLogFile::ScanForRotatedLogs() + { + std::wstring strSearchMask = m_strLogPath; + std::wstring strDir; + + size_t stDirPos = strSearchMask.find_last_of(L"\\/"); + if (stDirPos != std::wstring::npos) + strDir = strSearchMask.substr(0, stDirPos + 1); + + if (boost::iends_with(strSearchMask, L".log")) + strSearchMask.erase(strSearchMask.end() - 4, strSearchMask.end()); + strSearchMask += L".*.log"; + + std::vector vPaths; + WIN32_FIND_DATA wfd = { 0 }; + + HANDLE hFind = FindFirstFile(strSearchMask.c_str(), &wfd); + BOOL bFound = (hFind != INVALID_HANDLE_VALUE); + while (bFound) + { + std::wstring strLogFullPath = strDir + wfd.cFileName; + vPaths.push_back(strLogFullPath); + + bFound = FindNextFile(hFind, &wfd); + } + + std::sort(vPaths.begin(), vPaths.end(), [](const std::wstring& path1, const std::wstring& path2) { return boost::ilexicographical_compare(path1, path2); }); + std::swap(m_vRotatedFiles, vPaths); + } + + bool TLogFile::NeedRotation(size_t stDataSize) + { + unsigned long long ullCurrentSize = GetCurrentLogSize(); + unsigned long long ullMaxLogSize = m_spRotationInfo->GetMaxLogSize(); + + return ullCurrentSize + stDataSize > ullMaxLogSize; + } + } +}