LCOV - code coverage report
Current view: top level - src - Logger.hpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 108 117 92.3 %
Date: 2025-06-29 01:13:40 Functions: 107 107 100.0 %
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            : ** Copyright (C) 2020 Sylvain Fargier
       3                 :            : **
       4                 :            : ** This software is provided 'as-is', without any express or implied
       5                 :            : ** warranty.  In no event will the authors be held liable for any damages
       6                 :            : ** arising from the use of this software.
       7                 :            : **
       8                 :            : ** Permission is granted to anyone to use this software for any purpose,
       9                 :            : ** including commercial applications, and to alter it and redistribute it
      10                 :            : ** freely, subject to the following restrictions:
      11                 :            : **
      12                 :            : ** 1. The origin of this software must not be misrepresented; you must not
      13                 :            : **    claim that you wrote the original software. If you use this software
      14                 :            : **    in a product, an acknowledgment in the product documentation would be
      15                 :            : **    appreciated but is not required.
      16                 :            : ** 2. Altered source versions must be plainly marked as such, and must not be
      17                 :            : **    misrepresented as being the original software.
      18                 :            : ** 3. This notice may not be removed or altered from any source distribution.
      19                 :            : **
      20                 :            : ** Created on: 2021-12-30T17:07:25+01:00
      21                 :            : **     Author: Sylvain Fargier <fargie_s> <fargier.sylvain@gmail.com>
      22                 :            : **
      23                 :            : */
      24                 :            : 
      25                 :            : #ifndef LOGGER_HPP__
      26                 :            : #define LOGGER_HPP__
      27                 :            : 
      28                 :            : #include <chrono>
      29                 :            : #include <cstdint>
      30                 :            : #include <ctime>
      31                 :            : #include <functional>
      32                 :            : #include <ios>
      33                 :            : #include <memory>
      34                 :            : #include <sstream>
      35                 :            : #include <string>
      36                 :            : #include <tuple>
      37                 :            : #include <utility>
      38                 :            : 
      39                 :            : namespace logger {
      40                 :            : 
      41                 :            : class Logger;
      42                 :            : class NullLogger;
      43                 :            : 
      44                 :            : /**
      45                 :            :  * @brief available log levels
      46                 :            :  * @details enabling a level also enables all lower levels
      47                 :            :  */
      48                 :            : enum class Level : uint8_t
      49                 :            : {
      50                 :            :     EMERG,
      51                 :            :     ALERT,
      52                 :            :     CRIT,
      53                 :            :     ERROR,
      54                 :            :     WARNING,
      55                 :            :     NOTICE,
      56                 :            :     INFO,
      57                 :            :     DEBUG
      58                 :            : };
      59                 :            : 
      60                 :            : /**
      61                 :            :  * @brief logger features
      62                 :            :  */
      63                 :            : enum class Feature : uint8_t
      64                 :            : {
      65                 :            :     DATETIME = 0x01,  /**< display dateTime prefix */
      66                 :            :     TIMEDELTA = 0x02, /**< display thread-based elapsed time since last log */
      67                 :            :     LEVEL = 0x04,     /**< display level info */
      68                 :            :     CATEGORY = 0x08,  /**< display category info */
      69                 :            :     SYSLOG = 0x10,
      70                 :            :     NOCOLOR = 0x20, /**< disable colored output (default: auto) */
      71                 :            :     THREAD = 0x40 /**< display current thread id (id is generated by logger) */
      72                 :            : };
      73                 :            : 
      74                 :            : /* UTILITIES */
      75                 :            : 
      76                 :            : /**
      77                 :            :  * @brief utility class to log a time_point
      78                 :            :  */
      79                 :            : class LocalTime
      80                 :            : {
      81                 :            : public:
      82                 :            :     typedef std::chrono::time_point<std::chrono::system_clock> time_point;
      83                 :            : 
      84                 :            :     explicit LocalTime(const time_point &time);
      85                 :            :     LocalTime();
      86                 :            : 
      87                 :            :     const time_point time; /**< associated time-point */
      88                 :            : };
      89                 :            : std::string to_string(const LocalTime &time);
      90                 :            : 
      91                 :            : /**
      92                 :            :  * @brief utility class to log elapsed time
      93                 :            :  */
      94                 :            : class Elapsed
      95                 :            : {
      96                 :            : public:
      97                 :            :     Elapsed();
      98                 :            : 
      99                 :            :     /**
     100                 :            :      * @brief ticking method
     101                 :            :      * @details call this method forwarding its output to the logger to
     102                 :            :      * tick the timer
     103                 :            :      */
     104                 :            :     std::chrono::milliseconds tick();
     105                 :            : 
     106                 :            :     std::chrono::time_point<std::chrono::steady_clock> start; /**< timer start
     107                 :            :                                                                  point */
     108                 :            : };
     109                 :            : 
     110                 :            : /**
     111                 :            :  * @brief basic array iterator
     112                 :            :  * @details also works on containers (to display part)
     113                 :            :  * @details use iterator function to instantiate it
     114                 :            :  * @details this class requires a LegacyForwardIterator
     115                 :            :  */
     116                 :            : template<typename T>
     117                 :            : class Iterator
     118                 :            : {
     119                 :            : public:
     120                 :            :     typedef typename std::decay<const T>::type Iter;
     121                 :            : 
     122                 :          4 :     constexpr Iterator(const Iter &begin, const Iter &end) :
     123                 :          4 :         _begin(begin), _end(end)
     124                 :          4 :     {}
     125                 :            : 
     126                 :          4 :     inline const Iter &begin() const { return _begin; }
     127                 :          4 :     inline const Iter &end() const { return _end; }
     128                 :            : 
     129                 :            :     const Iter _begin;
     130                 :            :     const Iter _end;
     131                 :            : };
     132                 :            : 
     133                 :            : /**
     134                 :            :  * @brief array iterator function
     135                 :            :  * @details transforms basic array in range-iterable ones
     136                 :            :  * @details this function requires a LegacyRandomAccessIterator
     137                 :            :  * @param[in] begin start of array pointer or container iterator
     138                 :            :  * @param[in] len number of elements to display
     139                 :            :  */
     140                 :            : template<typename T>
     141                 :          1 : Iterator<T> iterator(const T &begin, std::size_t len)
     142                 :            : {
     143                 :          1 :     return Iterator<T>(begin, begin + len);
     144                 :            : }
     145                 :            : 
     146                 :            : /**
     147                 :            :  * @brief array iterator function
     148                 :            :  * @details transforms basic array in range-iterable ones
     149                 :            :  * @param[in] begin start of array pointer or iterator
     150                 :            :  * @param[in] end end of array pointer or iterator
     151                 :            :  */
     152                 :            : template<typename T>
     153                 :          3 : Iterator<T> iterator(const T &begin, const T &end)
     154                 :            : {
     155                 :          3 :     return Iterator<T>(begin, end);
     156                 :            : }
     157                 :            : 
     158                 :            : /**
     159                 :            :  * @brief main logger object
     160                 :            :  */
     161                 :            : class Logger
     162                 :            : {
     163                 :            : public:
     164                 :            :     struct Writer;
     165                 :            : 
     166                 :            :     explicit Logger(Level level = Level::NOTICE,
     167                 :            :                     Writer &writer = defaultWriter());
     168                 :            :     ~Logger();
     169                 :            :     Logger(const Logger &) = delete;
     170                 :            :     Logger &operator=(const Logger &) = delete;
     171                 :            :     Logger(Logger &&);
     172                 :            : 
     173                 :        322 :     inline const bool isEnabled() const { return !writer.buf.eof(); }
     174                 :            : 
     175                 :            :     /**
     176                 :            :      * @brief Logger writer
     177                 :            :      * @details generaly thread_local scoped
     178                 :            :      */
     179                 :            :     struct Writer
     180                 :            :     {
     181                 :            :         /** @brief log flusing and writing function */
     182                 :            :         void flush(Level level);
     183                 :            : 
     184                 :            :         /** @brief log buffer preparation */
     185                 :            :         void prepare(Level level);
     186                 :            :         /** @brief get current log line */
     187                 :            :         std::string str();
     188                 :            : 
     189                 :            :         std::stringstream buf; /**< log buffer, can be safely accessed */
     190                 :            :         Elapsed last;          /**< time elapsed since last log line */
     191                 :            : 
     192                 :            :     protected:
     193                 :            :         std::stringstream::pos_type prefixEnd; /**< end of "prepared" part */
     194                 :            :     } & writer;
     195                 :            :     const Level level; /**< level associated with this Logger */
     196                 :            : 
     197                 :            :     /**
     198                 :            :      * @brief get default writer for current thread
     199                 :            :      * @details mainly internally used
     200                 :            :      */
     201                 :            :     static Writer &defaultWriter();
     202                 :            : };
     203                 :            : 
     204                 :            : /**
     205                 :            :  * @brief external writer function type
     206                 :            :  *
     207                 :            :  * @param[in] level Level of line to print
     208                 :            :  * @param[in] line '\n' terminated line to print
     209                 :            :  */
     210                 :            : typedef std::function<bool(Level level, const std::string &line)> write_func_t;
     211                 :            : 
     212                 :            : // clang-format off
     213                 :            : // always resolve as "void"
     214                 :            : template<class, class T> struct type_sink { typedef T type; };
     215                 :            : template<class E, class T = void> using type_sink_t = typename type_sink<E, T>::type;
     216                 :            : 
     217                 :            : template<class T, class = void>
     218                 :            : struct has_toString : std::false_type {};
     219                 :            : template<class T>
     220                 :            : struct has_toString<T, type_sink_t<decltype(std::declval<T>().toString())>> : std::true_type {};
     221                 :            : 
     222                 :            : template<class T, class = void>
     223                 :            : struct has_to_string : std::false_type {};
     224                 :            : template<class T>
     225                 :            : struct has_to_string<T, type_sink_t<decltype(to_string(std::declval<T &>()))>> : std::true_type {};
     226                 :            : 
     227                 :            : /* Workarround for g++ 4.8.5 compiler, to explicitly exclude a type */
     228                 :            : template<class T, class = std::true_type>
     229                 :            : struct exclude_default : std::false_type {};
     230                 :            : 
     231                 :            : /* Warning: this is always true with g++ 4.8.5, due to bad template delcaration */
     232                 :            : template<class T, class = void>
     233                 :            : struct can_ostringstream : std::false_type {};
     234                 :            : template<class T>
     235                 :            : struct can_ostringstream<T, type_sink_t<decltype(std::declval<std::ostringstream>() << std::declval<T>())>> : std::true_type {};
     236                 :            : 
     237                 :            : template<class T, class = void>
     238                 :            : struct has_mapped_type : std::false_type {};
     239                 :            : template<class T>
     240                 :            : struct has_mapped_type<T, type_sink_t<typename T::mapped_type>> : std::true_type {};
     241                 :            : 
     242                 :            : template<class T, class = void>
     243                 :            : struct is_iterable_container : std::false_type {};
     244                 :            : /* according to C++ spec anything that has begin/end can be iterated using range */
     245                 :            : template<class T>
     246                 :            : struct is_iterable_container<T, typename std::enable_if<
     247                 :            :     !std::is_same<std::string, T>::value &&
     248                 :            :     type_sink_t<decltype(std::declval<T>().begin()), std::true_type>::value &&
     249                 :            :     type_sink_t<decltype(std::declval<T>().end()), std::true_type>::value>::type> : std::true_type {};
     250                 :            : // clang-format on
     251                 :            : 
     252                 :            : /**
     253                 :            :  * @brief general purpose logger operator
     254                 :            :  */
     255                 :            : template<typename T>
     256                 :            : typename std::enable_if<!exclude_default<T>::value && can_ostringstream<T>::value &&
     257                 :            :                             !is_iterable_container<T>::value &&
     258                 :            :                             !has_toString<T>::value && !has_to_string<T>::value,
     259                 :            :                         const Logger &>::type
     260                 :        268 :     operator<<(const Logger &logger, const T &t)
     261                 :            : {
     262                 :        268 :     if (!logger.isEnabled())
     263                 :          5 :         return logger;
     264                 :        263 :     logger.writer.buf << t;
     265                 :        263 :     return logger;
     266                 :            : }
     267                 :            : 
     268                 :            : /**
     269                 :            :  * @brief stl stream modifier logger operator
     270                 :            :  * @details support std::endl and others
     271                 :            :  */
     272                 :            : template<typename CharT = char, typename Traits = std::char_traits<char>>
     273                 :          2 : const Logger &operator<<(const Logger &logger,
     274                 :            :                          std::basic_ostream<CharT, Traits> &(*pf)(
     275                 :            :                              std::basic_ostream<CharT, Traits> &) )
     276                 :            : {
     277                 :          2 :     if (!logger.isEnabled())
     278                 :          0 :         return logger;
     279                 :          2 :     logger.writer.buf << pf;
     280                 :          2 :     return logger;
     281                 :            : }
     282                 :            : 
     283                 :            : /** @brief toString member logger operator */
     284                 :            : template<typename T>
     285                 :            : typename std::enable_if<!exclude_default<T>::value && has_toString<T>::value &&
     286                 :            :                             !has_to_string<T>::value,
     287                 :            :                         const Logger &>::type
     288                 :         13 :     operator<<(const Logger &logger, const T &t)
     289                 :            : {
     290                 :         13 :     if (!logger.isEnabled())
     291                 :          0 :         return logger;
     292                 :         13 :     return (logger << t.toString());
     293                 :            : }
     294                 :            : 
     295                 :            : /** @brief to_string function logger operator */
     296                 :            : template<typename T>
     297                 :            : typename std::enable_if<!exclude_default<T>::value && has_to_string<T>::value,
     298                 :            :                         const Logger &>::type
     299                 :          8 :     operator<<(const Logger &logger, const T &t)
     300                 :            : {
     301                 :          8 :     if (!logger.isEnabled())
     302                 :          0 :         return logger;
     303                 :          8 :     return (logger << to_string(t));
     304                 :            : }
     305                 :            : 
     306                 :            : /**
     307                 :            :  * @brief any duration logging
     308                 :            :  * @details will be logged up to millisecond precision
     309                 :            :  */
     310                 :            : template<typename T, typename U>
     311                 :            : typename std::enable_if<!exclude_default<std::chrono::duration<T, U>>::value,
     312                 :            :                         const Logger &>::type
     313                 :            :     operator<<(const Logger &logger, const std::chrono::duration<T, U> &ms)
     314                 :            : {
     315                 :            :     if (!logger.isEnabled())
     316                 :            :         return logger;
     317                 :            :     return (logger << std::chrono::duration_cast<std::chrono::milliseconds>(ms));
     318                 :            : }
     319                 :            : 
     320                 :            : /**
     321                 :            :  * @brief pair operator
     322                 :            :  */
     323                 :            : template<typename K, typename V>
     324                 :            : typename std::enable_if<!exclude_default<std::pair<K, V>>::value,
     325                 :            :                         const Logger &>::type
     326                 :          9 :     operator<<(const Logger &logger, const std::pair<K, V> &pair)
     327                 :            : {
     328                 :          9 :     if (!logger.isEnabled())
     329                 :          0 :         return logger;
     330                 :          9 :     logger << pair.first << ":" << pair.second;
     331                 :          9 :     return logger;
     332                 :            : }
     333                 :            : 
     334                 :            : /**
     335                 :            :  * @brief end of tuple operator
     336                 :            :  */
     337                 :            : template<size_t n, typename... T>
     338                 :            : typename std::enable_if<!exclude_default<std::tuple<T...>>::value &&
     339                 :            :                             (n >= sizeof...(T)),
     340                 :            :                         const Logger &>::type
     341                 :          1 :     operator<<(const Logger &logger, const std::tuple<T...> &)
     342                 :            : {
     343                 :          1 :     return logger;
     344                 :            : }
     345                 :            : 
     346                 :            : /**
     347                 :            :  * @brief tuple operator
     348                 :            :  */
     349                 :            : template<size_t n = 0, typename... T>
     350                 :            : typename std::enable_if<!exclude_default<std::tuple<T...>>::value &&
     351                 :            :                             (n < sizeof...(T)),
     352                 :            :                         const Logger &>::type
     353                 :          2 :     operator<<(const Logger &logger, const std::tuple<T...> &tup)
     354                 :            : {
     355                 :          2 :     if (!logger.isEnabled())
     356                 :          0 :         return logger;
     357                 :            :     if (n == 0)
     358                 :          1 :         logger << "(";
     359                 :            :     else
     360                 :          1 :         logger << ",";
     361                 :          2 :     logger << std::get<n>(tup);
     362                 :          2 :     operator<< <n + 1>(logger, tup);
     363                 :            :     if (n == 0)
     364                 :          1 :         logger << ")";
     365                 :          2 :     return logger;
     366                 :            : }
     367                 :            : 
     368                 :            : constexpr std::size_t MAX_CONTAINER_ITEMS = 20;
     369                 :            : constexpr std::ostringstream::off_type MAX_CONTAINER_LEN = 30;
     370                 :            : 
     371                 :            : /**
     372                 :            :  * @brief stl containers support (any)
     373                 :            :  * @details requirement is to be range-iterable and have value_type typedef
     374                 :            :  * @details container is limited to 20 items or 30 characters on non-debug
     375                 :            :  * loggers.
     376                 :            :  */
     377                 :            : template<typename T>
     378                 :            : typename std::enable_if<!exclude_default<T>::value &&
     379                 :            :                             is_iterable_container<T>::value &&
     380                 :            :                             !has_toString<T>::value && !has_to_string<T>::value,
     381                 :            :                         const Logger &>::type
     382                 :         12 :     operator<<(const Logger &logger, const T &value)
     383                 :            : {
     384                 :         12 :     if (!logger.isEnabled())
     385                 :          0 :         return logger;
     386                 :         12 :     logger << (has_mapped_type<T>::value ? "{" : "[");
     387                 :         12 :     std::size_t count = 0;
     388                 :         12 :     const std::ostringstream::pos_type pos = logger.writer.buf.tellp();
     389                 :         50 :     for (const auto &v : value)
     390                 :            :     {
     391                 :         41 :         if (count++)
     392                 :         29 :             logger << ",";
     393                 :         41 :         logger << v;
     394                 :         82 :         if (logger.level != Level::DEBUG &&
     395                 :         41 :             (count >= MAX_CONTAINER_ITEMS ||
     396                 :         82 :              logger.writer.buf.tellp() >= (pos + MAX_CONTAINER_LEN)))
     397                 :            :         {
     398                 :          3 :             logger << "...";
     399                 :          3 :             break;
     400                 :            :         }
     401                 :            :     }
     402                 :         12 :     logger << (has_mapped_type<T>::value ? "}" : "]");
     403                 :         12 :     return logger;
     404                 :            : }
     405                 :            : 
     406                 :            : /**
     407                 :            :  * @brief millisecond duration logging operator
     408                 :            :  */
     409                 :            : const Logger &operator<<(const Logger &logger,
     410                 :            :                          const std::chrono::milliseconds &ms);
     411                 :            : 
     412                 :            : /**
     413                 :            :  * @brief NullLogger discards everything
     414                 :            :  */
     415                 :            : class NullLogger : public Logger
     416                 :            : {
     417                 :            : public:
     418                 :            :     explicit NullLogger(Level level);
     419                 :            : 
     420                 :            :     template<typename T>
     421                 :          1 :     NullLogger &operator<<(T)
     422                 :            :     {
     423                 :          1 :         return *this;
     424                 :            :     }
     425                 :            : };
     426                 :            : 
     427                 :            : /**
     428                 :            :  * @brief Configuration object
     429                 :            :  * @details use this object to modify the logger behavior
     430                 :            :  */
     431                 :            : class Config
     432                 :            : {
     433                 :            : public:
     434                 :            :     /**
     435                 :            :      * @brief reset configuration and reload from env
     436                 :            :      */
     437                 :            :     Config &reset();
     438                 :            : 
     439                 :            :     /** @brief get current log level */
     440                 :            :     Level getLevel() const;
     441                 :            :     /** @brief set log level */
     442                 :            :     Config &setLevel(Level level);
     443                 :            : 
     444                 :            :     /**
     445                 :            :      * @brief clear and set category filter
     446                 :            :      * @param filter filters to set
     447                 :            :      * @details use an empty string to clear filters
     448                 :            :      */
     449                 :            :     Config &setFilter(const std::string &filter = std::string());
     450                 :            :     /**
     451                 :            :      * @brief add a new filter
     452                 :            :      * @param filter filters to append
     453                 :            :      * @details filters are appended to existing ones, being less prioritary
     454                 :            :      */
     455                 :            :     Config &addFilter(const std::string &filter);
     456                 :            :     /** @brief get current filters */
     457                 :            :     std::string getFilter() const;
     458                 :            : 
     459                 :            :     /** @brief enable a feature */
     460                 :            :     Config &enable(Feature feature);
     461                 :            :     /** @brief disable a feature */
     462                 :            :     Config &disable(Feature feature);
     463                 :            :     /** @brief check if a feature is enabled */
     464                 :            :     bool isEnabled(Feature feature) const;
     465                 :            : 
     466                 :            :     /**
     467                 :            :      * @brief Set the line writing function
     468                 :            :      *
     469                 :            :      * @param fun function pointer to set
     470                 :            :      * @details the writer function must return `true` if the given line has
     471                 :            :      * been consumed
     472                 :            :      */
     473                 :            :     Config &setWriteFunc(write_func_t fun);
     474                 :            : };
     475                 :            : 
     476                 :            : /**
     477                 :            :  * @brief reset stream flags to default value
     478                 :            :  * @details use as: debug() << std::fixed << std::setprecision(42) << 1.23 <<
     479                 :            :  * logger::clearflags;
     480                 :            :  */
     481                 :            : template<typename CharT, typename Traits>
     482                 :          1 : std::basic_ostream<CharT, Traits> &clearflags(
     483                 :            :     std::basic_ostream<CharT, Traits> &os)
     484                 :            : {
     485                 :          1 :     os.setf(std::ios_base::skipws | std::ios_base::dec,
     486                 :            :             ~std::ios_base::fmtflags(0));
     487                 :          1 :     os.width(0);
     488                 :          1 :     os.precision(6);
     489                 :          1 :     os.fill(os.widen(' '));
     490                 :          1 :     return os;
     491                 :            : }
     492                 :            : 
     493                 :            : /**
     494                 :            :  * @brief create a Logger object
     495                 :            :  * @param  level log level
     496                 :            :  * @param  category associated category
     497                 :            :  */
     498                 :            : Logger log(Level level, const std::string &category = std::string());
     499                 :            : 
     500                 :            : #ifdef NDEBUG
     501                 :            : 
     502                 :            : inline NullLogger debug(const std::string & = std::string())
     503                 :            : {
     504                 :            :     return NullLogger(Level::DEBUG);
     505                 :            : }
     506                 :            : 
     507                 :            : #else
     508                 :            : 
     509                 :            : /** @brief create a Level::DEBUG Logger */
     510                 :         12 : inline Logger debug(const std::string &category = std::string())
     511                 :            : {
     512                 :         12 :     return log(Level::DEBUG, category);
     513                 :            : }
     514                 :            : 
     515                 :            : #endif
     516                 :            : 
     517                 :            : /** @brief create a Level::INFO Logger */
     518                 :         23 : inline Logger info(const std::string &category = std::string())
     519                 :            : {
     520                 :         23 :     return log(Level::INFO, category);
     521                 :            : }
     522                 :            : 
     523                 :            : /** @brief create a Level::NOTICE Logger */
     524                 :          7 : inline Logger notice(const std::string &category = std::string())
     525                 :            : {
     526                 :          7 :     return log(Level::NOTICE, category);
     527                 :            : }
     528                 :            : 
     529                 :            : /** @brief create a Level::WARNING Logger */
     530                 :          3 : inline Logger warning(const std::string &category = std::string())
     531                 :            : {
     532                 :          3 :     return log(Level::WARNING, category);
     533                 :            : }
     534                 :            : 
     535                 :            : /** @brief create a Level::ERROR Logger */
     536                 :          3 : inline Logger error(const std::string &category = std::string())
     537                 :            : {
     538                 :          3 :     return log(Level::ERROR, category);
     539                 :            : }
     540                 :            : 
     541                 :            : /** @brief create a Level::CRIT Logger */
     542                 :          2 : inline Logger crit(const std::string &category = std::string())
     543                 :            : {
     544                 :          2 :     return log(Level::CRIT, category);
     545                 :            : }
     546                 :            : 
     547                 :            : /** @brief create a Level::ALERT Logger */
     548                 :            : inline Logger alert(const std::string &category = std::string())
     549                 :            : {
     550                 :            :     return log(Level::ALERT, category);
     551                 :            : }
     552                 :            : 
     553                 :            : /** @brief create a Level::EMERG Logger */
     554                 :          2 : inline Logger emerg(const std::string &category = std::string())
     555                 :            : {
     556                 :          2 :     return log(Level::EMERG, category);
     557                 :            : }
     558                 :            : 
     559                 :            : /**
     560                 :            :  * @brief format any number as hex (first casted to unsigned)
     561                 :            :  * @details use it along with `logger::hex`, ex: `logger::crit() <<
     562                 :            :  * logger::hex(42)`
     563                 :            :  * @details quite similar to `logger::crit() << std::hex << 42` but:
     564                 :            :  * - will always pad depending on type width
     565                 :            :  * - will not modify the stream's internal state (no `logger::clearflags`
     566                 :            :  * required)
     567                 :            :  * - will add a prefix (when requested)
     568                 :            :  * - will support `uint8_t` and `int8_t` types (considered as `char` by regular
     569                 :            :  * streams)
     570                 :            :  */
     571                 :            : template<typename T>
     572                 :            : struct FmtHex
     573                 :            : {
     574                 :         14 :     constexpr FmtHex(const T &value, bool prefix = false) :
     575                 :         14 :         value{value}, prefix{prefix}
     576                 :         14 :     {}
     577                 :            :     const T &value;
     578                 :            :     const bool prefix;
     579                 :            : 
     580                 :         12 :     std::string toString() const
     581                 :            :     {
     582                 :         12 :         std::string str((sizeof(T) << 1) + (prefix ? 2 : 0), '0');
     583                 :         12 :         std::string::reverse_iterator it{str.rbegin()};
     584                 :         12 :         for (typename std::make_unsigned<T>::type temp =
     585                 :         12 :                  static_cast<typename std::make_unsigned<T>::type>(value);
     586                 :        116 :              temp != 0; temp = temp >> 4)
     587                 :            :         {
     588                 :        104 :             int8_t digit{static_cast<int8_t>(temp & 0x0F)};
     589                 :        104 :             *it++ = (digit >= 10) ? (digit - 10 + 'A') : (digit + '0');
     590                 :            :         }
     591                 :         12 :         if (prefix)
     592                 :          3 :             str[1] = 'x';
     593                 :         24 :         return str;
     594                 :          0 :     }
     595                 :            : };
     596                 :            : 
     597                 :            : /**
     598                 :            :  * @brief convenience function for FmtHex
     599                 :            :  */
     600                 :            : template<typename T>
     601                 :         14 : inline const FmtHex<T> hex(const T &value, bool prefix = false)
     602                 :            : {
     603                 :         14 :     return FmtHex<T>(value, prefix);
     604                 :            : }
     605                 :            : 
     606                 :            : /**
     607                 :            :  * @brief optimization for FmtHex<uint8_t>
     608                 :            :  */
     609                 :          2 : inline const logger::Logger &operator<<(const logger::Logger &logger,
     610                 :            :                                         const logger::FmtHex<uint8_t> &hex)
     611                 :            : {
     612                 :          2 :     if (logger.isEnabled())
     613                 :            :     {
     614                 :          2 :         if (hex.prefix)
     615                 :          1 :             logger << "0x";
     616                 :          2 :         int8_t digit = ((hex.value >> 4) & 0xF);
     617                 :          2 :         logger << static_cast<char>((digit >= 10) ? (digit - 10 + 'A') :
     618                 :          2 :                                                     (digit + '0'));
     619                 :          2 :         digit = hex.value & 0xF;
     620                 :          2 :         return logger << static_cast<char>((digit >= 10) ? (digit - 10 + 'A') :
     621                 :          2 :                                                            (digit + '0'));
     622                 :            :     }
     623                 :          0 :     return logger;
     624                 :            : }
     625                 :            : 
     626                 :            : template<typename T>
     627                 :            : struct exclude_default<std::shared_ptr<T>> : std::true_type
     628                 :            : {};
     629                 :            : 
     630                 :            : template<typename T>
     631                 :            : struct exclude_default<std::unique_ptr<T>> : std::true_type
     632                 :            : {};
     633                 :            : 
     634                 :            : template<typename T>
     635                 :            : typename std::enable_if<
     636                 :            :     std::is_same<std::unique_ptr<typename T::element_type>, T>::value ||
     637                 :            :         std::is_same<std::shared_ptr<typename T::element_type>, T>::value,
     638                 :            :     const logger::Logger &>::type
     639                 :          4 :     operator<<(const logger::Logger &logger, const T &ptr)
     640                 :            : {
     641                 :          4 :     if (logger.isEnabled())
     642                 :            :     {
     643                 :          4 :         if (ptr)
     644                 :          2 :             return logger << *ptr;
     645                 :            :         else
     646                 :          2 :             return logger << "nullptr";
     647                 :            :     }
     648                 :          0 :     return logger;
     649                 :            : }
     650                 :            : 
     651                 :            : } // namespace logger
     652                 :            : 
     653                 :            : #endif

Generated by: LCOV version 1.14