LCOV - code coverage report
Current view: top level - include - SimpleLogger.h (source / functions) Hit Total Coverage
Test: lcov.info Lines: 75 76 98.7 %
Date: 2024-12-28 17:36:05 Functions: 89 162 54.9 %

          Line data    Source code
       1             : /**
       2             :  * @file SimpleLogger.h
       3             :  * @author Damien Balima (www.dams-labs.net)
       4             :  * @brief a simple logger, using fluent interfaces.
       5             :  * @date 2023-11-02
       6             :  *
       7             :  * @copyright Damien Balima (c) CC-BY-NC-SA-4.0 2023
       8             :  *
       9             :  */
      10             : #pragma once
      11             : #include <chrono>
      12             : #include <ctime>
      13             : #include <functional>
      14             : #include <iomanip>
      15             : #include <iostream>
      16             : #include <mutex>
      17             : #include <sstream>
      18             : 
      19             : namespace sipai {
      20             : enum class LogLevel { INFO, WARN, ERROR, DEBUG };
      21             : 
      22             : /**
      23             :  * @brief SimpleLogger class
      24             :  *
      25             :  */
      26             : class SimpleLogger {
      27             : public:
      28             :   using LogCallback = std::function<void(
      29             :       const std::string &, const std::string &, const std::string &)>;
      30             :   /**
      31             :    * @brief Get the Instance object
      32             :    * @remark This use a thread safe Meyers’ Singleton
      33             :    * @return const SimpleLogger&
      34             :    */
      35         188 :   const static SimpleLogger &getInstance() {
      36         188 :     static SimpleLogger instance;
      37         188 :     return instance;
      38             :   }
      39             :   SimpleLogger(SimpleLogger const &) = delete;
      40             :   void operator=(SimpleLogger const &) = delete;
      41             : 
      42             :   /**
      43             :    * @brief Set the Log Callback
      44             :    *
      45             :    * @param callback
      46             :    */
      47             :   void setLogCallback(LogCallback callback) { logCallback = callback; }
      48             : 
      49             :   /**
      50             :    * @brief Logs messages with a timestamp and log level.
      51             :    *
      52             :    * @tparam Args The types of the arguments that are passed to the method.
      53             :    * @param level The log level of the message (INFO, WARN, ERROR, DEBUG).
      54             :    * @param endl A boolean value that determines whether a newline character is
      55             :    * appended at the end of the log message. The default value is true.
      56             :    * @param args The arguments that make up the log message. These are forwarded
      57             :    * to the output stream.
      58             :    * @return const SimpleLogger& A reference to the SimpleLogger instance.
      59             :    */
      60             :   template <typename... Args>
      61         186 :   const SimpleLogger &log(LogLevel level, bool endl, Args &&...args) const {
      62         186 :     std::scoped_lock<std::mutex> lock(threadMutex_);
      63         186 :     currentLevel = level;
      64             : 
      65         186 :     std::ostringstream streamArgs;
      66         186 :     (streamArgs << ... << args);
      67             : 
      68         186 :     std::ostream *stream = (level == LogLevel::INFO) ? osout : oserr;
      69         186 :     *stream << "[" << get_timestamp() << "] [" << toString(level) << "] ";
      70         186 :     stream->precision(current_precision);
      71         186 :     *stream << std::fixed;
      72         186 :     *stream << streamArgs.str();
      73         186 :     if (endl) {
      74         186 :       *stream << std::endl;
      75             :     }
      76             : 
      77             :     // Call the log callback if set
      78         186 :     if (logCallback) {
      79           0 :       logCallback(get_timestamp(), toString(level), streamArgs.str());
      80             :     }
      81         186 :     return *this;
      82         186 :   }
      83             : 
      84             :   /**
      85             :    * @brief Appends messages to the output stream.
      86             :    *
      87             :    * @tparam Args The types of the arguments that are passed to the method.
      88             :    * @param args The arguments that make up the message. These are forwarded to
      89             :    * the output stream.
      90             :    * @return const SimpleLogger& A reference to the SimpleLogger instance.
      91             :    */
      92             :   template <typename... Args> const SimpleLogger &append(Args &&...args) const {
      93             :     std::scoped_lock<std::mutex> lock(threadMutex_);
      94             :     std::ostream *stream = (currentLevel == LogLevel::INFO) ? osout : oserr;
      95             :     stream->precision(current_precision);
      96             :     (*stream << ... << args);
      97             :     return *this;
      98             :   }
      99             : 
     100             :   /**
     101             :    * @brief Outputs messages to the output stream with a newline
     102             :    * character at the end, without timestamp and additional infos.
     103             :    *
     104             :    * @tparam Args The types of the arguments that are passed to the method.
     105             :    * @param args The arguments that make up the message. These are forwarded to
     106             :    * the output stream.
     107             :    * @return const SimpleLogger& A reference to the SimpleLogger instance.
     108             :    */
     109             :   template <typename... Args> const SimpleLogger &out(Args &&...args) const {
     110             :     std::scoped_lock<std::mutex> lock(threadMutex_);
     111             :     osout->precision(current_precision);
     112             :     (*osout << ... << args);
     113             :     *osout << std::endl;
     114             :     return *this;
     115             :   }
     116             : 
     117             :   /**
     118             :    * @brief Outputs messages to the error stream with a newline
     119             :    * character at the end, without timestamp and additional infos.
     120             :    *
     121             :    * @tparam Args The types of the arguments that are passed to the method.
     122             :    * @param args The arguments that make up the message. These are forwarded to
     123             :    * the error stream.
     124             :    * @return const SimpleLogger& A reference to the SimpleLogger instance.
     125             :    */
     126             :   template <typename... Args> const SimpleLogger &err(Args &&...args) const {
     127             :     std::scoped_lock<std::mutex> lock(threadMutex_);
     128             :     oserr->precision(current_precision);
     129             :     (*oserr << ... << args);
     130             :     *oserr << std::endl;
     131             :     return *this;
     132             :   }
     133             : 
     134             :   /**
     135             :    * @brief Logs messages with the INFO log level.
     136             :    *
     137             :    * @tparam Args The types of the arguments that are passed to the method.
     138             :    * @param args The arguments that make up the log message. These are forwarded
     139             :    * to the output stream.
     140             :    * @return const SimpleLogger& A reference to the SimpleLogger instance.
     141             :    */
     142         105 :   template <typename... Args> const SimpleLogger &info(Args &&...args) const {
     143         105 :     return log(LogLevel::INFO, true, args...);
     144             :   }
     145             : 
     146             :   /**
     147             :    * @brief Logs messages with the WARN log level.
     148             :    *
     149             :    * @tparam Args The types of the arguments that are passed to the method.
     150             :    * @param args The arguments that make up the log message. These are forwarded
     151             :    * to the error stream.
     152             :    * @return const SimpleLogger& A reference to the SimpleLogger instance.
     153             :    */
     154           1 :   template <typename... Args> const SimpleLogger &warn(Args &&...args) const {
     155           1 :     return log(LogLevel::WARN, true, args...);
     156             :   }
     157             : 
     158             :   /**
     159             :    * @brief Logs messages with the ERROR log level.
     160             :    *
     161             :    * @tparam Args The types of the arguments that are passed to the method.
     162             :    * @param args The arguments that make up the log message. These are forwarded
     163             :    * to the error stream.
     164             :    * @return const SimpleLogger& A reference to the SimpleLogger instance.
     165             :    */
     166           1 :   template <typename... Args> const SimpleLogger &error(Args &&...args) const {
     167           1 :     return log(LogLevel::ERROR, true, args...);
     168             :   }
     169             : 
     170             :   /**
     171             :    * @brief Logs messages with the DEBUG log level.
     172             :    *
     173             :    * @tparam Args The types of the arguments that are passed to the method.
     174             :    * @param args The arguments that make up the log message. These are forwarded
     175             :    * to the error stream.
     176             :    * @return const SimpleLogger& A reference to the SimpleLogger instance.
     177             :    */
     178          74 :   template <typename... Args> const SimpleLogger &debug(Args &&...args) const {
     179          74 :     return log(LogLevel::DEBUG, true, args...);
     180             :   }
     181             : 
     182             :   /**
     183             :    * @brief Outputs a newline character and flushes the stream.
     184             :    * @return const SimpleLogger& A reference to the SimpleLogger instance.
     185             :    */
     186             :   const SimpleLogger &endl() const {
     187             :     std::scoped_lock<std::mutex> lock(threadMutex_);
     188             :     std::ostream *stream = (currentLevel == LogLevel::INFO) ? osout : oserr;
     189             :     *stream << std::endl;
     190             :     stream->flush();
     191             :     return *this;
     192             :   }
     193             : 
     194             :   /**
     195             :    * @brief Set the Stream Out
     196             :    *
     197             :    * @param oss
     198             :    * @return const SimpleLogger&
     199             :    */
     200           2 :   const SimpleLogger &setStreamOut(std::ostream &oss) const {
     201           2 :     std::scoped_lock<std::mutex> lock(threadMutex_);
     202           2 :     osout = &oss;
     203           2 :     return *this;
     204           2 :   }
     205             : 
     206             :   /**
     207             :    * @brief Set the Stream Err
     208             :    *
     209             :    * @param oss
     210             :    * @return const SimpleLogger&
     211             :    */
     212           2 :   const SimpleLogger &setStreamErr(std::ostream &oss) const {
     213           2 :     std::scoped_lock<std::mutex> lock(threadMutex_);
     214           2 :     oserr = &oss;
     215           2 :     return *this;
     216           2 :   }
     217             : 
     218             :   /**
     219             :    * @brief Set the floating precision
     220             :    *
     221             :    * @param precision
     222             :    * @return const SimpleLogger&
     223             :    */
     224           6 :   const SimpleLogger &setPrecision(std::streamsize precision) const {
     225           6 :     std::scoped_lock<std::mutex> lock(threadMutex_);
     226           6 :     current_precision = precision;
     227           6 :     return *this;
     228           6 :   }
     229             : 
     230             :   /**
     231             :    * @brief Get the current floating precision
     232             :    *
     233             :    * @return precision
     234             :    */
     235           2 :   const std::streamsize &getPrecision() const { return current_precision; }
     236             : 
     237             :   /**
     238             :    * @brief Reset the floating precision
     239             :    *
     240             :    * @return const SimpleLogger&
     241             :    */
     242             :   const SimpleLogger &resetPrecision() const {
     243             :     std::scoped_lock<std::mutex> lock(threadMutex_);
     244             :     current_precision = default_precision;
     245             :     return *this;
     246             :   }
     247             : 
     248             :   /**
     249             :    * @brief static shortcut for log info.
     250             :    * @tparam Args
     251             :    * @param args
     252             :    * @return const SimpleLogger&
     253             :    */
     254             :   template <typename... Args>
     255         105 :   static const SimpleLogger &LOG_INFO(Args &&...args) {
     256         105 :     return getInstance().info(args...);
     257             :   }
     258             : 
     259             :   /**
     260             :    * @brief static shortcut for log warning.
     261             :    * @tparam Args
     262             :    * @param args
     263             :    * @return const SimpleLogger&
     264             :    */
     265             :   template <typename... Args>
     266           1 :   static const SimpleLogger &LOG_WARN(Args &&...args) {
     267           1 :     return getInstance().warn(args...);
     268             :   }
     269             : 
     270             :   /**
     271             :    * @brief static shortcut for log error.
     272             :    * @tparam Args
     273             :    * @param args
     274             :    * @return const SimpleLogger&
     275             :    */
     276             :   template <typename... Args>
     277           1 :   static const SimpleLogger &LOG_ERROR(Args &&...args) {
     278           1 :     return getInstance().error(args...);
     279             :   }
     280             : 
     281             :   /**
     282             :    * @brief static shortcut for log debug.
     283             :    * @tparam Args
     284             :    * @param args
     285             :    * @return const SimpleLogger&
     286             :    */
     287             :   template <typename... Args>
     288          74 :   static const SimpleLogger &LOG_DEBUG(Args &&...args) {
     289          74 :     return getInstance().debug(args...);
     290             :   }
     291             : 
     292             : private:
     293           1 :   SimpleLogger() {
     294           1 :     osout = &std::cout;
     295           1 :     oserr = &std::cerr;
     296           1 :   };
     297             :   std::streamsize default_precision = std::cout.precision();
     298             :   mutable std::streamsize current_precision = std::cout.precision();
     299             :   mutable std::mutex threadMutex_;
     300             :   mutable std::ostream *osout;
     301             :   mutable std::ostream *oserr;
     302             :   mutable LogLevel currentLevel = LogLevel::INFO;
     303             :   LogCallback logCallback = {};
     304             : 
     305         186 :   std::string get_timestamp() const {
     306         186 :     auto now = std::chrono::system_clock::now();
     307         186 :     auto now_c = std::chrono::system_clock::to_time_t(now);
     308         186 :     std::tm now_tm{};
     309         186 :     std::stringstream sst;
     310             : #if defined(WIN32) || defined(_WIN32) ||                                       \
     311             :     defined(__WIN32) && !defined(__CYGWIN__)
     312             :     localtime_s(&now_tm, &now_c);
     313             : #else
     314         186 :     localtime_r(&now_c, &now_tm);
     315             : #endif
     316             : 
     317         186 :     sst << std::put_time(&now_tm, "%F %T");
     318             : 
     319         372 :     return sst.str();
     320         186 :   }
     321             : 
     322         186 :   std::string toString(LogLevel level) const {
     323         186 :     switch (level) {
     324         106 :     case LogLevel::INFO:
     325         106 :       return "INFO";
     326           2 :     case LogLevel::WARN:
     327           2 :       return "WARN";
     328           2 :     case LogLevel::ERROR:
     329           2 :       return "ERROR";
     330          75 :     case LogLevel::DEBUG:
     331          75 :       return "DEBUG";
     332           1 :     default:
     333           1 :       return "UNKNOWN";
     334             :     }
     335             :   }
     336             : };
     337             : } // namespace sipai

Generated by: LCOV version 1.16