File indexing completed on 2024-06-26 07:05:12
0001
0002
0003
0004
0005
0006
0007
0008
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
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
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
0053 return "UNKNOWN";
0054 }
0055
0056
0057
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 ;
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
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
0095 m_logger.report(m_mylevel, m_caller, this->str());
0096 this->str("");
0097 return 0;
0098 }
0099
0100 private:
0101
0102
0103 LogLevel m_mylevel;
0104 const std::string m_caller;
0105 const LogSvc& m_logger;
0106 };
0107
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
0125
0126 using IOManipType1 = std::ostream&(std::ostream&);
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 }
0155
0156
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
0166
0167
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
0221
0222
0223 template <class ErrorType = Error> void raise(std::string_view msg) const {
0224 error() << msg << endmsg;
0225 throw ErrorType(msg);
0226 }
0227
0228
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
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 }
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