mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 07:50:05 +00:00 
			
		
		
		
	Add file logging to local application data directory
This commit is contained in:
		
							parent
							
								
									41d47878e3
								
							
						
					
					
						commit
						32cd672a40
					
				
					 6 changed files with 255 additions and 47 deletions
				
			
		
							
								
								
									
										178
									
								
								scwx-qt/source/scwx/qt/manager/log_manager.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								scwx-qt/source/scwx/qt/manager/log_manager.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,178 @@ | |||
| #include <scwx/qt/manager/log_manager.hpp> | ||||
| #include <scwx/util/logger.hpp> | ||||
| 
 | ||||
| #include <filesystem> | ||||
| #include <map> | ||||
| #include <ranges> | ||||
| #include <unordered_map> | ||||
| 
 | ||||
| #include <boost/process/environment.hpp> | ||||
| #include <fmt/format.h> | ||||
| #include <spdlog/spdlog.h> | ||||
| #include <QStandardPaths> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
| namespace qt | ||||
| { | ||||
| namespace manager | ||||
| { | ||||
| 
 | ||||
| static const std::string logPrefix_ = "scwx::qt::manager::log_manager"; | ||||
| static const auto        logger_    = scwx::util::Logger::Create(logPrefix_); | ||||
| 
 | ||||
| static void QtLogMessageHandler(QtMsgType                 messageType, | ||||
|                                 const QMessageLogContext& context, | ||||
|                                 const QString&            message); | ||||
| 
 | ||||
| class LogManager::Impl | ||||
| { | ||||
| public: | ||||
|    explicit Impl() {} | ||||
|    ~Impl() {} | ||||
| 
 | ||||
|    void PruneLogFiles(); | ||||
| 
 | ||||
|    std::string logPath_ {}; | ||||
|    std::string logFile_ {}; | ||||
|    int         pid_ {}; | ||||
| }; | ||||
| 
 | ||||
| LogManager::LogManager() : p(std::make_unique<Impl>()) {} | ||||
| LogManager::~LogManager() = default; | ||||
| 
 | ||||
| void LogManager::Initialize() | ||||
| { | ||||
|    // Initialize logger
 | ||||
|    scwx::util::Logger::Initialize(); | ||||
|    spdlog::set_level(spdlog::level::debug); | ||||
| 
 | ||||
|    // Install Qt Message Handler
 | ||||
|    qInstallMessageHandler(&QtLogMessageHandler); | ||||
| } | ||||
| 
 | ||||
| void LogManager::InitializeLogFile() | ||||
| { | ||||
|    p->logPath_ = | ||||
|       QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) | ||||
|          .toStdString(); | ||||
|    p->pid_     = boost::this_process::get_id(); | ||||
|    p->logFile_ = fmt::format("{}/supercell-wx.{}.log", p->logPath_, p->pid_); | ||||
| 
 | ||||
|    // Create log directory if it doesn't exist
 | ||||
|    if (!std::filesystem::exists(p->logPath_)) | ||||
|    { | ||||
|       if (!std::filesystem::create_directories(p->logPath_)) | ||||
|       { | ||||
|          logger_->error("Unable to create log directory: \"{}\"", p->logPath_); | ||||
|          return; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    scwx::util::Logger::AddFileSink(p->logFile_); | ||||
| 
 | ||||
|    p->PruneLogFiles(); | ||||
| } | ||||
| 
 | ||||
| void LogManager::Impl::PruneLogFiles() | ||||
| { | ||||
|    using namespace std::chrono_literals; | ||||
| 
 | ||||
|    static constexpr std::size_t kMaxLogFiles_        = 5; | ||||
|    static constexpr auto        kMinModificationAge_ = 30min; | ||||
| 
 | ||||
|    std::multimap<std::filesystem::file_time_type, std::filesystem::path> | ||||
|       logFiles {}; | ||||
| 
 | ||||
|    // Find existing log files in log directory
 | ||||
|    for (auto& file : std::filesystem::directory_iterator(logPath_)) | ||||
|    { | ||||
|       const std::string filename = file.path().filename().string(); | ||||
| 
 | ||||
|       if (file.is_regular_file() && filename.starts_with("supercell-wx.") && | ||||
|           filename.ends_with(".log")) | ||||
|       { | ||||
|          logger_->trace("Found log file: {}", filename); | ||||
| 
 | ||||
|          try | ||||
|          { | ||||
|             auto lastWriteTime = std::filesystem::last_write_time(file); | ||||
|             logFiles.insert({lastWriteTime, file}); | ||||
|          } | ||||
|          catch (const std::exception&) | ||||
|          { | ||||
|             logger_->error("Error getting last write time of file: {}", | ||||
|                            file.path().string()); | ||||
|          } | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    // Clean up old log files
 | ||||
|    auto now                   = std::filesystem::file_time_type::clock::now(); | ||||
|    auto modificationThreshold = now - kMinModificationAge_; | ||||
| 
 | ||||
|    for (auto& logFile : | ||||
|         logFiles | std::views::reverse | std::views::drop(kMaxLogFiles_)) | ||||
|    { | ||||
|       if (logFile.first < modificationThreshold) | ||||
|       { | ||||
|          const std::string filename = logFile.second.filename().string(); | ||||
| 
 | ||||
|          logger_->info("Removing old log file: {}", filename); | ||||
| 
 | ||||
|          std::error_code ec; | ||||
|          if (!std::filesystem::remove(logFile.second, ec)) | ||||
|          { | ||||
|             logger_->warn( | ||||
|                "Could not remove file: {}, {}", filename, ec.message()); | ||||
|          } | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| LogManager& LogManager::Instance() | ||||
| { | ||||
|    static LogManager LogManager_ {}; | ||||
|    return LogManager_; | ||||
| } | ||||
| 
 | ||||
| void QtLogMessageHandler(QtMsgType                 messageType, | ||||
|                          const QMessageLogContext& context, | ||||
|                          const QString&            message) | ||||
| { | ||||
|    static const auto qtLogger_ = scwx::util::Logger::Create("qt"); | ||||
| 
 | ||||
|    static const std::unordered_map<QtMsgType, spdlog::level::level_enum> | ||||
|       levelMap_ {{QtMsgType::QtDebugMsg, spdlog::level::level_enum::debug}, | ||||
|                  {QtMsgType::QtInfoMsg, spdlog::level::level_enum::info}, | ||||
|                  {QtMsgType::QtWarningMsg, spdlog::level::level_enum::warn}, | ||||
|                  {QtMsgType::QtCriticalMsg, spdlog::level::level_enum::err}, | ||||
|                  {QtMsgType::QtFatalMsg, spdlog::level::level_enum::critical}}; | ||||
| 
 | ||||
|    spdlog::level::level_enum level = spdlog::level::level_enum::info; | ||||
|    auto                      it    = levelMap_.find(messageType); | ||||
|    if (it != levelMap_.cend()) | ||||
|    { | ||||
|       level = it->second; | ||||
|    } | ||||
| 
 | ||||
|    spdlog::source_loc location {}; | ||||
|    if (context.file != nullptr && context.function != nullptr) | ||||
|    { | ||||
|       location = {context.file, context.line, context.function}; | ||||
|    } | ||||
| 
 | ||||
|    if (context.category != nullptr) | ||||
|    { | ||||
|       qtLogger_->log( | ||||
|          location, level, "[{}] {}", context.category, message.toStdString()); | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       qtLogger_->log(location, level, message.toStdString()); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| } // namespace manager
 | ||||
| } // namespace qt
 | ||||
| } // namespace scwx
 | ||||
							
								
								
									
										36
									
								
								scwx-qt/source/scwx/qt/manager/log_manager.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								scwx-qt/source/scwx/qt/manager/log_manager.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,36 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| #include <QObject> | ||||
| #include <QThread> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
| namespace qt | ||||
| { | ||||
| namespace manager | ||||
| { | ||||
| 
 | ||||
| class LogManager : public QObject | ||||
| { | ||||
|    Q_OBJECT | ||||
|    Q_DISABLE_COPY_MOVE(LogManager) | ||||
| 
 | ||||
| public: | ||||
|    explicit LogManager(); | ||||
|    ~LogManager(); | ||||
| 
 | ||||
|    void Initialize(); | ||||
|    void InitializeLogFile(); | ||||
| 
 | ||||
|    static LogManager& Instance(); | ||||
| 
 | ||||
| private: | ||||
|    class Impl; | ||||
|    std::unique_ptr<Impl> p; | ||||
| }; | ||||
| 
 | ||||
| } // namespace manager
 | ||||
| } // namespace qt
 | ||||
| } // namespace scwx
 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat