104e438cbSEd Tanous #pragma once 204e438cbSEd Tanous 304e438cbSEd Tanous #include <cstdio> 404e438cbSEd Tanous #include <cstdlib> 504e438cbSEd Tanous #include <ctime> 604e438cbSEd Tanous #include <filesystem> 704e438cbSEd Tanous #include <iostream> 804e438cbSEd Tanous #include <sstream> 904e438cbSEd Tanous #include <string> 1004e438cbSEd Tanous 1104e438cbSEd Tanous namespace crow 1204e438cbSEd Tanous { 1304e438cbSEd Tanous enum class LogLevel 1404e438cbSEd Tanous { 1504e438cbSEd Tanous Debug = 0, 1604e438cbSEd Tanous Info, 1704e438cbSEd Tanous Warning, 1804e438cbSEd Tanous Error, 1904e438cbSEd Tanous Critical, 2004e438cbSEd Tanous }; 2104e438cbSEd Tanous 2204e438cbSEd Tanous class Logger 2304e438cbSEd Tanous { 2404e438cbSEd Tanous private: 2504e438cbSEd Tanous // 2604e438cbSEd Tanous static std::string timestamp() 2704e438cbSEd Tanous { 2804e438cbSEd Tanous std::string date; 2904e438cbSEd Tanous date.resize(32, '\0'); 3004e438cbSEd Tanous time_t t = time(nullptr); 3104e438cbSEd Tanous 3204e438cbSEd Tanous tm myTm{}; 3304e438cbSEd Tanous 3404e438cbSEd Tanous gmtime_r(&t, &myTm); 3504e438cbSEd Tanous 3604e438cbSEd Tanous size_t sz = 3704e438cbSEd Tanous strftime(date.data(), date.size(), "%Y-%m-%d %H:%M:%S", &myTm); 3804e438cbSEd Tanous date.resize(sz); 3904e438cbSEd Tanous return date; 4004e438cbSEd Tanous } 4104e438cbSEd Tanous 4204e438cbSEd Tanous public: 4304e438cbSEd Tanous Logger([[maybe_unused]] const std::string& prefix, 4404e438cbSEd Tanous [[maybe_unused]] const std::string& filename, 4504e438cbSEd Tanous [[maybe_unused]] const size_t line, LogLevel levelIn) : 4604e438cbSEd Tanous level(levelIn) 4704e438cbSEd Tanous { 4804e438cbSEd Tanous #ifdef BMCWEB_ENABLE_LOGGING 4904e438cbSEd Tanous stringstream << "(" << timestamp() << ") [" << prefix << " " 5004e438cbSEd Tanous << std::filesystem::path(filename).filename() << ":" 5104e438cbSEd Tanous << line << "] "; 5204e438cbSEd Tanous #endif 5304e438cbSEd Tanous } 5404e438cbSEd Tanous ~Logger() 5504e438cbSEd Tanous { 5604e438cbSEd Tanous if (level >= getCurrentLogLevel()) 5704e438cbSEd Tanous { 5804e438cbSEd Tanous #ifdef BMCWEB_ENABLE_LOGGING 5904e438cbSEd Tanous stringstream << std::endl; 6004e438cbSEd Tanous std::cerr << stringstream.str(); 6104e438cbSEd Tanous #endif 6204e438cbSEd Tanous } 6304e438cbSEd Tanous } 6404e438cbSEd Tanous 65ecd6a3a2SEd Tanous Logger(const Logger&) = delete; 66ecd6a3a2SEd Tanous Logger(Logger&&) = delete; 67ecd6a3a2SEd Tanous Logger& operator=(const Logger&) = delete; 68ecd6a3a2SEd Tanous Logger& operator=(const Logger&&) = delete; 69ecd6a3a2SEd Tanous 7004e438cbSEd Tanous // 7104e438cbSEd Tanous template <typename T> 7204e438cbSEd Tanous Logger& operator<<([[maybe_unused]] T const& value) 7304e438cbSEd Tanous { 7404e438cbSEd Tanous if (level >= getCurrentLogLevel()) 7504e438cbSEd Tanous { 7604e438cbSEd Tanous #ifdef BMCWEB_ENABLE_LOGGING 77*9b6ffca5SEd Tanous // Somewhere in the code we're implicitly casting an array to a 78*9b6ffca5SEd Tanous // pointer in logging code. It's non-trivial to find, so disable 79*9b6ffca5SEd Tanous // the check here for now 80*9b6ffca5SEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) 8104e438cbSEd Tanous stringstream << value; 8204e438cbSEd Tanous #endif 8304e438cbSEd Tanous } 8404e438cbSEd Tanous return *this; 8504e438cbSEd Tanous } 8604e438cbSEd Tanous 8704e438cbSEd Tanous // 8804e438cbSEd Tanous static void setLogLevel(LogLevel level) 8904e438cbSEd Tanous { 9004e438cbSEd Tanous getLogLevelRef() = level; 9104e438cbSEd Tanous } 9204e438cbSEd Tanous 9304e438cbSEd Tanous static LogLevel getCurrentLogLevel() 9404e438cbSEd Tanous { 9504e438cbSEd Tanous return getLogLevelRef(); 9604e438cbSEd Tanous } 9704e438cbSEd Tanous 9804e438cbSEd Tanous private: 9904e438cbSEd Tanous // 10004e438cbSEd Tanous static LogLevel& getLogLevelRef() 10104e438cbSEd Tanous { 10204e438cbSEd Tanous static auto currentLevel = static_cast<LogLevel>(1); 10304e438cbSEd Tanous return currentLevel; 10404e438cbSEd Tanous } 10504e438cbSEd Tanous 10604e438cbSEd Tanous // 10704e438cbSEd Tanous std::ostringstream stringstream; 10804e438cbSEd Tanous LogLevel level; 10904e438cbSEd Tanous }; 11004e438cbSEd Tanous } // namespace crow 11104e438cbSEd Tanous 112600d2394SEd Tanous // The logging functions currently use macros. Now that we have c++20, ideally 113600d2394SEd Tanous // they'd use source_location with fixed functions, but for the moment, disable 114600d2394SEd Tanous // the check. 115600d2394SEd Tanous 116600d2394SEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 11704e438cbSEd Tanous #define BMCWEB_LOG_CRITICAL \ 11804e438cbSEd Tanous if (crow::Logger::getCurrentLogLevel() <= crow::LogLevel::Critical) \ 11904e438cbSEd Tanous crow::Logger("CRITICAL", __FILE__, __LINE__, crow::LogLevel::Critical) 120600d2394SEd Tanous 121600d2394SEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 12204e438cbSEd Tanous #define BMCWEB_LOG_ERROR \ 12304e438cbSEd Tanous if (crow::Logger::getCurrentLogLevel() <= crow::LogLevel::Error) \ 12404e438cbSEd Tanous crow::Logger("ERROR", __FILE__, __LINE__, crow::LogLevel::Error) 125600d2394SEd Tanous 126600d2394SEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 12704e438cbSEd Tanous #define BMCWEB_LOG_WARNING \ 12804e438cbSEd Tanous if (crow::Logger::getCurrentLogLevel() <= crow::LogLevel::Warning) \ 12904e438cbSEd Tanous crow::Logger("WARNING", __FILE__, __LINE__, crow::LogLevel::Warning) 130600d2394SEd Tanous 131600d2394SEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 13204e438cbSEd Tanous #define BMCWEB_LOG_INFO \ 13304e438cbSEd Tanous if (crow::Logger::getCurrentLogLevel() <= crow::LogLevel::Info) \ 13404e438cbSEd Tanous crow::Logger("INFO", __FILE__, __LINE__, crow::LogLevel::Info) 135600d2394SEd Tanous 136600d2394SEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 13704e438cbSEd Tanous #define BMCWEB_LOG_DEBUG \ 13804e438cbSEd Tanous if (crow::Logger::getCurrentLogLevel() <= crow::LogLevel::Debug) \ 13904e438cbSEd Tanous crow::Logger("DEBUG", __FILE__, __LINE__, crow::LogLevel::Debug) 140