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
|