From 5f3b39948f6f086ca29b00d1e8722a9594efbd38 Mon Sep 17 00:00:00 2001 From: Olivier BICHLER <olivier.bichler@cea.fr> Date: Mon, 4 Mar 2024 15:33:43 +0100 Subject: [PATCH] Initial proposal for Aidge::Log system --- include/aidge/utils/ErrorHandling.hpp | 8 +- include/aidge/utils/Log.hpp | 148 ++++++++++++++++++++++++++ src/utils/Log.cpp | 59 ++++++++++ unit_tests/utils/Test_Log.cpp | 31 ++++++ 4 files changed, 243 insertions(+), 3 deletions(-) create mode 100644 include/aidge/utils/Log.hpp create mode 100644 src/utils/Log.cpp create mode 100644 unit_tests/utils/Test_Log.cpp diff --git a/include/aidge/utils/ErrorHandling.hpp b/include/aidge/utils/ErrorHandling.hpp index 653a774b9..d4235d2db 100644 --- a/include/aidge/utils/ErrorHandling.hpp +++ b/include/aidge/utils/ErrorHandling.hpp @@ -18,13 +18,15 @@ #include <fmt/format.h> #include <fmt/ranges.h> +#include "aidge/utils/Log.hpp" + #ifdef NO_EXCEPTION #define AIDGE_THROW_OR_ABORT(ex, ...) \ -do { fmt::print(__VA_ARGS__); std::abort(); } while (false) +do { Aidge::Log::fatal(__VA_ARGS__); std::abort(); } while (false) #else #include <stdexcept> #define AIDGE_THROW_OR_ABORT(ex, ...) \ -throw ex(fmt::format(__VA_ARGS__)) +do { Aidge::Log::fatal(__VA_ARGS__); throw ex(fmt::format(__VA_ARGS__)); } while (false) #endif /** @@ -33,7 +35,7 @@ throw ex(fmt::format(__VA_ARGS__)) * If it asserts, it means an user error. */ #define AIDGE_ASSERT(stm, ...) \ -if (!(stm)) { fmt::print("Assertion failed: " #stm " in {}:{}", __FILE__, __LINE__); \ +if (!(stm)) { Aidge::Log::error("Assertion failed: " #stm " in {}:{}", __FILE__, __LINE__); \ AIDGE_THROW_OR_ABORT(std::runtime_error, __VA_ARGS__); } /** diff --git a/include/aidge/utils/Log.hpp b/include/aidge/utils/Log.hpp new file mode 100644 index 000000000..1321f18a9 --- /dev/null +++ b/include/aidge/utils/Log.hpp @@ -0,0 +1,148 @@ +/******************************************************************************** + * Copyright (c) 2023 CEA-List + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + ********************************************************************************/ + + +#ifndef AIDGE_LOG_H_ +#define AIDGE_LOG_H_ + +#include <memory> + +#include <fmt/format.h> +#include <fmt/ranges.h> + +namespace Aidge { +/** + * Aidge logging class, for displaying and file logging of events. +*/ +class Log { +public: + enum Level { + Debug = 0, + Info, + Notice, + Warn, + Error, + Fatal + }; + + /** + * Detailed messages for debugging purposes, providing information helpful + * for developers to trace and identify issues. + * Detailed insights of what is appening in an operation, not useful for the + * end-user. The operation is performed nominally. + * @note This level is disabled at compile time for Release, therefore + * inducing no runtime overhead for Release. + */ + template <typename... Args> + constexpr static void debug(Args&&... args) { +#ifndef NDEBUG + // only when compiled in Debug + log(Debug, fmt::format(std::forward<Args>(args)...)); +#endif + } + + /** + * Messages that provide a record of the normal operation, about + * the application's state, progress, or important events. + * Reports normal start, end and key steps in an operation. The operation is + * performed nominally. + */ + template <typename... Args> + constexpr static void info(Args&&... args) { + log(Info, fmt::format(std::forward<Args>(args)...)); + } + + /** + * Applies to normal but significant conditions that may require monitoring, + * like unusual or normal fallback events. + * Reports specific paths in an operation. The operation can still be + * performed normally. + */ + template <typename... Args> + constexpr static void notice(Args&&... args) { + log(Notice, fmt::format(std::forward<Args>(args)...)); + } + + /** + * Indicates potential issues or situations that may lead to errors but do + * not necessarily cause immediate problems. + * Some specific steps of the operation could not be performed, but it can + * still provide an exploitable result. + */ + template <typename... Args> + constexpr static void warn(Args&&... args) { + log(Warn, fmt::format(std::forward<Args>(args)...)); + } + + /** + * Signifies a problem or unexpected condition that the application can + * recover from, but attention is needed to prevent further issues. + * The operation could not be performed, but it does not prevent potentiel + * further operations. + */ + template <typename... Args> + constexpr static void error(Args&&... args) { + log(Error, fmt::format(std::forward<Args>(args)...)); + } + + /** + * Represents a critical error or condition that leads to the termination of + * the application, indicating a severe and unrecoverable problem. + * The operation could not be performed and any further operation is + * impossible. + */ + template <typename... Args> + constexpr static void fatal(Args&&... args) { + log(Fatal, fmt::format(std::forward<Args>(args)...)); + } + + /** + * Set the minimum log level displayed in the console. + */ + constexpr static void setConsoleLevel(Level level) { + mConsoleLevel = level; + } + + /** + * Set the minimum log level saved in the log file. + */ + constexpr static void setFileLevel(Level level) { + mFileLevel = level; + } + + /** + * Set the log file name. + * Close the current log file and open the one with the new file name. + * If empty, stop logging into a file. + */ + static void setFileName(const std::string& fileName) { + if (fileName != mFileName) { + mFileName = fileName; + mFile.release(); + + if (!fileName.empty()) { + initFile(fileName); + } + } + } + +private: + static void log(Level level, const std::string& msg); + static void initFile(const std::string& fileName); + + static Level mConsoleLevel; + static Level mFileLevel; + static std::string mFileName; + static std::unique_ptr<FILE, decltype(&std::fclose)> mFile; +}; +} + +#endif //AIDGE_LOG_H_ diff --git a/src/utils/Log.cpp b/src/utils/Log.cpp new file mode 100644 index 000000000..a4993755d --- /dev/null +++ b/src/utils/Log.cpp @@ -0,0 +1,59 @@ +/******************************************************************************** + * Copyright (c) 2023 CEA-List + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + ********************************************************************************/ + +#include "aidge/utils/Log.hpp" +#include "aidge/utils/ErrorHandling.hpp" + +#include <fmt/color.h> +#include <fmt/chrono.h> + +Aidge::Log::Level Aidge::Log::mConsoleLevel = Info; +Aidge::Log::Level Aidge::Log::mFileLevel = Debug; +std::string Aidge::Log::mFileName = "aidge_log.txt"; +std::unique_ptr<FILE, decltype(&std::fclose)> Aidge::Log::mFile {nullptr, nullptr}; + +void Aidge::Log::log(Level level, const std::string& msg) { + if (level >= mConsoleLevel) { + // Apply log level style only for console. + // Styles that were already applied to msg with fmt are kept also in + // the log file. + const auto modifier + = (level == Debug) ? fmt::fg(fmt::color::gray) + : (level == Notice) ? fmt::fg(fmt::color::light_yellow) + : (level == Warn) ? fmt::fg(fmt::color::orange) + : (level == Error) ? fmt::fg(fmt::color::red) + : (level == Fatal) ? fmt::bg(fmt::color::red) + : fmt::text_style(); + + fmt::println("{}", fmt::styled(msg, modifier)); + } + + if (level >= mFileLevel && !mFileName.empty()) { + if (!mFile) { + initFile(mFileName); + } + + fmt::println(mFile.get(), msg); + } +} + +void Aidge::Log::initFile(const std::string& fileName) { + mFile = std::unique_ptr<FILE, decltype(&std::fclose)>(std::fopen(fileName.c_str(), "a"), &std::fclose); + + if (!mFile) { + mFileName.clear(); // prevents AIDGE_THROW_OR_ABORT() to try to log into file + AIDGE_THROW_OR_ABORT(std::runtime_error, + "Could not create log file: {}", fileName); + } + + const std::time_t t = std::time(nullptr); + fmt::println(mFile.get(), "###### {:%Y-%m-%d %H:%M:%S} ######", fmt::localtime(t)); +} diff --git a/unit_tests/utils/Test_Log.cpp b/unit_tests/utils/Test_Log.cpp new file mode 100644 index 000000000..3d8e672b8 --- /dev/null +++ b/unit_tests/utils/Test_Log.cpp @@ -0,0 +1,31 @@ +/******************************************************************************** + * Copyright (c) 2023 CEA-List + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + * + ********************************************************************************/ + +#include <catch2/catch_test_macros.hpp> + +#include "aidge/utils/Log.hpp" + +#include <fmt/color.h> + +using namespace Aidge; + +TEST_CASE("[core/log] Log") { + SECTION("TestLog") { + Log::setConsoleLevel(Log::Debug); + Log::debug("debug"); + Log::debug("{}", fmt::styled("green debug", fmt::fg(fmt::color::green))); + Log::info("info"); + Log::notice("notice"); + Log::warn("warn"); + Log::error("error"); + Log::fatal("fatal"); + } +} -- GitLab