diff --git a/CMakeLists.txt b/CMakeLists.txt index d06bc48..87ae4bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,5 +3,4 @@ project(cpp-logger VERSION 0.1.0 LANGUAGES C CXX) # include_directories(logger) set(CMAKE_CXX_STANDARD 20) add_executable(cpplogger main.cpp - logger.h - manager.h) + logger.h) diff --git a/logger.h b/logger.h index 5877daf..9411c93 100644 --- a/logger.h +++ b/logger.h @@ -12,8 +12,8 @@ #include #include #include -#include "manager.h" #include +#include // Required for std::runtime_error // Handle file system for specific platform #ifdef _WIN32 @@ -91,7 +91,7 @@ class Logger { public: Logger(const std::string name, const std::string &root_dir, const std::string &file_name, const bool debug = false) { - this->name = name; + this->name = name; this->root_dir = root_dir; this->file_name = file_name; this->debug = debug; @@ -120,12 +120,22 @@ class Logger { this->log_file.open(this->root_dir + "/" + file_name); } + // Check if the log file was opened successfully + if (!this->log_file.is_open()) { + throw std::runtime_error("Failed to open log file at: " + (this->root_dir[this->root_dir.size() - 1] == '/' || this->root_dir[this->root_dir.size() - 1] == '\\' ? this->root_dir : this->root_dir + "/") + file_name); + } + // update the status of logger this->log_file << ">>> Logger " << this->name << " initiated at " << std::chrono::system_clock::now() << " <<<\n"; std::cout << LOGGER::COLOR::CYAN << ">>> Logger " << this->name << " initiated at " << std::chrono::system_clock::now() << " <<<" << LOGGER::COLOR::RESET << "\n"; - // Register the logger to manager buffer - BUF_MANAGER::register_buf(this); + } + + // Destructor to ensure exit_logger is called + ~Logger() { + if (is_active()) { + exit_logger(); + } } // Check if the logger is active or running @@ -183,8 +193,20 @@ class Logger { print_log(message, level); } + // Check for invalid log level before accessing handler + if (this->handler.find(level) == this->handler.end()) { + // To prevent recursive error logging if this itself is part of an error log, + // we print to cerr and throw a different, more specific error. + // Or, simply throw, assuming this method won't be called with invalid levels internally. + // For now, let's throw a runtime_error. + // A more advanced strategy might log this specific error to a default/emergency logger + // or use a predefined "UNKNOWN" level if we want to record the attempt. + // However, throwing is safest to indicate misuse of the API. + throw std::runtime_error("Invalid log level used: " + level); + } + this->log_file << std::chrono::system_clock::now() << " " << level << " " << message << "\n"; - this->handler.at(level).count++; + this->handler.at(level).count++; // Now safe to use .at() due to the check above, or use find result. this->log_count++; } @@ -198,7 +220,6 @@ class Logger { this->end_time = std::chrono::system_clock::now(); this->log_file << ">>> Logger " << this->name << " exited at " << this->end_time << " <<<\n"; this->log_file.close(); - BUF_MANAGER::unregister_buf(this); // Unregister the logger from buffer manager std::cout << LOGGER::COLOR::CYAN << ">>> Exited Logger " << this->name << " at " << this->end_time << " <<<" << LOGGER::COLOR::RESET << "\n"; } diff --git a/main.cpp b/main.cpp index 470e2bc..1c1ce5f 100644 --- a/main.cpp +++ b/main.cpp @@ -8,34 +8,58 @@ */ #include "logger.h" -#include +#include // For std::filesystem::path and std::filesystem::current_path() +#include // For std::runtime_error +#include // For std::cerr and std::endl -// Handle file system for specific platform -#ifdef _WIN32 -#include -#else -#include -#endif +// Handle file system for specific platform (getcwd is no longer used) +// #ifdef _WIN32 +// #include +// #else +// #include +// #endif int main() { const std::string name = "main"; - const std::string root_dir = std::string(getcwd(nullptr, 100)) + std::string("/cache"); // Insert absolute path of log file root + // Use std::filesystem for robust path construction + const std::string root_dir = (std::filesystem::current_path() / "cache").string(); const std::string log_file = "test_logger.log"; // Insert log file name const bool debug = true; - // Initiated logger instance - auto logger = Logger(name, root_dir, log_file, debug); - - // add some logs - logger.log("Debug log!", LOGGER::LEVEL::DEBUG); - logger.log("Info log!", LOGGER::LEVEL::INFO); - logger.log("Warning log!", LOGGER::LEVEL::WARNING); - logger.log("Error log!", LOGGER::LEVEL::ERROR); - logger.log("Fatal log!", LOGGER::LEVEL::FATAL); - logger.log("Unknown log!", LOGGER::LEVEL::UNKNOWN); - logger.log("Message log!", LOGGER::LEVEL::MESSAGE); - - // close logger - logger.exit_logger(); - return 0; + try { + // Initiated logger instance + auto logger = Logger(name, root_dir, log_file, debug); + + // add some logs + logger.log("Debug log!", LOGGER::LEVEL::DEBUG); + logger.log("Info log!", LOGGER::LEVEL::INFO); + logger.log("Warning log!", LOGGER::LEVEL::WARNING); + logger.log("Error log!", LOGGER::LEVEL::ERROR); + logger.log("Fatal log!", LOGGER::LEVEL::FATAL); + // logger.log("Unknown log!", LOGGER::LEVEL::UNKNOWN); // This level is valid + logger.log("Message log!", LOGGER::LEVEL::MESSAGE); + + // Example of logging an invalid level to test error handling in logger.log() + // Note: This will throw std::runtime_error due to recent changes in Logger::log() + // logger.log("This is an invalid level!", "INVALID_LEVEL"); + + + // The destructor of logger will call exit_logger() automatically. + // Explicitly calling logger.exit_logger(); is no longer strictly necessary + // if logger is the last thing to be used in this scope. + // However, if you need to ensure logs are flushed before further operations + // not shown here, or before a crash might prevent destructor from running, + // calling it explicitly can still be useful. + // For this example, we'll rely on the destructor. + // logger.exit_logger(); + + } catch (const std::runtime_error& e) { + std::cerr << "Runtime Error: " << e.what() << std::endl; + return 1; // Indicate failure + } catch (...) { + std::cerr << "An unexpected error occurred." << std::endl; + return 1; // Indicate failure + } + + return 0; // Indicate success } diff --git a/manager.h b/manager.h deleted file mode 100644 index 9486a2b..0000000 --- a/manager.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * ************************************************************************ - * Copyright (c) 2025, Dhanush H V. All rights reserved. - * Licensed under the MIT License. See the LICENSE file for more details - * ************************************************************************* - */ - -#ifndef MANAGER_H -#define MANAGER_H - -#include - -template -class BUF_MANAGER { -protected: - // All instance buffers would be registered here - // for the same runtime - static std::string TAG; - static inline std::unordered_set BUFFERS; - -public: - BUF_MANAGER() { - // Initialize the buffer for current runtime - TAG = "[BUF_MANAGER]"; - BUFFERS = std::unordered_set(); - } - - ~BUF_MANAGER() { - // Clear all the buffers before runtime exit - // to avoid memory overflow - BUFFERS.clear(); - } - - // Register a new buffer for the current runtime - static bool register_buf(T* buffer) { - // Register the new buffer to manager - BUFFERS.insert(buffer); - return BUFFERS.contains(buffer); - } - - // Unregister an existing registered buffer - static bool unregister_buf(T* buffer) { - BUFFERS.erase(buffer); // remove an existing buffers from the register - return !BUFFERS.contains(buffer); // check if the register was removed successfully - } - - // Get all the existing buffers in the register - static std::unordered_set get_buffs() { - return BUFFERS; - } - - // Get the total number of registered buffers in the register - static int buf_size() { - return BUFFERS.size(); - } -}; - -#endif //MANAGER_H