Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2024-06-26 07:05:12

0001 // SPDX-License-Identifier: LGPL-3.0-or-later
0002 // Copyright (C) 2022 Wouter Deconinck, Sylvester Joosten
0003 //
0004 // Logging service, which will use a callback to use the framework logging infrastructure.
0005 // use ::action(void(LogLevel, std::string_view, std::string_view)) to register
0006 // a logger.
0007 //
0008 // Also provides the LoggerMixin and LoggedService base classes
0009 //
0010 #pragma once
0011 
0012 #include <array>
0013 #include <functional>
0014 #include <ios>
0015 #include <iostream>
0016 #include <mutex>
0017 #include <ostream>
0018 #include <sstream>
0019 #include <string>
0020 
0021 #include <algorithms/error.h>
0022 #include <algorithms/service.h>
0023 #include <fmt/format.h>
0024 
0025 // Simple thread-safe logger with optional overrides by the calling framework
0026 namespace algorithms {
0027 
0028 enum class LogLevel : unsigned {
0029   kTrace    = 0,
0030   kDebug    = 1,
0031   kInfo     = 2,
0032   kWarning  = 3,
0033   kError    = 4,
0034   kCritical = 5,
0035 };
0036 constexpr std::string_view logLevelName(LogLevel level) {
0037   // Compiler can warn if not all of the enum is covered
0038   switch (level) {
0039   case LogLevel::kTrace:
0040     return "TRACE";
0041   case LogLevel::kDebug:
0042     return "DEBUG";
0043   case LogLevel::kInfo:
0044     return "INFO";
0045   case LogLevel::kWarning:
0046     return "WARNING";
0047   case LogLevel::kError:
0048     return "ERROR";
0049   case LogLevel::kCritical:
0050     return "CRITICAL";
0051   }
0052   // Default return to make gcc happy, will never happen
0053   return "UNKNOWN";
0054 }
0055 
0056 // Note: the log action is responsible for dealing with concurrent calls
0057 //       the default LogAction is a thread-safe example
0058 class LogSvc : public Service<LogSvc> {
0059 public:
0060   using LogAction = std::function<void(LogLevel, std::string_view, std::string_view)>;
0061   void defaultLevel(const LogLevel l) { m_level.set(detail::upcast_type_t<LogLevel>(l)); }
0062   LogLevel defaultLevel() const { return m_level; }
0063   void init() {
0064     ; // do nothing by default, as we are already initialized
0065   }
0066   void init(LogAction a) { m_action = a; }
0067   void report(const LogLevel l, std::string_view caller, std::string_view msg) const {
0068     m_action(l, caller, msg);
0069   }
0070 
0071 private:
0072   LogAction makeDefaultAction() {
0073     return [](const LogLevel l, std::string_view caller, std::string_view msg) {
0074       static std::mutex m;
0075       std::lock_guard<std::mutex> lock(m);
0076       fmt::print("{} [{}] {}\n", logLevelName(l), caller, msg);
0077     };
0078   }
0079 
0080   Property<LogLevel> m_level{this, "defaultLevel", LogLevel::kInfo,
0081                              "Default log level for the LogSvc"};
0082   LogAction m_action = makeDefaultAction();
0083 
0084   ALGORITHMS_DEFINE_SERVICE(LogSvc)
0085 };
0086 
0087 namespace detail {
0088   // Output buffer that calls our global logger's report() function
0089   class LoggerBuffer : public std::stringbuf {
0090   public:
0091     LoggerBuffer(const LogLevel l, std::string_view caller)
0092         : m_mylevel{l}, m_caller{caller}, m_logger{LogSvc::instance()} {}
0093     virtual int sync() {
0094       // report should deal with concurrency (the minimal version does)
0095       m_logger.report(m_mylevel, m_caller, this->str());
0096       this->str("");
0097       return 0;
0098     }
0099 
0100   private:
0101     // The output buffer knows the log level of its associated logger
0102     // (eg. is this the debug logger?)
0103     LogLevel m_mylevel;
0104     const std::string m_caller;
0105     const LogSvc& m_logger;
0106   };
0107   // thread-safe output stream for the logger
0108   class LoggerStream {
0109   public:
0110     LoggerStream(std::string_view caller, const LogLevel level,
0111                  const LogLevel threshold = LogSvc::instance().defaultLevel())
0112         : m_buffer{level, caller}, m_os{&m_buffer}, m_level{level}, m_threshold{threshold} {}
0113     LoggerStream()                    = delete;
0114     LoggerStream(const LoggerStream&) = delete;
0115 
0116     template <class Arg> LoggerStream& operator<<(Arg&& streamable) {
0117       if (m_level >= m_threshold) {
0118         std::lock_guard<std::mutex> lock{m_mutex};
0119         m_os << std::forward<Arg>(streamable);
0120         return *this;
0121       }
0122       return *this;
0123     }
0124     // To support input manipulators such as std::endl
0125     // Note: would be better with Concepts
0126     using IOManipType1 = std::ostream&(std::ostream&); // this capturs std::endl;
0127     using IOManipType2 = std::ios_base&(std::ios_base&);
0128     LoggerStream& operator<<(IOManipType1* f) {
0129       if (m_level >= m_threshold) {
0130         std::lock_guard<std::mutex> lock{m_mutex};
0131         f(m_os);
0132         return *this;
0133       }
0134       return *this;
0135     }
0136     LoggerStream& operator<<(IOManipType2* f) {
0137       if (m_level >= m_threshold) {
0138         std::lock_guard<std::mutex> lock{m_mutex};
0139         f(m_os);
0140         return *this;
0141       }
0142       return *this;
0143     }
0144     LogLevel threshold() const { return m_threshold; }
0145     void threshold(const LogLevel th) { m_threshold = th; }
0146 
0147   private:
0148     std::mutex m_mutex;
0149     LoggerBuffer m_buffer;
0150     std::ostream m_os;
0151     const LogLevel m_level;
0152     LogLevel m_threshold;
0153   };
0154 } // namespace detail
0155 
0156 // Mixin meant to add utility logger functions to algorithms/services/etc
0157 class LoggerMixin {
0158 public:
0159   LoggerMixin(std::string_view caller, const LogLevel threshold = LogSvc::instance().defaultLevel())
0160       : m_caller{caller}, m_logger{LogSvc::instance()} {
0161     level(threshold);
0162   }
0163 
0164 public:
0165   // Not done through Properties, as that would require entanglement with the
0166   // PropertyMixin which is not appropriate here. It's the FW responsibility to set this
0167   // on the algorithm level if desired, before or during the init() stage.
0168   void level(const LogLevel threshold) {
0169     m_level = threshold;
0170     m_critical.threshold(m_level);
0171     m_error.threshold(m_level);
0172     m_warning.threshold(m_level);
0173     m_info.threshold(m_level);
0174     m_debug.threshold(m_level);
0175     m_trace.threshold(m_level);
0176   }
0177   LogLevel level() const { return m_level; }
0178 
0179 protected:
0180   detail::LoggerStream& critical() const { return m_critical; }
0181   detail::LoggerStream& error() const { return m_error; }
0182   detail::LoggerStream& warning() const { return m_warning; }
0183   detail::LoggerStream& info() const { return m_info; }
0184   detail::LoggerStream& debug() const { return m_debug; }
0185   detail::LoggerStream& trace() const { return m_trace; }
0186 
0187   void critical(std::string_view msg) const { report<LogLevel::kCritical>(msg); }
0188   void error(std::string_view msg) const { report<LogLevel::kError>(msg); }
0189   void warning(std::string_view msg) const { report<LogLevel::kWarning>(msg); }
0190   void info(std::string_view msg) const { report<LogLevel::kInfo>(msg); }
0191   void debug(std::string_view msg) const { report<LogLevel::kDebug>(msg); }
0192   void trace(std::string_view msg) const { report<LogLevel::kTrace>(msg); }
0193 
0194   template <typename ...T> constexpr void critical(fmt::format_string<T...> fmt, T&&... args) const {
0195     report_fmt<LogLevel::kCritical>(fmt, std::forward<decltype(args)>(args)...);
0196   }
0197   template <typename ...T> constexpr void error(fmt::format_string<T...> fmt, T&&... args) const {
0198     report_fmt<LogLevel::kError>(fmt, std::forward<decltype(args)>(args)...);
0199   }
0200   template <typename ...T> constexpr void warning(fmt::format_string<T...> fmt, T&&... args) const {
0201     report_fmt<LogLevel::kWarning>(fmt, std::forward<decltype(args)>(args)...);
0202   }
0203   template <typename ...T> constexpr void info(fmt::format_string<T...> fmt, T&&... args) const {
0204     report_fmt<LogLevel::kInfo>(fmt, std::forward<decltype(args)>(args)...);
0205   }
0206   template <typename ...T> constexpr void debug(fmt::format_string<T...> fmt, T&&... args) const {
0207     report_fmt<LogLevel::kDebug>(fmt, std::forward<decltype(args)>(args)...);
0208   }
0209   template <typename ...T> constexpr void trace(fmt::format_string<T...> fmt, T&&... args) const {
0210     report_fmt<LogLevel::kTrace>(fmt, std::forward<decltype(args)>(args)...);
0211   }
0212 
0213   bool aboveCriticalThreshold() const { return m_level >= LogLevel::kCritical; }
0214   bool aboveErrorThreshold() const { return m_level >= LogLevel::kError; }
0215   bool aboveWarningThreshold() const { return m_level >= LogLevel::kWarning; }
0216   bool aboveInfoThreshold() const { return m_level >= LogLevel::kInfo; }
0217   bool aboveDebugThreshold() const { return m_level >= LogLevel::kDebug; }
0218   bool aboveTraceThreshold() const { return m_level >= LogLevel::kTrace; }
0219 
0220   // LoggerMixin also provides nice error raising
0221   // ErrorTypes needs to derive from Error, and needs to have a constructor that takes two
0222   // strings --> TODO add C++20 Concept version
0223   template <class ErrorType = Error> void raise(std::string_view msg) const {
0224     error() << msg << endmsg;
0225     throw ErrorType(msg);
0226   }
0227 
0228   // Endmsg (flush) support to initiate a sync() action
0229   static std::ostream& endmsg(std::ostream& os) {
0230     os.flush();
0231     return os;
0232   }
0233 
0234 private:
0235   template <LogLevel l, typename ...T>
0236   constexpr void report_fmt(fmt::format_string<T...> fmt, T&&... args) const {
0237     if (l >= m_level) {
0238       m_logger.report(l, m_caller, fmt::format(fmt, std::forward<decltype(args)>(args)...));
0239     }
0240   }
0241 
0242   template <LogLevel l> void report(std::string_view msg) const {
0243     if (l >= m_level) {
0244       m_logger.report(l, m_caller, msg);
0245     }
0246   }
0247 
0248   const std::string m_caller;
0249   LogLevel m_level;
0250   mutable detail::LoggerStream m_critical{m_caller, LogLevel::kCritical};
0251   mutable detail::LoggerStream m_error{m_caller, LogLevel::kError};
0252   mutable detail::LoggerStream m_warning{m_caller, LogLevel::kWarning};
0253   mutable detail::LoggerStream m_info{m_caller, LogLevel::kInfo};
0254   mutable detail::LoggerStream m_debug{m_caller, LogLevel::kDebug};
0255   mutable detail::LoggerStream m_trace{m_caller, LogLevel::kTrace};
0256 
0257   const LogSvc& m_logger;
0258 };
0259 
0260 // A Service base class with logger functionality
0261 template <class SvcType> class LoggedService : public Service<SvcType>, public LoggerMixin {
0262 protected:
0263   LoggedService(std::string_view name) : Service<SvcType>(name), LoggerMixin(name) {}
0264 };
0265 
0266 } // namespace algorithms
0267 
0268 #define ALGORITHMS_DEFINE_LOGGED_SERVICE(className)                                                \
0269 protected:                                                                                         \
0270   className() : LoggedService<className>(#className) {}                                            \
0271                                                                                                    \
0272 public:                                                                                            \
0273   friend class Service<className>;                                                                 \
0274   className(const className&) = delete;                                                            \
0275   void operator=(const className&)   = delete;                                                     \
0276   constexpr static const char* kName = #className;
0277 
0278 //#define endmsg std::flush