From 1ddd1eda2d57bd7a4e755e0dfc3377902cd03b17 Mon Sep 17 00:00:00 2001 From: kongr45gpen <electrovesta@gmail.com> Date: Wed, 7 Aug 2019 17:01:02 +0300 Subject: [PATCH] Use streams for logging to allow displaying different variables --- CMakeLists.txt | 1 + inc/Logger.hpp | 69 +++++++++++++++++++++++++++---------- inc/Service.hpp | 3 -- src/Logger.cpp | 16 +++++++++ src/Platform/x86/Logger.cpp | 2 +- 5 files changed, 69 insertions(+), 22 deletions(-) create mode 100644 src/Logger.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b8d5802a..ab4a0c23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ add_custom_target(check add_library(common OBJECT src/Service.cpp src/ErrorHandler.cpp + src/Logger.cpp src/Message.cpp src/MessageParser.cpp src/ServicePool.cpp diff --git a/inc/Logger.hpp b/inc/Logger.hpp index e5b51a6e..3f8a4335 100644 --- a/inc/Logger.hpp +++ b/inc/Logger.hpp @@ -3,37 +3,41 @@ #include <cstdint> #include <etl/String.hpp> +#include <etl/to_string.h> #include <ECSS_Definitions.hpp> #if defined LOGLEVEL_TRACE - #define LOGLEVEL Logger::trace +#define LOGLEVEL Logger::trace #elif defined LOGLEVEL_DEBUG - #define LOGLEVEL Logger::debug +#define LOGLEVEL Logger::debug #elif defined LOGLEVEL_INFO - #define LOGLEVEL Logger::info +#define LOGLEVEL Logger::info #elif defined LOGLEVEL_NOTICE - #define LOGLEVEL Logger::notice +#define LOGLEVEL Logger::notice #elif defined LOGLEVEL_WARNING - #define LOGLEVEL Logger::warning +#define LOGLEVEL Logger::warning #elif defined LOGLEVEL_ERROR - #define LOGLEVEL Logger::error +#define LOGLEVEL Logger::error #elif defined LOGLEVEL_EMERGENCY - #define LOGLEVEL Logger::emergency +#define LOGLEVEL Logger::emergency #else - #define LOGLEVEL Logger::disabled +#define LOGLEVEL Logger::disabled #endif #define _ac_LOGGER_ENABLED_LEVEL(level) (( (Logger::LogLevelType) LOGLEVEL) <= ( (Logger::LogLevelType) level)) -#define LOG(level, message) if (_ac_LOGGER_ENABLED_LEVEL(level)) { Logger::log(level, message); } +#define LOG(level) \ + if (_ac_LOGGER_ENABLED_LEVEL(level)) \ + if (Logger::LogEntry entry(level); true) \ + entry -#define LOG_TRACE(message) LOG(Logger::trace, message) -#define LOG_DEBUG(message) LOG(Logger::debug, message) -#define LOG_INFO(message) LOG(Logger::info, message) -#define LOG_NOTICE(message) LOG(Logger::notice, message) -#define LOG_WARNING(message) LOG(Logger::warning, message) -#define LOG_ERROR(message) LOG(Logger::error, message) -#define LOG_EMERGENCY(message) LOG(Logger::emergency, message) +#define LOG_TRACE LOG(Logger::trace) +#define LOG_DEBUG LOG(Logger::debug) +#define LOG_INFO LOG(Logger::info) +#define LOG_NOTICE LOG(Logger::notice) +#define LOG_WARNING LOG(Logger::warning) +#define LOG_ERROR LOG(Logger::error) +#define LOG_EMERGENCY LOG(Logger::emergency) /** * A logging class for ECSS Services that supports ETL's String and is lightweight enough to be used in embedded @@ -72,10 +76,31 @@ public: disabled = 255, ///< Use this log level to disable logging entirely. No message should be logged as disabled. }; + /** + * A class that defines a log message. + * + * Instead of using this class, prefer one of the above macros + */ + struct LogEntry { + String<LOGGER_MAX_MESSAGE_SIZE> message = ""; ///< The current log message itself + etl::format_spec format; ///< ETL's string format specification + LogLevel level; + + explicit LogEntry(LogLevel level); ///< Create a new LogEntry + + /** + * The LogEntry destructor gets called whenever a log message is finalized, and ready to be shown to the + * user. This function is responsible for calling the Logger::log function. + */ + ~LogEntry(); + + LogEntry(LogEntry const&) = delete; ///< Unimplemented copy constructor + }; + /** * @brief Unimplemented copy constructor * - * Does not allow Loggers should be copied. There should be only one instance for each Logger. + * Does not allow Loggers to be copied. There should be only one instance for each Logger. */ Logger(Logger const&) = delete; @@ -104,7 +129,15 @@ public: /** * Store a new log message */ - static void log(LogLevel level, String<LOGGER_MAX_MESSAGE_SIZE> message); + static void log(LogLevel level, String<LOGGER_MAX_MESSAGE_SIZE>& message); }; +template <class T> +Logger::LogEntry& operator<<(Logger::LogEntry& entry, const T value) { + etl::to_string(value, entry.message, entry.format, true); + + return entry; +} + + #endif //ECSS_SERVICES_LOGGER_HPP diff --git a/inc/Service.hpp b/inc/Service.hpp index 32e77e19..d7a8ee43 100644 --- a/inc/Service.hpp +++ b/inc/Service.hpp @@ -3,7 +3,6 @@ #include <cstdint> #include "Message.hpp" -#include <iostream> // This file should be removed class ServicePool; @@ -12,8 +11,6 @@ class ServicePool; * * A member of the Service class should be used as a singleton, i.e. must be created only once in * the code - * - * @todo Disable copy constructor */ class Service { private: diff --git a/src/Logger.cpp b/src/Logger.cpp new file mode 100644 index 00000000..aba0f7a0 --- /dev/null +++ b/src/Logger.cpp @@ -0,0 +1,16 @@ +#include <Logger.hpp> + +template <> +Logger::LogEntry& operator<<(Logger::LogEntry& entry, const char* value) { + entry.message.append(value); + + return entry; +} + +Logger::LogEntry::LogEntry(LogLevel level) : level(level) { + format.precision(3); +} + +Logger::LogEntry::~LogEntry() { + Logger::log(level, message); +} \ No newline at end of file diff --git a/src/Platform/x86/Logger.cpp b/src/Platform/x86/Logger.cpp index f97705ea..554cd3f7 100644 --- a/src/Platform/x86/Logger.cpp +++ b/src/Platform/x86/Logger.cpp @@ -7,7 +7,7 @@ #include <iomanip> // The implementation of this function appends ANSI codes that should add colours to a compatible terminal -void Logger::log(Logger::LogLevel level, String<LOGGER_MAX_MESSAGE_SIZE> message) { +void Logger::log(Logger::LogLevel level, String<LOGGER_MAX_MESSAGE_SIZE> & message) { // Get the current time & date std::time_t t = std::time(nullptr); std::tm tm = *std::localtime(&t); -- GitLab