123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- // Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
- //
- // Permission to use, copy, modify, and/or distribute this software for any
- // purpose with or without fee is hereby granted, provided that the above
- // copyright notice and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- // PERFORMANCE OF THIS SOFTWARE.
- #ifndef __LOG_FORMATTER_H
- #define __LOG_FORMMATER_H
- #include <cstddef>
- #include <string>
- #include <iostream>
- #include <boost/lexical_cast.hpp>
- #include <log/logger_level.h>
- namespace isc {
- namespace log {
- /// \brief The internal replacement routine
- ///
- /// This is used internally by the Formatter. Replaces a placeholder
- /// in the message by replacement. If the placeholder is not found,
- /// it adds a complain at the end.
- void
- replacePlaceholder(std::string* message, const std::string& replacement,
- const unsigned placeholder);
- ///
- /// \brief The log message formatter
- ///
- /// This class allows us to format logging messages conveniently. We
- /// call something like logger.warn(WARN_MSG).arg(15).arg(dnsMsg). This
- /// outputs some text with placeholders replaced by the arguments, if
- /// the logging verbosity is at WARN level or more.
- ///
- /// To make this work, we use the Formatter. The warn (or whatever logging
- /// function) returns a Formatter object. That one holds the string to be
- /// output with the placeholders. It also remembers if there should be any
- /// output at all (eg. if the logging is enabled for this level). When there's
- /// no .arg call on the object, it is destroyed right away and we use the
- /// destructor to output the text (but only in case we should output anything).
- ///
- /// If there's an .arg call, we return reference to the same object, so another
- /// .arg can be called on it. After the last .arg call is done, the object is
- /// destroyed and, again, we can produce the output.
- ///
- /// Of course, if the logging is turned off, we don't bother with any replacing
- /// and just return.
- ///
- /// User of logging code should not really care much about this class, only
- /// call the .arg method to generate the correct output.
- ///
- /// The class is a template to allow easy testing. Also, we want everything
- /// here in the header anyway and it doesn't depend on the details of what
- /// Logger really is, so it doesn't hurt anything.
- ///
- /// Also, if you are interested in the internals, you might find the copy
- /// constructor a bit strange. It deactivates the original formatter. We don't
- /// really want to support copying of the Formatter by user, but C++ needs a
- /// copy constructor when returning from the logging functions, so we need one.
- /// And if we did not deactivate the original Formatter, that one would get
- /// destroyed before any call to .arg, producing an output, and then the one
- /// the .arg calls are called on would get destroyed as well, producing output
- /// again. So, think of this behaviour as soul moving from one to another.
- template<class Logger> class Formatter {
- private:
- /// \brief The logger we will use to output the final message.
- ///
- /// If NULL, we are not active and should not produce anything.
- mutable Logger* logger_;
- /// \brief Message severity
- Severity severity_;
- /// \brief The messages with %1, %2... placeholders
- std::string* message_;
- /// \brief Which will be the next placeholder to replace
- unsigned nextPlaceholder_;
- public:
- /// \brief Constructor of "active" formatter
- ///
- /// This will create a formatter. If the arguments are set, it
- /// will be active (will produce output). If you leave them all as NULL,
- /// it will create an inactive Formatter -- one that'll produce no output.
- ///
- /// It is not expected to be called by user of logging system directly.
- ///
- /// \param severity The severity of the message (DEBUG, ERROR etc.)
- /// \param message The message with placeholders. We take ownership of
- /// it and we will modify the string. Must not be NULL unless
- /// logger is also NULL, but it's not checked.
- /// \param logger The logger where the final output will go, or NULL
- /// if no output is wanted.
- Formatter(const Severity& severity = NONE, std::string* message = NULL,
- Logger* logger = NULL) :
- logger_(logger), severity_(severity), message_(message),
- nextPlaceholder_(0)
- {
- }
- /// \brief Copy constructor
- ///
- /// "Control" is passed to the created object in that it is the created object
- /// that will have responsibility for outputting the formatted message - the
- /// object being copied relinquishes that responsibility.
- Formatter(const Formatter& other) :
- logger_(other.logger_), severity_(other.severity_),
- message_(other.message_), nextPlaceholder_(other.nextPlaceholder_)
- {
- other.logger_ = NULL;
- }
- /// \brief Destructor.
- //
- /// This is the place where output happens if the formatter is active.
- ~ Formatter() {
- if (logger_) {
- logger_->output(severity_, *message_);
- delete message_;
- }
- }
- /// \brief Assignment operator
- ///
- /// Essentially the same function as the assignment operator - the object being
- /// assigned to takes responsibility for outputting the message.
- Formatter& operator =(const Formatter& other) {
- if (&other != this) {
- logger_ = other.logger_;
- severity_ = other.severity_;
- message_ = other.message_;
- nextPlaceholder_ = other.nextPlaceholder_;
- other.logger_ = NULL;
- }
- return *this;
- }
- /// \brief Replaces another placeholder
- ///
- /// Replaces another placeholder and returns a new formatter with it.
- /// Deactivates the current formatter. In case the formatter is not active,
- /// only produces another inactive formatter.
- ///
- /// \param arg The argument to place into the placeholder.
- template<class Arg> Formatter& arg(const Arg& value) {
- if (logger_) {
- return (arg(boost::lexical_cast<std::string>(value)));
- } else {
- return (*this);
- }
- }
- /// \brief String version of arg.
- Formatter& arg(const std::string& arg) {
- if (logger_) {
- // Note that this method does a replacement and returns the
- // modified string. If there are multiple invocations of arg() (e.g.
- // logger.info(msgid).arg(xxx).arg(yyy)...), each invocation
- // operates on the string returned by the previous one. This
- // sequential operation means that if we had a message like "%1 %2",
- // and called .arg("%2").arg(42), we would get "42 42"; the first
- // call replaces the %1" with "%2" and the second replaces all
- // occurrences of "%2" with 42. (Conversely, the sequence
- // .arg(42).arg("%1") would return "42 %1" - there are no recursive
- // replacements).
- replacePlaceholder(message_, arg, ++nextPlaceholder_ );
- }
- return (*this);
- }
- };
- }
- }
- #endif
|