diff --git a/.gitmodules b/.gitmodules index 2fc4ae0960502c851fe5afb7c4ffb3290db9f120..c30fec28aea7f8a5584f89122ff9fc8a58a6863a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "ci/page_style/doxygen_dark_theme"] path = ci/page_style/doxygen_dark_theme url = https://github.com/MaJerle/doxygen_dark_theme.git +[submodule "lib/logger"] + path = lib/logger + url = https://gitlab.com/acubesat/obc/logger.git diff --git a/.idea/vcs.xml b/.idea/vcs.xml index b361e80b9ac2795e185ae4f8a0ebeac8becdd851..85a1105c248f8e6e1cc46c1dfc393d31028d0525 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -5,5 +5,6 @@ <mapping directory="$PROJECT_DIR$/ci/page_style/doxygen_dark_theme" vcs="Git" /> <mapping directory="$PROJECT_DIR$/lib/Catch2" vcs="Git" /> <mapping directory="$PROJECT_DIR$/lib/etl" vcs="Git" /> + <mapping directory="$PROJECT_DIR$/lib/logger" vcs="Git" /> </component> -</project> \ No newline at end of file +</project> diff --git a/CMakeLists.txt b/CMakeLists.txt index 51091706143f679881f5743fe96f47001f63aa8a..99ba91acd1eaae9beffe3b64a8f7588c7e0ab9fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,8 @@ project(ecss_services) set(CMAKE_CXX_STANDARD 17) # Specify the directories for #includes -include_directories("${PROJECT_SOURCE_DIR}/inc" "${PROJECT_SOURCE_DIR}/lib/etl/include") +include_directories("${PROJECT_SOURCE_DIR}/inc" "${PROJECT_SOURCE_DIR}/lib/etl/include" + "${PROJECT_SOURCE_DIR}/lib/logger/inc") add_custom_target(check COMMAND ./cppcheck.sh @@ -21,9 +22,9 @@ include_directories(${ECSS_CONFIGURATION}) # Specify the .cpp files common across all targets add_library(common OBJECT + lib/logger/src/Logger.cpp src/Service.cpp src/ErrorHandler.cpp - src/Logger.cpp src/Message.cpp src/MessageParser.cpp src/ServicePool.cpp @@ -45,12 +46,17 @@ add_library(common OBJECT ) # Specify the .cpp files for the executables -file(GLOB x86_main_SRC "src/Platform/x86/*.cpp") +file(GLOB x86_main_SRC "src/Platform/x86/*.cpp" "lib/logger/src/Platform/x86/*.cpp") + add_executable(ecss_services $<TARGET_OBJECTS:common> ${x86_main_SRC} ) +# Logs all levels of messages. This command can be added by other users of this +# library to override the respective log level. +target_compile_definitions(ecss_services PUBLIC LOGLEVEL_TRACE) + IF (EXISTS "${PROJECT_SOURCE_DIR}/lib/Catch2/CMakeLists.txt") # Gather all the .cpp files corresponding to tests file(GLOB test_main_SRC "test/*.cpp") diff --git a/ci/clang-tidy.sh b/ci/clang-tidy.sh index 3ac4810e6a55343c491976dff0e11eac2a146689..07a04e541dfeb0f04d9aa79a28a854f35d791e12 100755 --- a/ci/clang-tidy.sh +++ b/ci/clang-tidy.sh @@ -13,7 +13,7 @@ cd "$(dirname "$0")" GCCVERSION=`g++ -dumpversion` clang-tidy `find ../src/ -type f -regextype posix-egrep -regex '.*\.(cpp|hpp|c|h)'` \ - -extra-arg=-fcolor-diagnostics -- -std=c++17 -I../inc -I../lib/etl/include -I../inc/Platform/x86 \ + -extra-arg=-fcolor-diagnostics -- -std=c++17 -I../inc -I../lib/etl/include -I../inc/Platform/x86 -I../lib/logger/inc/ \ -I/usr/include/c++/$GCCVERSION -I/usr/include/x86_64-linux-gnu/c++/$GCCVERSION \ -I/usr/include/c++/$GCCVERSION/$MACHTYPE diff --git a/inc/Logger.hpp b/inc/Logger.hpp deleted file mode 100644 index 158c8d65a5d11ad722138441e5ff9cdc056c05bd..0000000000000000000000000000000000000000 --- a/inc/Logger.hpp +++ /dev/null @@ -1,225 +0,0 @@ -#ifndef ECSS_SERVICES_LOGGER_HPP -#define ECSS_SERVICES_LOGGER_HPP - -#include <cstdint> -#include <etl/String.hpp> -#include <etl/to_string.h> -#include "ECSS_Configuration.hpp" -#include "ECSS_Definitions.hpp" - -#if defined LOGLEVEL_TRACE -#define LOGLEVEL Logger::trace // Ignore-MISRA -#elif defined LOGLEVEL_DEBUG -#define LOGLEVEL Logger::debug // Ignore-MISRA -#elif defined LOGLEVEL_INFO -#define LOGLEVEL Logger::info // Ignore-MISRA -#elif defined LOGLEVEL_NOTICE -#define LOGLEVEL Logger::notice // Ignore-MISRA -#elif defined LOGLEVEL_WARNING -#define LOGLEVEL Logger::warning // Ignore-MISRA -#elif defined LOGLEVEL_ERROR -#define LOGLEVEL Logger::error // Ignore-MISRA -#elif defined LOGLEVEL_EMERGENCY -#define LOGLEVEL Logger::emergency // Ignore-MISRA -#elif !defined LOGLEVEL -#define LOGLEVEL Logger::disabled // Ignore-MISRA -#endif - -#define LOG_TRACE (LOG<Logger::trace>()) ///< @see LOG @relates Logger -#define LOG_DEBUG (LOG<Logger::debug>()) ///< @see LOG @relates Logger -#define LOG_INFO (LOG<Logger::info>()) ///< @see LOG @relates Logger -#define LOG_NOTICE (LOG<Logger::notice>()) ///< @see LOG @relates Logger -#define LOG_WARNING (LOG<Logger::warning>()) ///< @see LOG @relates Logger -#define LOG_ERROR (LOG<Logger::error>()) ///< @see LOG @relates Logger -#define LOG_EMERGENCY (LOG<Logger::emergency>()) ///< @see LOG @relates Logger - -/** - * A logging class for ECSS Services that supports ETL's String and is lightweight enough to be used in embedded - * development. - * - * @note Always use the \ref LOG function and its associated utility macros to log. Do not directly use the Logger - * class. - */ -class Logger { -public: - /** - * No need to instantiate a Logger object for now. - */ - Logger() = delete; - - /** - * The underlying type to be used for values of Logger::LogLevel. - */ - typedef uint8_t LogLevelType; - - /** - * ETL's string format specification, to be used for all logged messages - */ - static etl::format_spec format; - - /** - * Log levels supported by the logger. Each level represents a different severity of the logged Message, - * and messages of lower severities can be filtered on top of more significant ones. - * - * Each severity is tied to a number. The higher the number, the higher the severity. - */ - enum LogLevel : LogLevelType { - trace = 32, ///< Very detailed information, useful for tracking the individual steps of an operation - debug = 64, ///< General debugging information - info = 96, ///< Noteworthy or periodical events - notice = 128, ///< Uncommon but expected events - warning = 160, ///< Unexpected events that do not compromise the operability of a function - error = 192, ///< Unexpected failure of an operation - emergency = 254, ///< Unexpected failure that renders the entire system unusable - disabled = 255, ///< Use this log level to disable logging entirely. No message should be logged as disabled. - }; - - /** - * An empty enum representing a dummy log entry that will not be logged due to an insufficient level. - * - * @internal - */ - enum class NoLogEntry {}; - - /** - * A class that defines a log message. - * - * Instead of using this class, prefer one of the above macros. - * @see LOG - * @internal - */ - struct LogEntry { - String<LoggerMaxMessageSize> message = ""; ///< The current log message itself, starting from a blank slate - LogLevel level; ///< The log level of this message - - 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. - * - * According to the C++ standard, a variable used only within an expression will be immediately destroyed once - * the processing of this expression is over. This allows a syntax such as `LogEntry(...) << "some" << "text"`, - * where the destructor will be called strictly **after** all the `<<` operations have been completed. This - * allows the destructor to send the finalized log entry for further processing. - */ - ~LogEntry(); - - LogEntry(LogEntry const&) = delete; ///< Unimplemented copy constructor - - /** - * Stream operator to append new values to a log record - * - * @tparam T The type of value to append - * @param value The new value to add - * @todo See if noexcept can be added here without triggering warnings - * @return The current Logger::LogEntry where the value has been appended - */ - template <class T> - Logger::LogEntry& operator<<(const T value) { - etl::to_string(value, message, format, true); - - return *this; - } - - Logger::LogEntry& operator<<(const std::string& value); - }; - - /** - * Returns whether a log entry of level \p level is logged, based on the compilation constants - * @param level The level of the log entry - * @return True if the logging is enabled for \p level, false if not - */ - static constexpr bool isLogged(LogLevelType level) { - return static_cast<LogLevelType>(LOGLEVEL) <= level; - } - - /** - * Store a new log message - */ - static void log(LogLevel level, etl::istring & message); -}; - -/** - * Create a stream to log a Message - * - * This functions appends one line to the Logs (which could be printed to screen, transferred via UART or stored for - * later use.) - * - * Examples of usage: - * @code - * LOG<Logger::debug>() << "Reached point of no return"; - * LOG<Logger::error>() << "More than " << 50 << " dogs found!"; - * @endcode - * - * You can also use one of the \ref LOG_TRACE, \ref LOG_DEBUG, \ref LOG_INFO, \ref LOG_NOTICE, \ref LOG_WARNING, - * \ref LOG_ERROR or \ref LOG_EMERGENCY defines, which avoid the need of explicitly passing the log level: - * @code - * LOG_DEBUG << "Reached point of no return"; - * LOG_ERROR << "More than " << 50 << " dogs found!"; - * @endcode - * - * See \ref Logger::LogLevel for an explanation of the different log levels. - * - * @par Implementation details - * Functions here are defined as `constexpr` in order to let them be optimized as soon as possible. The LOG() - * function returns an instance of \ref Logger::LogEntry if the level is high enough to be shown, or an instance of - * \ref Logger::NoLogEntry if the log entry will not be displayed. As this is a templated function, it is acceptable - * to support different return types using the `auto` keyword. - * - * @warning For messages that will not be logged, any calls to functions that contain **side effects will still take - * place**. - * @code - * LOG_DEBUG << "The temperature is: " << getTemperature(); - * @endcode - * In the above example, if `getTemperature()` will cause a side effect (e.g. an I2C connection or a `std::cout` print), - * it will still be executed, even if the debug message will not be printed to the screen due to an insufficient - * LOGLEVEL. - * - * @section GlobalLogLevels Global log levels - * The **global log level** defines the minimum severity of events to be displayed. Log entries with a severity equal - * to or higher than the global log level will be shown. Log entries with a severity smaller than the global log level - * will not be shown. - * - * The global log level can be set by defining one of the following constants: - * - `LOGLEVEL_TRACE` - * - `LOGLEVEL_DEBUG` - * - `LOGLEVEL_INFO` - * - `LOGLEVEL_NOTICE` - * - `LOGLEVEL_WARNING` - * - `LOGLEVEL_ERROR` - * - `LOGLEVEL_EMERGENCY` - * - * @relates Logger - * @tparam level The log level. A value of \ref Logger::LogLevel - * @return Returns \ref Logger::LogEntry if the level is sufficient to be logged, or \ref Logger::NoLogEntry if the - * message will not be logged. This is determined at compile-time. - */ -template <Logger::LogLevel level> -constexpr __attribute__((always_inline)) inline auto LOG() { - if constexpr (Logger::isLogged(level)) { - return Logger::LogEntry(level); - } else { - return Logger::NoLogEntry(); - } -}; - -/** - * A no-op function that considers an empty log entry that will not be displayed, processed or stored. - * - * @warning Note that functions containing **side effects** will get properly executed. Only use functions that return - * plain values as parts of the log function, so they might be optimized away at compile time. - * - * @tparam T The type of the data that will be ignored - * @param noLogEntry A dummy no-op log entry - * @param value The data that will be ignored - * @return A dummy no-op log entry - * @see Logger::LogEntry::operator<<(const T value) - * @relates Logger::LogEntry - */ -template <typename T> -[[maybe_unused]] constexpr Logger::NoLogEntry operator<<(const Logger::NoLogEntry noLogEntry, T value) { - return noLogEntry; -} - -#endif //ECSS_SERVICES_LOGGER_HPP diff --git a/inc/Platform/x86/ECSS_Configuration.hpp b/inc/Platform/x86/ECSS_Configuration.hpp index d1ee738207484dd77aa4c4f8b53b3cbd0d77f811..1e7f59f29fe3c3c2d081d35614c4ed2baa0f79db 100644 --- a/inc/Platform/x86/ECSS_Configuration.hpp +++ b/inc/Platform/x86/ECSS_Configuration.hpp @@ -9,9 +9,6 @@ * @see ServiceDefinitions Define the service types that will be compiled */ -// Log all levels of messages -#define LOGLEVEL_TRACE - /** * @defgroup ServiceDefinitions Service compilation switches * These preprocessor defines control whether the compilation of each ECSS service is enabled. By not defining one of diff --git a/lib/logger b/lib/logger new file mode 160000 index 0000000000000000000000000000000000000000..4de567283a883fa4d93f454b6979cd4c90c5382b --- /dev/null +++ b/lib/logger @@ -0,0 +1 @@ +Subproject commit 4de567283a883fa4d93f454b6979cd4c90c5382b diff --git a/src/Logger.cpp b/src/Logger.cpp deleted file mode 100644 index 76e36d59af7a58a5b9c34cd9fa3aca3592fee4b1..0000000000000000000000000000000000000000 --- a/src/Logger.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include <Logger.hpp> - -etl::format_spec Logger::format; - -// Reimplementation of the function for variable C strings -template <> -Logger::LogEntry& Logger::LogEntry::operator<<(char* value) { - message.append(value); - return *this; -} - -// Reimplementation of the function for C strings -template <> -Logger::LogEntry& Logger::LogEntry::operator<<(const char* value) { - message.append(value); - return *this; -} - -Logger::LogEntry::LogEntry(LogLevel level) : level(level) {} - -Logger::LogEntry::~LogEntry() { - // When the destructor is called, the log message is fully "designed". Now we can finally "display" it to the user. - Logger::log(level, message); -} diff --git a/src/Platform/x86/Logger.cpp b/src/Platform/x86/Logger.cpp deleted file mode 100644 index 85d0797656a4831c718e5b64740eb977ddb98c1d..0000000000000000000000000000000000000000 --- a/src/Platform/x86/Logger.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include <Logger.hpp> -#include <etl/String.hpp> -#include <iostream> -#include <ECSS_Definitions.hpp> - -#include <chrono> -#include <iomanip> - -// The implementation of this function appends ANSI codes that should add colours to a compatible terminal -void Logger::log(Logger::LogLevel level, etl::istring & message) { - // Get the current time & date - std::time_t t = std::time(nullptr); - std::tm tm = *std::localtime(&t); - - // Get the log level and its colour - std::string name; - std::string colour; - bool keepColour = false; // Whether to keep the colour in the rest of the message - - if (level <= Logger::trace) { - name = "trace"; - colour = "90"; // bright black - keepColour = true; - } else if (level <= Logger::debug) { - name = "debug"; - colour = "90"; // bright black - } else if (level <= Logger::info) { - name = "info"; - colour = "32"; // green - } else if (level <= Logger::notice) { - name = "notice"; - colour = "36"; // cyan - } else if (level <= Logger::warning) { - name = "warning"; - colour = "33"; // yellow - } else if (level <= Logger::error) { - name = "error"; - colour = "31"; // red - } else { - name = "emergency"; - colour = "31"; // red - keepColour = true; - } - - std::ostringstream ss; // A string stream to create the log message - ss << "\033" "[0;90m" << std::put_time(&tm, "%FT%T%z") << "\033" "[0m "; // The date - ss << "[\033" "[1;" << colour << "m" << std::setfill(' ') << std::setw(7) << std::right // Ignore-MISRA - << name << std::setw(0) << "\033" "[0m] "; // The log level // Ignore-MISRA - - if (keepColour) { - ss << "\033" "[0;" << colour << "m"; // Ignore-MISRA - } - ss << message.c_str(); // The message itself - if (keepColour) { - ss << "\033" "[0m"; - } - - ss << "\n"; - std::cerr << ss.str(); -} - -// Reimplementation of the log function for C++ strings -// This is kept in the Platform files, since we don't want to mess with std::strings in the microcontroller -Logger::LogEntry& Logger::LogEntry::operator<<(const std::string & value) { - message.append(value.c_str()); - - return *this; -}