#pragma once #include #include #include #include #include #include #include #include // For snprintf #include // For std::unique_ptr namespace custom { enum class LogLevel { DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3 }; class Logger { public: static Logger& getInstance(); void setLevel(LogLevel level); void setLogFile(const std::string& filename); void log(LogLevel level, const std::string& message); void debug(const std::string& message); void info(const std::string& message); void warn(const std::string& message); void error(const std::string& message); template void log(LogLevel level, const char* format, Args... args); template void debug(const char* format, Args... args); template void info(const char* format, Args... args); template void warn(const char* format, Args... args); template void error(const char* format, Args... args); private: Logger() = default; ~Logger() = default; Logger(const Logger&) = delete; Logger& operator=(const Logger&) = delete; std::string getCurrentTime(); std::string levelToString(LogLevel level); LogLevel currentLevel_ = LogLevel::INFO; std::unique_ptr logFile_; std::mutex logMutex_; }; // Convenience macros #define LOG_DEBUG(msg, ...) custom::Logger::getInstance().debug(msg, ##__VA_ARGS__) #define LOG_INFO(msg, ...) custom::Logger::getInstance().info(msg, ##__VA_ARGS__) #define LOG_WARN(msg, ...) custom::Logger::getInstance().warn(msg, ##__VA_ARGS__) #define LOG_ERROR(msg, ...) custom::Logger::getInstance().error(msg, ##__VA_ARGS__) template void Logger::log(LogLevel level, const char* format, Args... args) { if (level < currentLevel_) { return; } // A buffer to hold the formatted string. // First, we try with a small buffer. char buffer[256]; int size = snprintf(buffer, sizeof(buffer), format, args...); if (size < 0) { // Encoding error error("Encoding error in log message."); return; } std::string message; if (size < sizeof(buffer)) { // The message fit in the buffer. message = buffer; } else { // The buffer was too small, we need to allocate a larger one. std::unique_ptr dynamic_buffer(new char[size + 1]); int new_size = snprintf(dynamic_buffer.get(), size + 1, format, args...); if (new_size >= 0 && new_size <= size) { message = dynamic_buffer.get(); } else { error("Encoding error in log message after buffer resize."); return; } } log(level, message); } template void Logger::debug(const char* format, Args... args) { log(LogLevel::DEBUG, format, args...); } template void Logger::info(const char* format, Args... args) { log(LogLevel::INFO, format, args...); } template void Logger::warn(const char* format, Args... args) { log(LogLevel::WARN, format, args...); } template void Logger::error(const char* format, Args... args) { log(LogLevel::ERROR, format, args...); } } // namespace custom