diff --git a/libs/logger/include/Logger.hpp b/libs/logger/include/Logger.hpp index 064d3a1..1b45bea 100644 --- a/libs/logger/include/Logger.hpp +++ b/libs/logger/include/Logger.hpp @@ -1,12 +1,122 @@ #pragma once +#include +#include +#include +#include #include +#include + +#define COLOR_RESET "\033[0m" +#define COLOR_FATAL "\033[38;5;208m" +#define COLOR_ERROR "\033[38;5;160m" +#define COLOR_WARNING "\033[38;5;226m" +#define COLOR_INFO "\033[38;5;13m" +#define COLOR_DEBUG "\033[38;5;21m" +#define COLOR_GRAY "\033[38;5;232m" namespace Logger { -void Log(const std::string& message); +/** + * @brief Enum describing the severity of the log. + */ +enum class LogType +{ + Fatal, + Error, + Warning, + Info, + Debug, +}; + +/** + * @brief Enum describing how much to log. + * + * @note Debug: Logs all log types. + * Normal: Logs everything except debug. + * Sparse: Only logs Error, Fatal and warnings. + * None: self explanatory. + */ +enum class LogLevel +{ + None, + Sparse, + Normal, + Debug, +}; + +/** + * @brief Singleton logger for a unified output. + * + * @note Debug output will be logged onto the stdout, other logs will be sent to the syslog. + */ +class LogInterface +{ + const bool _enableLoggingStdout; + LogLevel _logLevel; + + /** + * @brief Hidden default constructor. + */ + LogInterface(const char* processName, LogLevel logLevel, bool enableLoggingStdout); + + /** + * @brief Deleted constructor and operator. + */ + LogInterface(const LogInterface&) = delete; + std::unique_ptr& operator=(const LogInterface&) = delete; + + /** + * @brief Returns true or false based on the given log leven and how the Interface is + * initialzed. + */ + bool CanLogLogWithSetLogLevel(LogType logType); + + /** + * @brief Returns a string literal containing the color meant for the given LogType. + */ + constexpr const char* GetLogColor(LogType LogType); + +public: + /** + * @brief Destructor. + */ + ~LogInterface(); + + /** + * @return The singleton instance. + */ + static std::unique_ptr& GetInstance(); + + /** + * @brief Initializes the instance, logging without initializing is not possible. + * + * @param processName The name of the process. + * + * @param logLevel The level at which output will be shown, + * see the LogLevel enum for more details. + * + * @param enableLoggingStdout When enabled, will log the non-debug messages to the + * stdout as well as the syslog. + */ + static void Initialize(const char* processName, LogLevel logLevel, bool enableLoggingStdout); + + /** + * @brief Function used for logging a message in the format for the LogType. + */ + void Log(const char* logMessage, const LogType logType); +}; -} +} /* namespace Logger */ -#define LOG_DEBUG(logMessage) Logger::Log(logMessage); +#define LOG_ERROR(logMessage) \ + Logger::LogInterface::GetInstance()->Log(logMessage, Logger::LogType::Error); +#define LOG_FATAL(logMessage) \ + Logger::LogInterface::GetInstance()->Log(logMessage, Logger::LogType::Fatal); +#define LOG_WARNING(logMessage) \ + Logger::LogInterface::GetInstance()->Log(logMessage, Logger::LogType::Warning); +#define LOG_INFO(logMessage) \ + Logger::LogInterface::GetInstance()->Log(logMessage, Logger::LogType::Info); +#define LOG_DEBUG(logMessage) \ + Logger::LogInterface::GetInstance()->Log(logMessage, Logger::LogType::Debug); diff --git a/libs/logger/src/Logger.cpp b/libs/logger/src/Logger.cpp index 345abc7..be190ad 100644 --- a/libs/logger/src/Logger.cpp +++ b/libs/logger/src/Logger.cpp @@ -1,10 +1,110 @@ #include #include +#include namespace Logger { -void Log(const std::string& message) +LogInterface::LogInterface(const char* processName, LogLevel logLevel, bool enableLoggingStdout) + : _logLevel(logLevel), _enableLoggingStdout(enableLoggingStdout) { - std::cout << message << std::endl; + openlog(processName, LOG_NOWAIT, LOG_NOWAIT); } + +LogInterface::~LogInterface() +{ + try { + closelog(); + } catch (const std::exception& e) { + std::cerr << e.what() << '\n'; + } +} + +std::unique_ptr& LogInterface::GetInstance() +{ + static std::unique_ptr logger; + return logger; +} + +void LogInterface::Initialize(const char* processName, LogLevel logLevel, bool enableLoggingStdout) +{ + auto& logger = GetInstance(); + + if (logger != nullptr) { + throw std::runtime_error("Logger is already initialized."); + } + + logger = + std::unique_ptr(new LogInterface(processName, logLevel, enableLoggingStdout)); +} + +bool LogInterface::CanLogLogWithSetLogLevel(LogType logType) +{ + switch (_logLevel) { + case LogLevel::None: + return false; + case LogLevel::Sparse: + return (logType != LogType::Debug && logType != LogType::Info); + case LogLevel::Normal: + return (logType != LogType::Debug); + case LogLevel::Debug: + return true; + default: + return false; + } +} + +constexpr const char* LogInterface::GetLogColor(LogType LogType) +{ + switch (LogType) { + case LogType::Fatal: + return COLOR_FATAL; + case LogType::Error: + return COLOR_ERROR; + case LogType::Warning: + return COLOR_WARNING; + case LogType::Info: + return COLOR_INFO; + case LogType::Debug: + return COLOR_DEBUG; + default: + return ""; + } +} + +void LogInterface::Log(const char* logMessage, const LogType logType) +{ + if (!CanLogLogWithSetLogLevel(logType)) { + return; + } + + if (_enableLoggingStdout || logType == LogType::Debug) { + std::cout << GetLogColor(logType) << logMessage << COLOR_RESET << "\n"; + } + + try { + switch (logType) { + case LogType::Fatal: + syslog(LOG_DAEMON | LOG_ERR, "%s", logMessage); + break; + case LogType::Error: + syslog(LOG_DAEMON | LOG_ERR, "%s", logMessage); + break; + case LogType::Warning: + syslog(LOG_DAEMON | LOG_WARNING, "%s", logMessage); + break; + case LogType::Info: + syslog(LOG_DAEMON | LOG_INFO, "%s", logMessage); + break; + case LogType::Debug: + syslog(LOG_DAEMON | LOG_DEBUG, "%s", logMessage); + break; + default: + break; + } + } catch (const std::exception& e) { + std::cerr << GetLogColor(LogType::Error) << "Error using syslog: " << e.what() + << COLOR_RESET "\n"; + } +} + } // namespace Logger diff --git a/taskmasterd/CMakeLists.txt b/taskmasterd/CMakeLists.txt index 36f3767..911f6f4 100644 --- a/taskmasterd/CMakeLists.txt +++ b/taskmasterd/CMakeLists.txt @@ -11,14 +11,17 @@ add_executable(${EXECUTABLE_NAME}) file(GLOB_RECURSE SOURCES "src/*.cpp") # Add compile options -set(COMPILE_OPTIONS -Wall -Wextra -Werror -pedantic -O3 -march=native) -add_compile_options(${COMPILE_OPTIONS}) +set(COMPILE_OPTIONS -Wall -Wextra -Werror -pedantic -O3 -march=native -g -DPROGRAM_NAME="${EXECUTABLE_NAME}") +target_compile_options(${EXECUTABLE_NAME} PRIVATE ${COMPILE_OPTIONS}) + +# Enable fsanitize: +# target_link_options(${EXECUTABLE_NAME} PRIVATE -fsanitize=address) # Link to the needed libs target_link_libraries(${EXECUTABLE_NAME} PRIVATE logger) # add executable sources -target_sources(${EXECUTABLE_NAME} PRIVATE ${SOURCES} main.cpp) +target_sources(${EXECUTABLE_NAME} PRIVATE ${SOURCES}) # include header directory target_include_directories(${EXECUTABLE_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..) diff --git a/taskmasterd/src/main.cpp b/taskmasterd/src/main.cpp index 7e3b6ad..16029ce 100644 --- a/taskmasterd/src/main.cpp +++ b/taskmasterd/src/main.cpp @@ -1,15 +1,22 @@ #include #include +#include + +#ifndef PROGRAM_NAME +#define PROGRAM_NAME "taskmasterd" +#endif int main(int argc, char** argv) { - testFunc(); + (void) argc; + (void) argv; + Logger::LogInterface::Initialize(PROGRAM_NAME, Logger::LogLevel::Debug, true); - if (argc > 2) { - LOG_DEBUG("argc > 2") - return 1; - } + LOG_DEBUG("Debug log") + LOG_ERROR("Error log") + LOG_FATAL("Fatal log") + LOG_WARNING("Warning log") + LOG_INFO("Info log") - LOG_DEBUG("argc <= 2") return 0; }