LCOV - code coverage report
Current view: top level - log - log_formatter.h (source / functions) Hit Total Coverage
Test: report.info Lines: 15 29 51.7 %
Date: 2012-05-15 Functions: 35 43 81.4 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 46 279 16.5 %

           Branch data     Line data    Source code
       1                 :            : // Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
       2                 :            : //
       3                 :            : // Permission to use, copy, modify, and/or distribute this software for any
       4                 :            : // purpose with or without fee is hereby granted, provided that the above
       5                 :            : // copyright notice and this permission notice appear in all copies.
       6                 :            : //
       7                 :            : // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
       8                 :            : // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
       9                 :            : // AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
      10                 :            : // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
      11                 :            : // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
      12                 :            : // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
      13                 :            : // PERFORMANCE OF THIS SOFTWARE.
      14                 :            : 
      15                 :            : #ifndef __LOG_FORMATTER_H
      16                 :            : #define __LOG_FORMMATER_H
      17                 :            : 
      18                 :            : #include <cstddef>
      19                 :            : #include <string>
      20                 :            : #include <iostream>
      21                 :            : 
      22                 :            : #include <exceptions/exceptions.h>
      23                 :            : #include <boost/lexical_cast.hpp>
      24                 :            : #include <log/logger_level.h>
      25                 :            : 
      26                 :            : namespace isc {
      27                 :            : namespace log {
      28                 :            : 
      29                 :            : /// \brief Format Failure
      30                 :            : ///
      31                 :            : /// This exception is used to wrap a bad_lexical_cast exception thrown during
      32                 :            : /// formatting an argument.
      33                 :            : 
      34                 :          0 : class FormatFailure : public isc::Exception {
      35                 :            : public:
      36                 :          0 :     FormatFailure(const char* file, size_t line, const char* what) :
      37 [ #  # ][ #  # ]:          0 :         isc::Exception(file, line, what)
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
      38                 :          0 :     {}
      39                 :            : };
      40                 :            : 
      41                 :            : 
      42                 :            : /// \brief Mismatched Placeholders
      43                 :            : ///
      44                 :            : /// This exception is used when the number of placeholders do not match
      45                 :            : /// the number of arguments passed to the formatter.
      46                 :            : 
      47                 :          0 : class MismatchedPlaceholders : public isc::Exception {
      48                 :            : public:
      49                 :            :     MismatchedPlaceholders(const char* file, size_t line, const char* what) :
      50                 :            :         isc::Exception(file, line, what)
      51                 :            :     {}
      52                 :            : };
      53                 :            : 
      54                 :            : 
      55                 :            : ///
      56                 :            : /// \brief Internal excess placeholder checker
      57                 :            : ///
      58                 :            : /// This is used internally by the Formatter to check for excess
      59                 :            : /// placeholders (and fewer arguments).
      60                 :            : void
      61                 :            : checkExcessPlaceholders(std::string* message, unsigned int placeholder);
      62                 :            : 
      63                 :            : ///
      64                 :            : /// \brief The internal replacement routine
      65                 :            : ///
      66                 :            : /// This is used internally by the Formatter. Replaces a placeholder
      67                 :            : /// in the message by replacement. If the placeholder is not found,
      68                 :            : /// it adds a complain at the end.
      69                 :            : void
      70                 :            : replacePlaceholder(std::string* message, const std::string& replacement,
      71                 :            :                    const unsigned placeholder);
      72                 :            : 
      73                 :            : ///
      74                 :            : /// \brief The log message formatter
      75                 :            : ///
      76                 :            : /// This class allows us to format logging messages conveniently. We
      77                 :            : /// call something like logger.warn(WARN_MSG).arg(15).arg(dnsMsg). This
      78                 :            : /// outputs some text with placeholders replaced by the arguments, if
      79                 :            : /// the logging verbosity is at WARN level or more.
      80                 :            : ///
      81                 :            : /// To make this work, we use the Formatter. The warn (or whatever logging
      82                 :            : /// function) returns a Formatter object. That one holds the string to be
      83                 :            : /// output with the placeholders. It also remembers if there should be any
      84                 :            : /// output at all (eg. if the logging is enabled for this level). When there's
      85                 :            : /// no .arg call on the object, it is destroyed right away and we use the
      86                 :            : /// destructor to output the text (but only in case we should output anything).
      87                 :            : ///
      88                 :            : /// If there's an .arg call, we return reference to the same object, so another
      89                 :            : /// .arg can be called on it. After the last .arg call is done, the object is
      90                 :            : /// destroyed and, again, we can produce the output.
      91                 :            : ///
      92                 :            : /// Of course, if the logging is turned off, we don't bother with any replacing
      93                 :            : /// and just return.
      94                 :            : ///
      95                 :            : /// User of logging code should not really care much about this class, only
      96                 :            : /// call the .arg method to generate the correct output.
      97                 :            : ///
      98                 :            : /// The class is a template to allow easy testing. Also, we want everything
      99                 :            : /// here in the header anyway and it doesn't depend on the details of what
     100                 :            : /// Logger really is, so it doesn't hurt anything.
     101                 :            : ///
     102                 :            : /// Also, if you are interested in the internals, you might find the copy
     103                 :            : /// constructor a bit strange. It deactivates the original formatter. We don't
     104                 :            : /// really want to support copying of the Formatter by user, but C++ needs a
     105                 :            : /// copy constructor when returning from the logging functions, so we need one.
     106                 :            : /// And if we did not deactivate the original Formatter, that one would get
     107                 :            : /// destroyed before any call to .arg, producing an output, and then the one
     108                 :            : /// the .arg calls are called on would get destroyed as well, producing output
     109                 :            : /// again. So, think of this behaviour as soul moving from one to another.
     110                 :            : template<class Logger> class Formatter {
     111                 :            : private:
     112                 :            :     /// \brief The logger we will use to output the final message.
     113                 :            :     ///
     114                 :            :     /// If NULL, we are not active and should not produce anything.
     115                 :            :     mutable Logger* logger_;
     116                 :            : 
     117                 :            :     /// \brief Message severity
     118                 :            :     Severity severity_;
     119                 :            : 
     120                 :            :     /// \brief The messages with %1, %2... placeholders
     121                 :            :     std::string* message_;
     122                 :            : 
     123                 :            :     /// \brief Which will be the next placeholder to replace
     124                 :            :     unsigned nextPlaceholder_;
     125                 :            : 
     126                 :            : 
     127                 :            : public:
     128                 :            :     /// \brief Constructor of "active" formatter
     129                 :            :     ///
     130                 :            :     /// This will create a formatter. If the arguments are set, it
     131                 :            :     /// will be active (will produce output). If you leave them all as NULL,
     132                 :            :     /// it will create an inactive Formatter -- one that'll produce no output.
     133                 :            :     ///
     134                 :            :     /// It is not expected to be called by user of logging system directly.
     135                 :            :     ///
     136                 :            :     /// \param severity The severity of the message (DEBUG, ERROR etc.)
     137                 :            :     /// \param message The message with placeholders. We take ownership of
     138                 :            :     ///     it and we will modify the string. Must not be NULL unless
     139                 :            :     ///     logger is also NULL, but it's not checked.
     140                 :            :     /// \param logger The logger where the final output will go, or NULL
     141                 :            :     ///     if no output is wanted.
     142                 :          0 :     Formatter(const Severity& severity = NONE, std::string* message = NULL,
     143                 :            :               Logger* logger = NULL) :
     144                 :            :         logger_(logger), severity_(severity), message_(message),
     145                 :      41253 :         nextPlaceholder_(0)
     146                 :            :     {
     147                 :          0 :     }
     148                 :            : 
     149                 :            :     /// \brief Copy constructor
     150                 :            :     ///
     151                 :            :     /// "Control" is passed to the created object in that it is the created object
     152                 :            :     /// that will have responsibility for outputting the formatted message - the
     153                 :            :     /// object being copied relinquishes that responsibility.
     154                 :            :     Formatter(const Formatter& other) :
     155                 :            :         logger_(other.logger_), severity_(other.severity_),
     156                 :            :         message_(other.message_), nextPlaceholder_(other.nextPlaceholder_)
     157                 :            :     {
     158                 :            :         other.logger_ = NULL;
     159                 :            :     }
     160                 :            : 
     161                 :            :     /// \brief Destructor.
     162                 :            :     //
     163                 :            :     /// This is the place where output happens if the formatter is active.
     164                 :      41253 :     ~ Formatter() {
     165         [ +  + ]:      41253 :         if (logger_) {
     166                 :      40803 :             checkExcessPlaceholders(message_, ++nextPlaceholder_);
     167                 :      40803 :             logger_->output(severity_, *message_);
     168         [ +  - ]:      40803 :             delete message_;
     169                 :            :         }
     170                 :      41253 :     }
     171                 :            : 
     172                 :            :     /// \brief Assignment operator
     173                 :            :     ///
     174                 :            :     /// Essentially the same function as the assignment operator - the object being
     175                 :            :     /// assigned to takes responsibility for outputting the message.
     176                 :            :     Formatter& operator =(const Formatter& other) {
     177         [ -  + ]:          4 :         if (&other != this) {
     178                 :          0 :             logger_ = other.logger_;
     179                 :          0 :             severity_ = other.severity_;
     180                 :          0 :             message_ = other.message_;
     181                 :          0 :             nextPlaceholder_ = other.nextPlaceholder_;
     182                 :          0 :             other.logger_ = NULL;
     183                 :            :         }
     184                 :            : 
     185                 :            :         return *this;
     186                 :            :     }
     187                 :            : 
     188                 :            :     /// \brief Replaces another placeholder
     189                 :            :     ///
     190                 :            :     /// Replaces another placeholder and returns a new formatter with it.
     191                 :            :     /// Deactivates the current formatter. In case the formatter is not active,
     192                 :            :     /// only produces another inactive formatter.
     193                 :            :     ///
     194                 :            :     /// \param value The argument to place into the placeholder.
     195                 :      69251 :     template<class Arg> Formatter& arg(const Arg& value) {
     196 [ +  + ][ +  - ]:      69254 :         if (logger_) {
         [ +  + ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
                 [ +  - ]
           [ +  -  +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ #  # ][ +  - ]
     197                 :            :             try {
     198 [ +  - ][ +  - ]:      69254 :                 return (arg(boost::lexical_cast<std::string>(value)));
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ +  - ]
         [ +  - ][ #  # ]
                 [ +  - ]
     199 [ #  # ][ #  # ]:          0 :             } catch (const boost::bad_lexical_cast& ex) {
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
            [ # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     200                 :            : 
     201                 :            :                 // A bad_lexical_cast during a conversion to a string is
     202                 :            :                 // *extremely* unlikely to fail.  However, there is nothing
     203                 :            :                 // in the documentation that rules it out, so we need to handle
     204                 :            :                 // it.  As it is a potentially very serious problem, throw the
     205                 :            :                 // exception detailing the problem with as much information as
     206                 :            :                 // we can.  (Note that this does not include 'value' -
     207                 :            :                 // boost::lexical_cast failed to convert it to a string, so an
     208                 :            :                 // attempt to do so here would probably fail as well.)
     209 [ #  # ][ #  # ]:          0 :                 isc_throw(FormatFailure, "bad_lexical_cast in call to "
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
            [ # ][ #  # ]
            [ #  # ][ # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
     210                 :            :                           "Formatter::arg(): " << ex.what());
     211                 :            :             }
     212                 :            :         } else {
     213                 :            :             return (*this);
     214                 :            :         }
     215                 :            :     }
     216                 :            : 
     217                 :            :     /// \brief String version of arg.
     218                 :            :     ///
     219                 :            :     /// \param arg The text to place into the placeholder.
     220                 :      95959 :     Formatter& arg(const std::string& arg) {
     221         [ +  + ]:     100717 :         if (logger_) {
           [ #  #  +  - ]
         [ +  - ][ +  + ]
     222                 :            :             // Note that this method does a replacement and returns the
     223                 :            :             // modified string. If there are multiple invocations of arg() (e.g.
     224                 :            :             // logger.info(msgid).arg(xxx).arg(yyy)...), each invocation
     225                 :            :             // operates on the string returned by the previous one. This
     226                 :            :             // sequential operation means that if we had a message like "%1 %2",
     227                 :            :             // and called .arg("%2").arg(42), we would get "42 42"; the first
     228                 :            :             // call replaces the %1" with "%2" and the second replaces all
     229                 :            :             // occurrences of "%2" with 42. (Conversely, the sequence
     230                 :            :             // .arg(42).arg("%1") would return "42 %1" - there are no recursive
     231                 :            :             // replacements).
     232 [ +  - ][ +  - ]:     100404 :             replacePlaceholder(message_, arg, ++nextPlaceholder_ );
     233                 :            :         }
     234                 :      95959 :         return (*this);
     235                 :            :     }
     236                 :            : };
     237                 :            : 
     238                 :            : }
     239                 :            : }
     240                 :            : 
     241                 :            : #endif

Generated by: LCOV version 1.9