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: 89 119 74.8 %
Date: 2020-02-21 00:44:40 Functions: 29 33 87.9 %
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 <cstdio>
      15             : #include <ctime>
      16             : #include <map>
      17             : #include <memory>
      18             : #include <set>
      19             : #include <stdexcept>
      20             : 
      21             : #include "mcrl2/utilities/text_utility.h"
      22             : 
      23             : namespace mcrl2 {
      24             : 
      25             :   namespace log {
      26             : 
      27             : /// \brief Log levels that are supported
      28             : /// \note log_debugi with i>=1 automatically indent 2*i spaces.
      29             : enum log_level_t
      30             : {
      31             :   quiet, // No log message should ever be printed to this log level!
      32             :   error,
      33             :   warning,
      34             :   info,
      35             :   status,
      36             :   verbose,
      37             :   debug,
      38             :   debug1,
      39             :   debug2,
      40             :   debug3,
      41             :   debug4,
      42             :   debug5
      43             : };
      44             : 
      45             : /// \brief Convert log level to string
      46             : /// This string is used to prefix messages in the logging output.
      47             : inline
      48        7526 : std::string log_level_to_string(const log_level_t level)
      49             : {
      50             :   static const char* const buffer[] = {"quiet", "error", "warning", "info", "status", "verbose", "debug", "debug1", "debug2", "debug3", "debug4", "debug5"};
      51        7526 :   return buffer[level];
      52             : }
      53             : 
      54             : /// \brief Convert string to log level
      55             : inline
      56           0 : log_level_t log_level_from_string(const std::string& s)
      57             : {
      58           0 :   if (s == "quiet")
      59             :   {
      60           0 :     return quiet;
      61             :   }
      62           0 :   else if (s == "error")
      63             :   {
      64           0 :     return error;
      65             :   }
      66           0 :   else if (s == "warning")
      67             :   {
      68           0 :     return warning;
      69             :   }
      70           0 :   else if (s == "info")
      71             :   {
      72           0 :     return info;
      73             :   }
      74           0 :   else if (s == "status")
      75             :   {
      76           0 :     return status;
      77             :   }
      78           0 :   else if (s == "verbose")
      79             :   {
      80           0 :     return verbose;
      81             :   }
      82           0 :   else if (s == "debug")
      83             :   {
      84           0 :     return debug;
      85             :   }
      86           0 :   else if (s == "debug1")
      87             :   {
      88           0 :     return debug1;
      89             :   }
      90           0 :   else if (s == "debug2")
      91             :   {
      92           0 :     return debug2;
      93             :   }
      94           0 :   else if (s == "debug3")
      95             :   {
      96           0 :     return debug3;
      97             :   }
      98           0 :   else if (s == "debug4")
      99             :   {
     100           0 :     return debug4;
     101             :   }
     102           0 :   else if (s == "debug5")
     103             :   {
     104           0 :     return debug5;
     105             :   }
     106             :   else
     107             :   {
     108           0 :     throw std::runtime_error("Unknown log-level " + s + " provided.");
     109             :   }
     110             : }
     111             : 
     112             : 
     113             : std::string format_time(const time_t* t);
     114             : 
     115             : /// \brief Interface class for output policy.
     116             : ///
     117             : /// Separates the exact way of doing output from the logger class.
     118             : class output_policy
     119             : {
     120             :   public:
     121             :     /// \brief Constructor.
     122          27 :     output_policy()
     123          27 :     {}
     124             : 
     125             :     /// \brief Destructor.
     126          27 :     virtual ~output_policy()
     127          27 :     {}
     128             : 
     129             :     /// \brief Output message.
     130             :     /// \param[in] msg Message that is written to output.
     131             :     /// \param[in] hint Hint for the stream to which the output is written.
     132             :     /// \param[in] timestamp Timestamp to use in the output.
     133             :     /// \param[in] level The log level to print the message to.
     134             :     /// \param[in] print_time_information An indication whether the time must be printed.
     135             :     ///  \details Any implementation must assure that output is written using an atomic action, to prevent
     136             :     /// mixing of different lines into one line in the output.
     137             :     virtual void output(const log_level_t level, const std::string& hint, const time_t timestamp, const std::string& msg, const bool print_time_information) = 0;
     138             : };
     139             : 
     140             : std::set<output_policy*> initialise_output_policies();
     141             : 
     142             : /// \brief Class for logging messages
     143             : ///
     144             : /// Based on a description in the article "Logging In C++", Petru Marginean
     145             : /// Dr. Dobb's Journal, September 5, 2007
     146             : /// (url: http://drdobbs.com/cpp/201804215)
     147             : /// Requires that OutputPolicy is a class which as a static member output(const std::string&)
     148             : class logger
     149             : {
     150             :   public:
     151             :   // Prevent copying loggers
     152             :   private:
     153             :     logger(const logger&)
     154             :     {}
     155             : 
     156             :     logger& operator =(const logger&)
     157             :     {
     158             :       return *this;
     159             :     }
     160             : 
     161             :   protected:
     162             :     /// \brief Stream that is printed to internally
     163             :     /// Collects the full debug message that we are currently printing.
     164             :     std::ostringstream m_os;
     165             : 
     166             :     /// \brief The loglevel of the current message
     167             :     log_level_t m_level;
     168             : 
     169             :     /// \brief An indication whether time information should be printed.
     170        4137 :     static bool& m_print_time_information()
     171             :     {
     172             :       static bool print_timing_info=false;
     173        4137 :       return print_timing_info;
     174             :     }
     175             : 
     176             :     /// \brief The message hint of the current message
     177             :     std::string m_hint;
     178             : 
     179             :     /// \brief Timestamp of the current message
     180             :     time_t m_timestamp;
     181             : 
     182             :     /// \brief Output policies
     183             :     static
     184        4137 :     std::set<output_policy*>& output_policies()
     185             :     {
     186        4137 :       static std::set<output_policy*> m_output_policies = initialise_output_policies();
     187        4137 :       return m_output_policies;
     188             :     }
     189             : 
     190             :     /// \brief Mapping of message hint to loglevel. This allows a finegrained
     191             :     /// control of log messages to log levels. It can e.g. be set that for some
     192             :     /// message hint all messages up to debug level are printed, whereas for other
     193             :     /// message hints no messages are printed at all.
     194             :     static
     195      140037 :     std::map<std::string, log_level_t>& hint_to_level()
     196             :     {
     197      140037 :       static std::map<std::string, log_level_t> m_hint_to_level;
     198      140037 :       return m_hint_to_level;
     199             :     }
     200             : 
     201             :   public:
     202             :     /// \brief The default log level that is used if no specific log level has
     203             :     /// been set.
     204             :     static
     205       54292 :     log_level_t default_reporting_level()
     206             :     {
     207       54292 :       std::map<std::string, log_level_t>::const_iterator i = hint_to_level().find(default_hint());
     208       54292 :       if(i != hint_to_level().end())
     209             :       {
     210        7228 :         return i->second;
     211             :       }
     212             :       else
     213             :       {
     214       47064 :         return info;
     215             :       }
     216             :     }
     217             : 
     218             :     /// \brief Default constructor
     219        4137 :     logger()
     220        4137 :     {}
     221             : 
     222             :     /// \brief Destructor; flushes output.
     223             :     /// Flushing during destruction is important to confer thread safety to the
     224             :     /// logging mechanism. Requires that output performs output in an atomic way.
     225        4137 :     ~logger()
     226        4137 :     {
     227        8274 :       for(output_policy* policy: output_policies())
     228             :       {
     229        4137 :         policy->output(m_level, m_hint, m_timestamp, m_os.str(), m_print_time_information());
     230             :       }
     231        4137 :     }
     232             : 
     233             :     /// \brief Default hint (empty)
     234       59046 :     static std::string default_hint()
     235             :     {
     236       59046 :       static std::string default_hint;
     237       59046 :       return default_hint;
     238             :     }
     239             : 
     240             :     /// \brief Register output policy
     241             :     static
     242           0 :     void register_output_policy(output_policy& policy)
     243             :     {
     244           0 :       output_policies().insert(&policy);
     245           0 :     }
     246             : 
     247             :     /// \brief Unregister output policy
     248             :     static
     249             :     void unregister_output_policy(output_policy& policy)
     250             :     {
     251             :       std::set<output_policy*>::iterator i = output_policies().find(&policy);
     252             :       if(i != output_policies().end())
     253             :       {
     254             :         output_policies().erase(i);
     255             :       }
     256             :     }
     257             : 
     258             :     /// \brief Clear all output policies
     259             :     static
     260             :     void clear_output_policies()
     261             :     {
     262             :       output_policies().clear();
     263             :     }
     264             : 
     265             :     /// \brief Set reporting level
     266             :     /// \param[in] level Log level
     267             :     /// \param[in] hint The hint for which to set log level
     268             :     static
     269          10 :     void set_reporting_level(const log_level_t level, const std::string& hint = default_hint())
     270             :     {
     271          10 :       hint_to_level()[hint] = level;
     272          10 :     }
     273             : 
     274             :     /// \brief Get reporting level
     275             :     /// \param[in] hint The hint for which to get log level
     276             :     static
     277       15721 :     log_level_t get_reporting_level(const std::string& hint = default_hint())
     278             :     {
     279       15721 :       std::map<std::string, log_level_t>::const_iterator i = hint_to_level().find(hint);
     280       15721 :       if(i != hint_to_level().end())
     281             :       {
     282         314 :         return i->second;
     283             :       }
     284             :       else
     285             :       {
     286       15407 :         return default_reporting_level();
     287             :       }
     288             :     }
     289             : 
     290             :     /// \brief Clear reporting level
     291             :     /// \param hint Reset the log level for hint
     292             :     static
     293           1 :     void clear_reporting_level(const std::string& hint)
     294             :     {
     295           1 :       hint_to_level().erase(hint);
     296           1 :     }
     297             : 
     298             :     /// \brief Indicate that timing information should be printed.
     299             :     static void set_report_time_info()
     300             :     {
     301             :       m_print_time_information()=true;
     302             :     }
     303             : 
     304             :     /// \brief Indicate that timing information should not be printed.
     305             :     static void clear_report_time_info()
     306             :     {
     307             :       m_print_time_information()=false;
     308             :     }
     309             : 
     310             :     /// \brief Get whether timing information is printed.
     311             :     /// \return True if time information is printed, otherwise false.
     312             :     static bool get_report_time_info() 
     313             :     {
     314             :       return m_print_time_information();
     315             :     }
     316             : 
     317             :     /// Get access to the stream provided by the logger.
     318             :     /// \param[in] l Log level for the stream
     319             :     /// \param[in] hint The hint for which the stream has to be provided.
     320        4137 :     std::ostringstream& get(const log_level_t l, const std::string& hint = default_hint())
     321             :     {
     322        4137 :       m_level = l;
     323        4137 :       m_hint = hint;
     324        4137 :       std::time(&m_timestamp);
     325        4137 :       return m_os;
     326             :     }
     327             : };
     328             : 
     329             : class formatter_interface
     330             : {
     331             : public:
     332             :   /// \brief Format msg,
     333             :   /// \param[in] level The log level of the message
     334             :   /// \param[in] hint The hint provided for the message
     335             :   /// \param[in] timestamp The timestamp of the log message
     336             :   /// \param[in] msg The message to be formatted
     337             :   /// \param[in] print_time_information A boolean that if true indicates that time usage information must be printed. If false this 
     338             :   ///                                   information is suppressed. 
     339             :   /// \return The formatted message (\a msg)
     340             :   static std::string format(const log_level_t level, const std::string& hint, const time_t timestamp, const std::string& msg, const bool print_time_information)
     341             :   {
     342             :     /// suppress non used variable warnings.
     343             :     (void)level; (void)hint; (void)timestamp; (void)print_time_information;
     344             : 
     345             :     return msg;
     346             :   }
     347             : }; 
     348             : 
     349             : /// \brief Mixin that takes care of formatting of a message.
     350             : ///
     351             : /// In this case, the formatter
     352             : class formatter: public formatter_interface
     353             : {
     354             : protected:
     355             :   /// \brief Records whether the last message that was printed ended with
     356             :   ///        a new line.
     357             :   static
     358        8268 :   bool& last_message_ended_with_newline()
     359             :   {
     360             :     static bool m_last_message_ended_with_newline = true;
     361        8268 :     return m_last_message_ended_with_newline;
     362             :   }
     363             : 
     364             :   static
     365        8274 :   bool& last_message_was_status()
     366             :   {
     367             :     static bool m_last_message_was_status = false;
     368        8274 :     return m_last_message_was_status;
     369             :   }
     370             : 
     371             :   static
     372          30 :   std::string& last_hint()
     373             :   {
     374          30 :     static std::string m_last_hint;
     375          30 :     return m_last_hint;
     376             :   }
     377             : 
     378             :   static
     379      469847 :   std::size_t& caret_pos()
     380             :   {
     381             :     static std::size_t m_caret_pos = 0;
     382      469847 :     return m_caret_pos;
     383             :   }
     384             : 
     385             :   static
     386        3388 :   std::size_t& last_caret_pos()
     387             :   {
     388             :     static std::size_t m_last_caret_pos = 0;
     389        3388 :     return m_last_caret_pos;
     390             :   }
     391             : 
     392             : public:
     393             :   /// \brief Prefix each line in s with some extra information.
     394             :   /// The things that are added are:
     395             :   /// - current time
     396             :   /// - hint
     397             :   /// - log level
     398             :   /// - indentation
     399             :   static std::string format(const log_level_t level, const std::string& hint, const time_t timestamp, const std::string& msg, const bool print_time_information);
     400             : };
     401             : 
     402             : /// \brief File output class.
     403             : ///
     404             : /// Provides facilities to output to a file. By default output is sent to stderr.
     405             : class file_output: public output_policy
     406             : {
     407             :   protected:
     408             :     /// \brief Map hints to streams
     409             :     /// This allows messages with different hints to be written to different output
     410             :     /// streams.
     411             :     static
     412       16546 :     std::map<std::string, FILE*>& hint_to_stream()
     413             :     {
     414       16546 :       static std::map<std::string, FILE*> m_hint_to_stream;
     415       16546 :       return m_hint_to_stream;
     416             :     }
     417             : 
     418             :     /// \brief Gets a stream handle for hint
     419             :     /// \param[in] hint Hint for which to provide a stream handle.
     420        4137 :     FILE* get_stream(const std::string& hint)
     421             :     {
     422        4137 :       std::map<std::string, FILE*>::iterator i = hint_to_stream().find(hint);
     423        4137 :       if(i == hint_to_stream().end())
     424             :       {
     425        4133 :         i = hint_to_stream().find(logger::default_hint());
     426             :       }
     427        4137 :       if (i == hint_to_stream().end())
     428             :       {
     429        4133 :         return stderr;
     430             :       }
     431             :       else
     432             :       {
     433           4 :         return i->second;
     434             :       }
     435             :     }
     436             : 
     437             :   public:
     438          27 :     file_output()
     439          27 :     {}
     440             : 
     441          27 :     virtual ~file_output()
     442          27 :     {}
     443             : 
     444             :     /// \brief Set stream handle for a hint
     445             :     /// \param[in] stream A file handle
     446             :     /// \param[in] hint The hint for which to set the handle to stream.
     447             :     static
     448           2 :     void set_stream(FILE* stream, const std::string& hint = logger::default_hint())
     449             :     {
     450           2 :       hint_to_stream()[hint] = stream;
     451           2 :     }
     452             : 
     453             :     /// \overload
     454             :     /// Output message to stream.
     455             :     /// \param[in] level The log level on which to output the message
     456             :     /// \param[in] timestamp The timestamp to use for the message
     457             :     /// \param[in] msg The message to be printed
     458             :     /// \param[in] hint The hint of the stream to which we print.
     459             :     /// \param[in] print_time_information A boolean that if true indicates that time usage information must be printed. If false this 
     460             :     ///                                   information is suppressed. 
     461             :     ///
     462             :     /// \note This uses fprintf (and not e.g. <<) because fprintf is guaranteed to be
     463             :     /// atomic.
     464        4137 :     virtual void output(const log_level_t level, const std::string& hint, const time_t timestamp, const std::string& msg, const bool print_time_information)
     465             :     {
     466        4137 :       FILE* p_stream = get_stream(hint);
     467        4137 :       if (!p_stream)
     468             :       {
     469           0 :         return;
     470             :       }
     471             : 
     472        4137 :       fprintf(p_stream, "%s", formatter::format(level, hint, timestamp, msg, print_time_information).c_str());
     473        4137 :       fflush(p_stream);
     474             :     }
     475             : };
     476             : 
     477             : /// \brief The default output policy used by the logger
     478             : inline
     479          27 : output_policy& default_output_policy()
     480             : {
     481          27 :   static file_output m_default = file_output();
     482          27 :   return m_default;
     483             : }
     484             : 
     485             : /// \brief Initialise the output policies. This returns the singleton set
     486             : ///        containing the default output policy.
     487             : inline
     488          27 : std::set<output_policy*> initialise_output_policies()
     489             : {
     490          27 :   std::set<output_policy*> result;
     491          27 :   result.insert(&default_output_policy());
     492          27 :   return result;
     493             : }
     494             : 
     495             : /// \brief Default logger that we use
     496             : typedef logger mcrl2_logger;
     497             : 
     498             : /// \brief Unless otherwise specified, we compile away all debug messages that have
     499             : /// a log level greater than MCRL2MaxLogLevel.
     500             : constexpr log_level_t MCRL2MaxLogLevel = mcrl2::log::debug;
     501             : 
     502             : /// \returns True whenever the logging for the given level is enabled.
     503      191061 : constexpr bool mCRL2logEnabled(const log_level_t level)
     504             : {
     505      191061 :   return level <= MCRL2MaxLogLevel && level <= mcrl2_logger::default_reporting_level();
     506             : }
     507             : 
     508             : /// \returns True whenever the logging for the given level and hint is enabled.
     509       44885 : constexpr bool mCRL2logEnabled(const log_level_t level, const std::string& hint)
     510             : {
     511       44885 :   return level <= MCRL2MaxLogLevel && level <= mcrl2_logger::get_reporting_level(hint);
     512             : }
     513             : 
     514             :   } // namespace log
     515             : } // namespace mcrl2
     516             : 
     517             : /// \brief mCRL2log(level) or mCRL2log(level, hint) provide the stream used to log.
     518             : /// \details Uses variadic macros to allow mCRL2log(level) as well as mCRL2log(level, "hint").
     519             : #define mCRL2log(...) if (mcrl2::log::mCRL2logEnabled(__VA_ARGS__)) mcrl2::log::mcrl2_logger().get(__VA_ARGS__)
     520             : 
     521             : #endif // MCRL2_UTILITIES_LOGGER_H

Generated by: LCOV version 1.13