#pragma once #include #include #include #include #include #include #include #include namespace Incart::Common { class SimpleLogger { private: std::recursive_mutex m_lock; std::string m_filePath; std::ofstream m_out; uintmax_t m_maxFileSize; size_t m_freeBlockSize; uintmax_t m_currFileSize = 0; std::string m_tempFilePath; QFileInfo m_fileInfo; public: SimpleLogger(const std::string& filePath, size_t maxFileSize, size_t freeBlockSize) : m_filePath(filePath) , m_maxFileSize(maxFileSize) , m_freeBlockSize(freeBlockSize > maxFileSize ? maxFileSize : freeBlockSize) , m_fileInfo(QString::fromStdString(filePath)) { std::size_t fileNameIndex = m_filePath.find_last_of("/\\"); std::string fileDirectory = (fileNameIndex == std::string::npos) ? "" : m_filePath.substr(0, fileNameIndex + 1); // example: C:/KTWin/ std::string fileName = (fileNameIndex == std::string::npos) ? m_filePath : m_filePath.substr(fileNameIndex + 1); // example: ErrorLog.txt std::size_t fileExtensionIndex = fileName.find_last_of("."); std::string fileNameWithoutExtension = (fileExtensionIndex == std::string::npos) ? fileName : fileName.substr(0, fileExtensionIndex); // example: ErrorLog std::string extension = (fileExtensionIndex == std::string::npos) ? "" : fileName.substr(fileExtensionIndex); // example: .txt m_tempFilePath = fileDirectory + fileNameWithoutExtension + "_temp" + extension; } void write(const std::string& record) { std::string currTime = getCurrTime(); size_t recordSize = currTime.size() + 3 + record.size(); std::lock_guard locker(m_lock); if (isFileExist()) { m_currFileSize = getFileLength(); if (recordSize + m_currFileSize > m_maxFileSize) { createTruncatedFileCopy(); } } else { m_currFileSize = 0; } m_out.open(m_filePath, std::ios::app); if (m_out.is_open()) { m_out << std::dec << currTime << " " << record << std::endl; } m_out.close(); } private: bool isFileExist() { return m_fileInfo.exists(); } uintmax_t getFileLength() { return static_cast(m_fileInfo.size()); } inline std::tm getLocaltime(const std::time_t* timer) const { std::tm bt {}; #if defined(__unix__) localtime_r(timer, &bt); #elif defined(_MSC_VER) localtime_s(&bt, timer); #else static std::mutex mtx; std::lock_guard lock(mtx); bt = *std::localtime(timer); #endif return bt; } void createTruncatedFileCopy() { std::ifstream inStreamFile(m_filePath, std::ios::in); if (!inStreamFile.good()) { return; } std::string line; for (size_t index = 0; (index < m_freeBlockSize) && std::getline(inStreamFile, line); index = inStreamFile.tellg()) { } m_out.open(m_tempFilePath, std::ios::app); while (std::getline(inStreamFile, line)) { m_out << line << std::endl; } m_out.close(); inStreamFile.close(); if (std::remove(m_filePath.c_str()) != 0) { return; } std::rename(m_tempFilePath.c_str(), m_filePath.c_str()); } std::string getCurrTime() const { std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); std::time_t currentTime = std::chrono::system_clock::to_time_t(now); std::chrono::milliseconds now2 = std::chrono::duration_cast(now.time_since_epoch()); std::tm currTime = getLocaltime(¤tTime); char buffer[13]; strftime(buffer, sizeof(buffer), "%X.000", &currTime); sprintf(buffer + 9, "%d", (int)(now2.count() % 1000)); return std::string(buffer); } }; } // namespace Incart::Common