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
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
// Copyright (C) 2011-2024 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/.

#include <config.h>

#include <algorithm><--- 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 <array><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <log4cplus/logger.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <log4cplus/configurator.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <log4cplus/hierarchy.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <log4cplus/consoleappender.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <log4cplus/fileappender.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <log4cplus/syslogappender.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <log4cplus/helpers/loglog.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <log4cplus/version.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <log/logger.h>
#include <log/logger_support.h>
#include <log/logger_level_impl.h>
#include <log/logger_manager.h>
#include <log/logger_manager_impl.h>
#include <log/log_messages.h>
#include <log/logger_name.h>
#include <log/logger_specification.h>
#include <log/buffer_appender_impl.h>

#include <exceptions/isc_assert.h>

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

using namespace std;
using boost::lexical_cast;

namespace isc {
namespace log {

// Reset hierarchy of loggers back to default settings.  This removes all
// appenders from loggers, sets their severity to NOT_SET (so that events are
// passed back to the parent) and resets the root logger to logging
// informational messages.  (This last is not a log4cplus default, so we have to
// explicitly reset the logging severity.)
void
LoggerManagerImpl::processInit() {
    storeBufferAppenders();

    log4cplus::Logger::getDefaultHierarchy().resetConfiguration();
    initRootLogger();
}

// Flush the BufferAppenders at the end of processing a new specification
void
LoggerManagerImpl::processEnd() {
    flushBufferAppenders();
}

// Process logging specification.  Set up the common states then dispatch to
// add output specifications.
void
LoggerManagerImpl::processSpecification(const LoggerSpecification& spec) {
    string const& name(spec.getName());
    string const& root_logger_name(getRootLoggerName());

    log4cplus::Logger logger = log4cplus::Logger::getInstance(expandLoggerName(name));

    // Set severity level according to specification entry.
    logger.setLogLevel(LoggerLevelImpl::convertFromBindLevel(
                       Level(spec.getSeverity(), spec.getDbglevel())));

    // Set the additive flag.
    logger.setAdditivity(spec.getAdditive());

    // Replace all appenders for this logger.
    logger.removeAllAppenders();

    if (name == root_logger_name) {
        // Store a copy of the root specification. It might be required later.
        root_spec_ = spec;
    }

    // Output options given?
    if (spec.optionCount() > 0) {
        // If there are output options provided, continue with the given spec.
        appenderFactory(logger, spec);
    } else {
        // If there are no output options, inherit them from the root logger.
        // It's important that root_spec_.getName() is not used further since it
        // may be different than the logger being configured here.
        appenderFactory(logger, root_spec_);
    }
}

void
LoggerManagerImpl::appenderFactory(log4cplus::Logger& logger,
                                   LoggerSpecification const& spec) {
    for (OutputOption const& i : spec) {
        switch (i.destination) {
        case OutputOption::DEST_CONSOLE:
            createConsoleAppender(logger, i);
            break;

        case OutputOption::DEST_FILE:
            createFileAppender(logger, i);
            break;

        case OutputOption::DEST_SYSLOG:
            createSyslogAppender(logger, i);
            break;

        default:
            // Not a valid destination.  As we are in the middle of updating
            // logging destinations, we could be in the situation where
            // there are no valid appenders.  For this reason, throw an
            // exception.
            isc_throw(UnknownLoggingDestination,
                      "Unknown logging destination, code = " << i.destination);
        }
    }
}

// Console appender - log to either stdout or stderr.
void
LoggerManagerImpl::createConsoleAppender(log4cplus::Logger& logger,
                                         const OutputOption& opt)
{
    log4cplus::SharedAppenderPtr console(
        new log4cplus::ConsoleAppender(
            (opt.stream == OutputOption::STR_STDERR), opt.flush));

    setAppenderLayout(console, (opt.pattern.empty() ?
                                OutputOption::DEFAULT_CONSOLE_PATTERN : opt.pattern));
    logger.addAppender(console);
}

// File appender.  Depending on whether a maximum size is given, either
// a standard file appender or a rolling file appender will be created.
// In the case of the latter, we set "UseLockFile" to true so that
// log4cplus internally avoids race in rolling over the files by multiple
// processes.  This feature isn't supported in log4cplus 1.0.x, but setting
// the property unconditionally is okay as unknown properties are simply
// ignored.
void
LoggerManagerImpl::createFileAppender(log4cplus::Logger& logger,
                                      const OutputOption& opt)
{
    // Append to existing file
    const std::ios::openmode mode = std::ios::app;

    log4cplus::SharedAppenderPtr fileapp;
    if (opt.maxsize == 0) {
        fileapp = log4cplus::SharedAppenderPtr(new log4cplus::FileAppender(
            opt.filename, mode, opt.flush));
    } else {
        log4cplus::helpers::Properties properties;
        properties.setProperty("File", opt.filename);

        // log4cplus supports file sizes past INT_MAX only in suffixed format.
        // Convert from integer.
        uint64_t maxsize(opt.maxsize);
        size_t i(0);
        while (std::numeric_limits<int32_t>::max() < maxsize && i < 2) {
            maxsize /= 1000;
            ++i;
        }
        std::array<std::string, 3> const suffixes({"", "KB", "MB"});
        std::string const max_file_size(to_string(maxsize) + suffixes[i]);

        // If maxsize is still past INT_MAX, it will overflow in log4cplus,
        // so stop here instead.
        if (std::numeric_limits<int32_t>::max() < maxsize) {
            isc_throw(BadValue, "expected maxsize < "
                                << std::numeric_limits<int32_t>::max()
                                << "MB, but instead got " << max_file_size);
        }

        properties.setProperty("MaxFileSize", max_file_size);
        properties.setProperty("MaxBackupIndex",
                               lexical_cast<string>(opt.maxver));
        properties.setProperty("ImmediateFlush", opt.flush ? "true" : "false");
        properties.setProperty("UseLockFile", "true");
        fileapp = log4cplus::SharedAppenderPtr(
            new log4cplus::RollingFileAppender(properties));
    }

    setAppenderLayout(fileapp, (opt.pattern.empty() ?
                                OutputOption::DEFAULT_FILE_PATTERN : opt.pattern));
    logger.addAppender(fileapp);
}

void
LoggerManagerImpl::createBufferAppender(log4cplus::Logger& logger) {
    log4cplus::SharedAppenderPtr bufferapp(new internal::BufferAppender());
    bufferapp->setName("buffer");
    logger.addAppender(bufferapp);
    // Since we do not know at what level the loggers will end up
    // running, set it to the highest for now
    logger.setLogLevel(log4cplus::TRACE_LOG_LEVEL);
}

// Syslog appender.
void
LoggerManagerImpl::createSyslogAppender(log4cplus::Logger& logger,
                                         const OutputOption& opt)
{
    log4cplus::helpers::Properties properties;
    properties.setProperty("ident", getRootLoggerName());
    properties.setProperty("facility", opt.facility);
    log4cplus::SharedAppenderPtr syslogapp(
        new log4cplus::SysLogAppender(properties));
    setAppenderLayout(syslogapp, (opt.pattern.empty() ?
                                  OutputOption::DEFAULT_SYSLOG_PATTERN : opt.pattern));
    logger.addAppender(syslogapp);
}


// One-time initialization of the log4cplus system
void
LoggerManagerImpl::init(isc::log::Severity severity, int dbglevel,
                        bool buffer)
{
    // Set up basic configurator.  This attaches a ConsoleAppender to the
    // root logger with suitable output.  This is used until we we have
    // actually read the logging configuration, in which case the output
    // may well be changed.
    log4cplus::BasicConfigurator config;
    config.configure();

    // Add the additional debug levels
    LoggerLevelImpl::init();

    initRootLogger(severity, dbglevel, buffer);
}

// Reset logging to default configuration.  This closes all appenders
// and resets the root logger to output INFO messages to the console.
// It is principally used in testing.
void
LoggerManagerImpl::reset(isc::log::Severity severity, int dbglevel)
{
    // Initialize the root logger
    initRootLogger(severity, dbglevel);
}

// Initialize the root logger
void LoggerManagerImpl::initRootLogger(isc::log::Severity severity,
                                       int dbglevel, bool buffer)
{
    log4cplus::Logger::getDefaultHierarchy().resetConfiguration();

    // Disable log4cplus' own logging, unless --enable-debug was
    // specified to configure. Note that this does not change
    // LogLog's levels (that is still just INFO).
#ifndef ENABLE_DEBUG
    log4cplus::helpers::LogLog::getLogLog()->setQuietMode(true);
#endif

    // Set the log4cplus root to not output anything - effectively we are
    // ignoring it.
    log4cplus::Logger::getRoot().setLogLevel(log4cplus::OFF_LOG_LEVEL);

    // Set the level for the Kea root logger to the given severity and
    // debug level.
    log4cplus::Logger kea_root = log4cplus::Logger::getInstance(
                                                    getRootLoggerName());
    kea_root.setLogLevel(LoggerLevelImpl::convertFromBindLevel(
                                                    Level(severity, dbglevel)));

    if (buffer) {
        createBufferAppender(kea_root);
    } else {
        OutputOption opt;
        createConsoleAppender(kea_root, opt);
    }
}


void LoggerManagerImpl::setAppenderLayout(
        log4cplus::SharedAppenderPtr& appender,
        std::string pattern)
{
    // Finally the text of the message
    appender->setLayout(
#if LOG4CPLUS_VERSION < LOG4CPLUS_MAKE_VERSION(2, 0, 0)
                        auto_ptr<log4cplus::Layout>
#else
                        unique_ptr<log4cplus::Layout>
#endif
                        (new log4cplus::PatternLayout(pattern)));
}

void LoggerManagerImpl::storeBufferAppenders() {
    // Walk through all loggers, and find any buffer appenders there
    log4cplus::LoggerList loggers = log4cplus::Logger::getCurrentLoggers();
    for (auto& it : loggers) {
        log4cplus::SharedAppenderPtr buffer_appender = it.getAppender("buffer");
        if (buffer_appender) {
            buffer_appender_store_.push_back(buffer_appender);
        }
    }
}

void LoggerManagerImpl::flushBufferAppenders() {
    std::vector<log4cplus::SharedAppenderPtr> copy;
    buffer_appender_store_.swap(copy);

    for (auto const& it : copy) {
        internal::BufferAppender* app = dynamic_cast<internal::BufferAppender*>(it.get());
        isc_throw_assert(app);
        app->flush();
    }
}

} // namespace log
} // namespace isc