|
@@ -0,0 +1,146 @@
|
|
|
+// 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 <string>
|
|
|
+#include <boost/lexical_cast.hpp>
|
|
|
+
|
|
|
+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 Prefix (eg. "ERROR", "DEBUG" or like that)
|
|
|
+ const char* prefix_;
|
|
|
+ /// \brief The messages with %1, %2... placeholders
|
|
|
+ std::string* message_;
|
|
|
+ /// \brief Which will be the next placeholder to replace
|
|
|
+ unsigned nextPlaceholder_;
|
|
|
+ Formatter& operator =(const Formatter& other);
|
|
|
+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 prefix The severity prefix, like "ERROR" or "DEBUG"
|
|
|
+ /// \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 char* prefix = NULL, std::string* message = NULL,
|
|
|
+ Logger* logger = NULL) :
|
|
|
+ logger_(logger), prefix_(prefix), message_(message),
|
|
|
+ nextPlaceholder_(1)
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+ Formatter(const Formatter& other) :
|
|
|
+ logger_(other.logger_), prefix_(other.prefix_),
|
|
|
+ message_(other.message_), nextPlaceholder_(other.nextPlaceholder_)
|
|
|
+ {
|
|
|
+ other.logger_ = false;
|
|
|
+ }
|
|
|
+ /// \brief Destructor.
|
|
|
+ //
|
|
|
+ /// This is the place where output happens if the formatter is active.
|
|
|
+ ~ Formatter() {
|
|
|
+ if (logger_) {
|
|
|
+ logger_->output(prefix_, *message_);
|
|
|
+ delete message_;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /// \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) {
|
|
|
+ return (arg(boost::lexical_cast<std::string>(value)));
|
|
|
+ }
|
|
|
+ /// \brief String version of arg.
|
|
|
+ Formatter& arg(const std::string& arg) {
|
|
|
+ if (logger_) {
|
|
|
+ // FIXME: This logic has a problem. If we had a message like
|
|
|
+ // "%1 %2" and called .arg("%2").arg(42), we would get "42 %2".
|
|
|
+ // But we consider this to be rare enough not to complicate
|
|
|
+ // matters.
|
|
|
+ replacePlaceholder(message_, arg, nextPlaceholder_ ++);
|
|
|
+ }
|
|
|
+ return (*this);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+}
|
|
|
+}
|
|
|
+
|
|
|
+#endif
|