LCOV - code coverage report
Current view: top level - utilities/include/mcrl2/utilities - logger.h (source / functions) Hit Total Coverage
Test: mcrl2_coverage.info.cleaned Lines: 60 86 69.8 %
Date: 2024-04-13 03:38:08 Functions: 23 28 82.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Author(s): Jeroen Keiren
       2             : // Copyright: see the accompanying file COPYING or copy at
       3             : // https://github.com/mCRL2org/mCRL2/blob/master/COPYING
       4             : //
       5             : // Distributed under the Boost Software License, Version 1.0.
       6             : // (See accompanying file LICENSE_1_0.txt or copy at
       7             : // http://www.boost.org/LICENSE_1_0.txt)
       8             : //
       9             : /// \file logger.h
      10             : 
      11             : #ifndef MCRL2_UTILITIES_LOGGER_H
      12             : #define MCRL2_UTILITIES_LOGGER_H
      13             : 
      14             : #include <atomic>
      15             : #include <cstdio>
      16             : #include <ctime>
      17             : #include <map>
      18             : #include <memory>
      19             : #include <set>
      20             : #include <stdexcept>
      21             : 
      22             : #include "mcrl2/utilities/text_utility.h"
      23             : 
      24             : namespace mcrl2::log {
      25             : 
      26             : /// \brief Log levels that are supported
      27             : /// \note log_debugi with i>=1 automatically indent 2*i spaces.
      28             : enum log_level_t
      29             : {
      30             :   quiet, // No log message should ever be printed to this log level!
      31             :   error,
      32             :   warning,
      33             :   info,
      34             :   status, // This is overwritten in the output when printed consecutively, and the lowest level that is shown by default.
      35             :   verbose,
      36             :   debug,
      37             :   trace,
      38             : };
      39             : 
      40             : /// \brief Convert log level to string
      41             : /// This string is used to prefix messages in the logging output.
      42             : inline
      43        7428 : std::string log_level_to_string(const log_level_t level)
      44             : {
      45             :   static const char* const buffer[] = {"quiet", "error", "warning", "info", "status", "verbose", "debug", "trace"};
      46        7428 :   return buffer[level];
      47             : }
      48             : 
      49             : /// \brief Convert string to log level
      50             : inline
      51           0 : log_level_t log_level_from_string(const std::string& s)
      52             : {
      53           0 :   if (s == "quiet")
      54             :   {
      55           0 :     return quiet;
      56             :   }
      57           0 :   else if (s == "error")
      58             :   {
      59           0 :     return error;
      60             :   }
      61           0 :   else if (s == "warning")
      62             :   {
      63           0 :     return warning;
      64             :   }
      65           0 :   else if (s == "info")
      66             :   {
      67           0 :     return info;
      68             :   }
      69           0 :   else if (s == "status")
      70             :   {
      71           0 :     return status;
      72             :   }
      73           0 :   else if (s == "verbose")
      74             :   {
      75           0 :     return verbose;
      76             :   }
      77           0 :   else if (s == "debug")
      78             :   {
      79           0 :     return debug;
      80             :   }
      81           0 :   else if (s == "trace")
      82             :   {
      83           0 :     return trace;
      84             :   }
      85             :   else
      86             :   {
      87           0 :     throw std::runtime_error("Unknown log-level " + s + " provided.");
      88             :   }
      89             : }
      90             : 
      91             : std::string format_time(const time_t* t);
      92             : 
      93             : /// \brief Interface class for output policy.
      94             : ///
      95             : /// Separates the exact way of doing output from the logger class.
      96             : class output_policy
      97             : {
      98             :   public:
      99             :     /// \brief Constructor.
     100          30 :     output_policy()
     101          30 :     {}
     102             : 
     103             :     /// \brief Destructor.
     104          30 :     virtual ~output_policy()
     105          30 :     {}
     106             : 
     107             :     /// \brief Output message.
     108             :     /// \param[in] msg Message that is written to output.
     109             :     /// \param[in] timestamp Timestamp to use in the output.
     110             :     /// \param[in] level The log level to print the message to.
     111             :     /// \param[in] print_time_information An indication whether the time must be printed.
     112             :     ///  \details Any implementation must assure that output is written using an atomic action, to prevent
     113             :     /// mixing of different lines into one line in the output.
     114             :     virtual void output(const log_level_t level, const time_t timestamp, const std::string& msg, const bool print_time_information) = 0;
     115             : };
     116             : 
     117             : std::set<output_policy*> initialise_output_policies();
     118             : 
     119             : /// \brief Class for logging messages
     120             : ///
     121             : /// Based on a description in the article "Logging In C++", Petru Marginean
     122             : /// Dr. Dobb's Journal, September 5, 2007
     123             : /// (url: http://drdobbs.com/cpp/201804215)
     124             : /// Requires that OutputPolicy is a class which as a static member output(const std::string&)
     125             : class logger
     126             : {
     127             :   public:
     128             :   // Prevent copying loggers
     129             :   private:
     130             :     logger(const logger&)
     131             :     {}
     132             : 
     133             :     logger& operator =(const logger&)
     134             :     {
     135             :       return *this;
     136             :     }
     137             : 
     138             :   protected:
     139             :     /// \brief Stream that is printed to internally
     140             :     /// Collects the full debug message that we are currently printing.
     141             :     std::ostringstream m_os;
     142             : 
     143             :     /// \brief The loglevel of the current message
     144             :     log_level_t m_level;
     145             : 
     146             :     /// \brief Timestamp of the current message
     147             :     time_t m_timestamp;
     148             : 
     149      226146 :    static std::atomic<log_level_t>& log_level()
     150             :     {
     151             :       static std::atomic<log_level_t> g_log_level(log_level_t::info);
     152      226146 :       return g_log_level;
     153             :     }
     154             : 
     155             :     /// \brief An indication whether time information should be printed.
     156        4245 :     static bool& m_print_time_information()
     157             :     {
     158             :       thread_local bool print_timing_info=false;
     159        4245 :       return print_timing_info;
     160             :     }
     161             : 
     162             :     /// \brief Output policies
     163             :     static
     164        4245 :     std::set<output_policy*>& output_policies()
     165             :     {
     166        4245 :       static std::set<output_policy*> m_output_policies = initialise_output_policies();
     167        4245 :       return m_output_policies;
     168             :     }
     169             : 
     170             :   public:
     171             :     /// \brief Default constructor
     172        4245 :     logger()
     173        4245 :     {}
     174             : 
     175             :     /// \brief Destructor; flushes output.
     176             :     /// Flushing during destruction is important to confer thread safety to the
     177             :     /// logging mechanism. Requires that output performs output in an atomic way.
     178        4245 :     ~logger()
     179             :     {
     180        8490 :       for(output_policy* policy: output_policies())
     181             :       {
     182        4245 :         policy->output(m_level, m_timestamp, m_os.str(), m_print_time_information());
     183             :       }
     184        4245 :     }
     185             : 
     186             :     /// \brief Register output policy
     187             :     static
     188           0 :     void register_output_policy(output_policy& policy)
     189             :     {
     190           0 :       output_policies().insert(&policy);
     191           0 :     }
     192             : 
     193             :     /// \brief Unregister output policy
     194             :     static
     195             :     void unregister_output_policy(output_policy& policy)
     196             :     {
     197             :       std::set<output_policy*>::iterator i = output_policies().find(&policy);
     198             :       if(i != output_policies().end())
     199             :       {
     200             :         output_policies().erase(i);
     201             :       }
     202             :     }
     203             : 
     204             :     /// \brief Clear all output policies
     205             :     static
     206             :     void clear_output_policies()
     207             :     {
     208             :       output_policies().clear();
     209             :     }
     210             : 
     211             :     /// \brief Set reporting level
     212             :     /// \param[in] level Log level
     213             :     static
     214           4 :     void set_reporting_level(const log_level_t level)
     215             :     {
     216           4 :       log_level() = level;
     217           4 :     }
     218             : 
     219             :     /// \brief Get reporting level
     220             :     static
     221      226142 :     log_level_t get_reporting_level()
     222             :     {
     223      226142 :       return log_level();
     224             :     }
     225             : 
     226             :     /// \brief Indicate that timing information should be printed.
     227           0 :     static void set_report_time_info()
     228             :     {
     229           0 :       m_print_time_information() = true;
     230           0 :     }
     231             : 
     232             :     /// \brief Indicate that timing information should not be printed.
     233             :     static void clear_report_time_info()
     234             :     {
     235             :       m_print_time_information() = false;
     236             :     }
     237             : 
     238             :     /// \brief Get whether timing information is printed.
     239             :     /// \return True if time information is printed, otherwise false.
     240             :     static bool get_report_time_info() 
     241             :     {
     242             :       return m_print_time_information();
     243             :     }
     244             : 
     245             :     /// Get access to the stream provided by the logger.
     246             :     /// \param[in] l Log level for the stream
     247        4245 :     std::ostringstream& get(const log_level_t l)
     248             :     {
     249        4245 :       m_level = l;
     250        4245 :       std::time(&m_timestamp);
     251        4245 :       return m_os;
     252             :     }
     253             : };
     254             : 
     255             : class formatter_interface
     256             : {
     257             : public:
     258             :   /// \brief Format msg,
     259             :   /// \param[in] level The log level of the message
     260             :   /// \param[in] timestamp The timestamp of the log message
     261             :   /// \param[in] msg The message to be formatted
     262             :   /// \param[in] print_time_information A boolean that if true indicates that time usage information must be printed. If false this 
     263             :   ///                                   information is suppressed. 
     264             :   /// \return The formatted message (\a msg)
     265             :   static std::string format(const log_level_t level, const time_t timestamp, const std::string& msg, const bool print_time_information)
     266             :   {
     267             :     /// suppress non used variable warnings.
     268             :     (void)level; (void)timestamp; (void)print_time_information;
     269             : 
     270             :     return msg;
     271             :   }
     272             : }; 
     273             : 
     274             : /// \brief Mixin that takes care of formatting of a message.
     275             : ///
     276             : /// In this case, the formatter
     277             : class formatter: public formatter_interface
     278             : {
     279             : protected:
     280             :   /// \brief Records whether the last message that was printed ended with
     281             :   ///        a new line.
     282             :   static
     283        8484 :   std::atomic<bool>& last_message_ended_with_newline()
     284             :   {
     285             :     static std::atomic<bool> m_last_message_ended_with_newline = true;
     286        8484 :     return m_last_message_ended_with_newline;
     287             :   }
     288             : 
     289             :   static
     290        8490 :   std::atomic<bool>& last_message_was_status()
     291             :   {
     292             :     static std::atomic<bool> m_last_message_was_status = false;
     293        8490 :     return m_last_message_was_status;
     294             :   }
     295             : 
     296             :   static
     297      478368 :   std::atomic<std::size_t>& caret_pos()
     298             :   {
     299             :     static std::atomic<std::size_t> m_caret_pos = 0;
     300      478368 :     return m_caret_pos;
     301             :   }
     302             : 
     303             :   static
     304        3398 :   std::atomic<std::size_t>& last_caret_pos()
     305             :   {
     306             :     static std::atomic<std::size_t> m_last_caret_pos = 0;
     307        3398 :     return m_last_caret_pos;
     308             :   }
     309             : 
     310             : public:
     311             :   /// \brief Prefix each line in s with some extra information.
     312             :   /// The things that are added are:
     313             :   /// - current time
     314             :   /// - log level
     315             :   /// - indentation
     316             :   static std::string format(const log_level_t level, const time_t timestamp, const std::string& msg, const bool print_time_information);
     317             : };
     318             : 
     319             : /// \brief File output class.
     320             : ///
     321             : /// Provides facilities to output to a file. By default output is sent to stderr.
     322             : class file_output: public output_policy
     323             : {
     324             :   protected:
     325             :     /// \brief Obtain the underlying stream used to print to a file.
     326        4247 :     static std::atomic<FILE*>& get_stream()
     327             :     {
     328        4247 :       static std::atomic<FILE*> g_stream(stderr);
     329        4247 :       return g_stream;
     330             :     }
     331             : 
     332             :   public:
     333          30 :     file_output()
     334          30 :     {}
     335             : 
     336          30 :     virtual ~file_output()
     337          30 :     {}
     338             :  
     339             :     /// \param[in] stream A file handle
     340             :     static
     341           2 :     void set_stream(FILE* stream)
     342             :     {
     343           2 :       get_stream() = stream;
     344           2 :     }
     345             : 
     346             :     /// Output message to stream.
     347             :     /// \param[in] level The log level on which to output the message
     348             :     /// \param[in] timestamp The timestamp to use for the message
     349             :     /// \param[in] msg The message to be printed
     350             :     /// \param[in] print_time_information A boolean that if true indicates that time usage information must be printed. If false this 
     351             :     ///                                   information is suppressed. 
     352             :     ///
     353             :     /// \note This uses fprintf (and not e.g. <<) because fprintf is guaranteed to be
     354             :     /// atomic.
     355        4245 :     virtual void output(const log_level_t level, const time_t timestamp, const std::string& msg, const bool print_time_information) override
     356             :     {
     357        4245 :       FILE* p_stream = get_stream();
     358        4245 :       if (!p_stream)
     359             :       {
     360           0 :         return;
     361             :       }
     362             : 
     363        4245 :       fprintf(p_stream, "%s", formatter::format(level, timestamp, msg, print_time_information).c_str());
     364        4245 :       fflush(p_stream);
     365             :     }
     366             : };
     367             : 
     368             : /// \brief The default output policy used by the logger
     369             : inline
     370          30 : output_policy& default_output_policy()
     371             : {
     372          30 :   static file_output m_default = file_output();
     373          30 :   return m_default;
     374             : }
     375             : 
     376             : /// \brief Initialise the output policies. This returns the singleton set
     377             : ///        containing the default output policy.
     378             : inline
     379          30 : std::set<output_policy*> initialise_output_policies()
     380             : {
     381          30 :   std::set<output_policy*> result;
     382          30 :   result.insert(&default_output_policy());
     383          30 :   return result;
     384           0 : }
     385             : 
     386             : /// \returns True whenever the logging for the given level is enabled.
     387      226142 : inline bool mCRL2logEnabled(const log_level_t level)
     388             : {
     389      226142 :   return level <= mcrl2::log::logger::get_reporting_level();
     390             : }
     391             : 
     392             : } // namespace mcrl2::log
     393             : 
     394             : /// \brief mCRL2log(LEVEL) provides the stream used to log.
     395             : #define mCRL2log(LEVEL) if (mcrl2::log::mCRL2logEnabled(LEVEL)) mcrl2::log::logger().get(LEVEL)
     396             : 
     397             : #endif // MCRL2_UTILITIES_LOGGER_H

Generated by: LCOV version 1.14