1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
// Copyright (C) 2011-2020 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef LOG_FORMATTER_H
#define LOG_FORMATTER_H

#include <cstddef><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <string><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <iostream><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <exceptions/exceptions.h>
#include <log/logger_level.h>

#include <boost/make_shared.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <boost/shared_ptr.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <boost/lexical_cast.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

namespace isc {
namespace log {

/// \brief Format Failure
///
/// This exception is used to wrap a bad_lexical_cast exception thrown during
/// formatting an argument.

class FormatFailure : public isc::Exception {
public:
    FormatFailure(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what)
    {}
};


/// \brief Mismatched Placeholders
///
/// This exception is used when the number of placeholders do not match
/// the number of arguments passed to the formatter.

class MismatchedPlaceholders : public isc::Exception {
public:
    MismatchedPlaceholders(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what)
    {}
};


///
/// \brief Internal excess placeholder checker
///
/// This is used internally by the Formatter to check for excess
/// placeholders (and fewer arguments).
void
checkExcessPlaceholders(std::string& message, unsigned int placeholder);

///
/// \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 behavior 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
    boost::shared_ptr<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,
              boost::shared_ptr<std::string> message = boost::make_shared<std::string>(),
              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_) {
            try {
                checkExcessPlaceholders(*message_, ++nextPlaceholder_);
                logger_->output(severity_, *message_);
            } catch (...) {
                // Catch and ignore all exceptions here.
            }
        }
    }

    /// \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 value The argument to place into the placeholder.
    template<class Arg> Formatter& arg(const Arg& value) {
        if (logger_) {
            try {
                return (arg(boost::lexical_cast<std::string>(value)));
            } catch (const boost::bad_lexical_cast& ex) {
                // The formatting of the log message got wrong, we don't want
                // to output it.
                deactivate();
                // A bad_lexical_cast during a conversion to a string is
                // *extremely* unlikely to fail.  However, there is nothing
                // in the documentation that rules it out, so we need to handle
                // it.  As it is a potentially very serious problem, throw the
                // exception detailing the problem with as much information as
                // we can.  (Note that this does not include 'value' -
                // boost::lexical_cast failed to convert it to a string, so an
                // attempt to do so here would probably fail as well.)
                isc_throw(FormatFailure, "bad_lexical_cast in call to "
                          "Formatter::arg(): " << ex.what());
            }
        } else {
            return (*this);
        }
    }

    /// \brief String version of arg.
    ///
    /// \param arg The text to place into the placeholder.
    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).
            try {
                replacePlaceholder(*message_, arg, ++nextPlaceholder_);
            } catch (...) {
                // Something went wrong here, the log message is broken, so
                // we don't want to output it, nor we want to check all the
                // placeholders were used (because they won't be).
                deactivate();
                throw;
            }
        }
        return (*this);
    }

    /// \brief Turn off the output of this logger.
    ///
    /// If the logger would output anything at the end, now it won't.
    /// Also, this turns off the strict checking of placeholders, if
    /// it is compiled in.
    ///
    /// The expected use is when there was an exception processing
    /// the arguments for the message.
    void deactivate() {
        if (logger_) {
            message_.reset();
            logger_ = NULL;
        }
    }
};

} // namespace log
} // namespace isc

#endif