Kea 2.7.0
logger_manager_impl.cc
Go to the documentation of this file.
1// Copyright (C) 2011-2024 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <algorithm>
10#include <iostream>
11#include <array>
12
13#include <log4cplus/logger.h>
14#include <log4cplus/configurator.h>
15#include <log4cplus/hierarchy.h>
16#include <log4cplus/consoleappender.h>
17#include <log4cplus/fileappender.h>
18#include <log4cplus/syslogappender.h>
19#include <log4cplus/helpers/loglog.h>
20#include <log4cplus/version.h>
21
22#include <log/logger.h>
23#include <log/logger_support.h>
25#include <log/logger_manager.h>
27#include <log/log_messages.h>
28#include <log/logger_name.h>
31
33
34#include <boost/lexical_cast.hpp>
35
36using namespace std;
37using boost::lexical_cast;
38
39namespace isc {
40namespace log {
41
42// Reset hierarchy of loggers back to default settings. This removes all
43// appenders from loggers, sets their severity to NOT_SET (so that events are
44// passed back to the parent) and resets the root logger to logging
45// informational messages. (This last is not a log4cplus default, so we have to
46// explicitly reset the logging severity.)
47void
49 storeBufferAppenders();
50
51 log4cplus::Logger::getDefaultHierarchy().resetConfiguration();
52 initRootLogger();
53}
54
55// Flush the BufferAppenders at the end of processing a new specification
56void
58 flushBufferAppenders();
59}
60
61// Process logging specification. Set up the common states then dispatch to
62// add output specifications.
63void
65 string const& name(spec.getName());
66 string const& root_logger_name(getRootLoggerName());
67
68 log4cplus::Logger logger = log4cplus::Logger::getInstance(expandLoggerName(name));
69
70 // Set severity level according to specification entry.
72 Level(spec.getSeverity(), spec.getDbglevel())));
73
74 // Set the additive flag.
75 logger.setAdditivity(spec.getAdditive());
76
77 // Replace all appenders for this logger.
78 logger.removeAllAppenders();
79
80 if (name == root_logger_name) {
81 // Store a copy of the root specification. It might be required later.
82 root_spec_ = spec;
83 }
84
85 // Output options given?
86 if (spec.optionCount() > 0) {
87 // If there are output options provided, continue with the given spec.
88 appenderFactory(logger, spec);
89 } else {
90 // If there are no output options, inherit them from the root logger.
91 // It's important that root_spec_.getName() is not used further since it
92 // may be different than the logger being configured here.
93 appenderFactory(logger, root_spec_);
94 }
95}
96
97void
98LoggerManagerImpl::appenderFactory(log4cplus::Logger& logger,
99 LoggerSpecification const& spec) {
100 for (OutputOption const& i : spec) {
101 switch (i.destination) {
103 createConsoleAppender(logger, i);
104 break;
105
107 createFileAppender(logger, i);
108 break;
109
111 createSyslogAppender(logger, i);
112 break;
113
114 default:
115 // Not a valid destination. As we are in the middle of updating
116 // logging destinations, we could be in the situation where
117 // there are no valid appenders. For this reason, throw an
118 // exception.
120 "Unknown logging destination, code = " << i.destination);
121 }
122 }
123}
124
125// Console appender - log to either stdout or stderr.
126void
127LoggerManagerImpl::createConsoleAppender(log4cplus::Logger& logger,
128 const OutputOption& opt)
129{
130 log4cplus::SharedAppenderPtr console(
131 new log4cplus::ConsoleAppender(
132 (opt.stream == OutputOption::STR_STDERR), opt.flush));
133
134 setAppenderLayout(console, (opt.pattern.empty() ?
136 logger.addAppender(console);
137}
138
139// File appender. Depending on whether a maximum size is given, either
140// a standard file appender or a rolling file appender will be created.
141// In the case of the latter, we set "UseLockFile" to true so that
142// log4cplus internally avoids race in rolling over the files by multiple
143// processes. This feature isn't supported in log4cplus 1.0.x, but setting
144// the property unconditionally is okay as unknown properties are simply
145// ignored.
146void
147LoggerManagerImpl::createFileAppender(log4cplus::Logger& logger,
148 const OutputOption& opt)
149{
150 // Append to existing file
151 const std::ios::openmode mode = std::ios::app;
152
153 log4cplus::SharedAppenderPtr fileapp;
154 if (opt.maxsize == 0) {
155 fileapp = log4cplus::SharedAppenderPtr(new log4cplus::FileAppender(
156 opt.filename, mode, opt.flush));
157 } else {
158 log4cplus::helpers::Properties properties;
159 properties.setProperty("File", opt.filename);
160
161 // log4cplus supports file sizes past INT_MAX only in suffixed format.
162 // Convert from integer.
163 uint64_t maxsize(opt.maxsize);
164 size_t i(0);
165 while (std::numeric_limits<int32_t>::max() < maxsize && i < 2) {
166 maxsize /= 1000;
167 ++i;
168 }
169 std::array<std::string, 3> const suffixes({"", "KB", "MB"});
170 std::string const max_file_size(to_string(maxsize) + suffixes[i]);
171
172 // If maxsize is still past INT_MAX, it will overflow in log4cplus,
173 // so stop here instead.
174 if (std::numeric_limits<int32_t>::max() < maxsize) {
175 isc_throw(BadValue, "expected maxsize < "
176 << std::numeric_limits<int32_t>::max()
177 << "MB, but instead got " << max_file_size);
178 }
179
180 properties.setProperty("MaxFileSize", max_file_size);
181 properties.setProperty("MaxBackupIndex",
182 lexical_cast<string>(opt.maxver));
183 properties.setProperty("ImmediateFlush", opt.flush ? "true" : "false");
184 properties.setProperty("UseLockFile", "true");
185 fileapp = log4cplus::SharedAppenderPtr(
186 new log4cplus::RollingFileAppender(properties));
187 }
188
189 setAppenderLayout(fileapp, (opt.pattern.empty() ?
191 logger.addAppender(fileapp);
192}
193
194void
195LoggerManagerImpl::createBufferAppender(log4cplus::Logger& logger) {
196 log4cplus::SharedAppenderPtr bufferapp(new internal::BufferAppender());
197 bufferapp->setName("buffer");
198 logger.addAppender(bufferapp);
199 // Since we do not know at what level the loggers will end up
200 // running, set it to the highest for now
201 logger.setLogLevel(log4cplus::TRACE_LOG_LEVEL);
202}
203
204// Syslog appender.
205void
206LoggerManagerImpl::createSyslogAppender(log4cplus::Logger& logger,
207 const OutputOption& opt)
208{
209 log4cplus::helpers::Properties properties;
210 properties.setProperty("ident", getRootLoggerName());
211 properties.setProperty("facility", opt.facility);
212 log4cplus::SharedAppenderPtr syslogapp(
213 new log4cplus::SysLogAppender(properties));
214 setAppenderLayout(syslogapp, (opt.pattern.empty() ?
216 logger.addAppender(syslogapp);
217}
218
219
220// One-time initialization of the log4cplus system
221void
223 bool buffer)
224{
225 // Set up basic configurator. This attaches a ConsoleAppender to the
226 // root logger with suitable output. This is used until we we have
227 // actually read the logging configuration, in which case the output
228 // may well be changed.
229 log4cplus::BasicConfigurator config;
230 config.configure();
231
232 // Add the additional debug levels
234
235 initRootLogger(severity, dbglevel, buffer);
236}
237
238// Reset logging to default configuration. This closes all appenders
239// and resets the root logger to output INFO messages to the console.
240// It is principally used in testing.
241void
243{
244 // Initialize the root logger
245 initRootLogger(severity, dbglevel);
246}
247
248// Initialize the root logger
249void LoggerManagerImpl::initRootLogger(isc::log::Severity severity,
250 int dbglevel, bool buffer)
251{
252 log4cplus::Logger::getDefaultHierarchy().resetConfiguration();
253
254 // Disable log4cplus' own logging, unless --enable-debug was
255 // specified to configure. Note that this does not change
256 // LogLog's levels (that is still just INFO).
257#ifndef ENABLE_DEBUG
258 log4cplus::helpers::LogLog::getLogLog()->setQuietMode(true);
259#endif
260
261 // Set the log4cplus root to not output anything - effectively we are
262 // ignoring it.
263 log4cplus::Logger::getRoot().setLogLevel(log4cplus::OFF_LOG_LEVEL);
264
265 // Set the level for the Kea root logger to the given severity and
266 // debug level.
267 log4cplus::Logger kea_root = log4cplus::Logger::getInstance(
269 kea_root.setLogLevel(LoggerLevelImpl::convertFromBindLevel(
270 Level(severity, dbglevel)));
271
272 if (buffer) {
273 createBufferAppender(kea_root);
274 } else {
275 OutputOption opt;
276 createConsoleAppender(kea_root, opt);
277 }
278}
279
280
281void LoggerManagerImpl::setAppenderLayout(
282 log4cplus::SharedAppenderPtr& appender,
283 std::string pattern)
284{
285 // Finally the text of the message
286 appender->setLayout(
287#if LOG4CPLUS_VERSION < LOG4CPLUS_MAKE_VERSION(2, 0, 0)
288 auto_ptr<log4cplus::Layout>
289#else
290 unique_ptr<log4cplus::Layout>
291#endif
292 (new log4cplus::PatternLayout(pattern)));
293}
294
295void LoggerManagerImpl::storeBufferAppenders() {
296 // Walk through all loggers, and find any buffer appenders there
297 log4cplus::LoggerList loggers = log4cplus::Logger::getCurrentLoggers();
298 for (auto& it : loggers) {
299 log4cplus::SharedAppenderPtr buffer_appender = it.getAppender("buffer");
300 if (buffer_appender) {
301 buffer_appender_store_.push_back(buffer_appender);
302 }
303 }
304}
305
306void LoggerManagerImpl::flushBufferAppenders() {
307 std::vector<log4cplus::SharedAppenderPtr> copy;
308 buffer_appender_store_.swap(copy);
309
310 for (auto const& it : copy) {
311 internal::BufferAppender* app = dynamic_cast<internal::BufferAppender*>(it.get());
312 isc_throw_assert(app);
313 app->flush();
314 }
315}
316
317} // namespace log
318} // namespace isc
static log4cplus::LogLevel convertFromBindLevel(const isc::log::Level &level)
Convert Kea level to log4cplus logging level.
static void init()
Initialize extended logging levels.
void processSpecification(const LoggerSpecification &spec)
Process Specification.
void processEnd()
End Processing.
static void init(isc::log::Severity severity=isc::log::INFO, int dbglevel=0, bool buffer=false)
Implementation-specific initialization.
static void reset(isc::log::Severity severity=isc::log::INFO, int dbglevel=0)
Reset logging.
void processInit()
Initialize Processing.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define isc_throw_assert(expr)
Replacement for assert() that throws if the expression is false.
Definition isc_assert.h:18
Logging initialization functions.
isc::log::Logger logger("asiodns")
Use the ASIO logger.
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition data.cc:1420
const std::string & getRootLoggerName()
Get root logger name.
std::string expandLoggerName(const std::string &name)
Expand logger name.
Severity
Severity Levels.
Defines the logger used by the top-level component of kea-lfc.
Log level structure.
static const std::string DEFAULT_FILE_PATTERN
Default layout pattern for file logs.
static const std::string DEFAULT_SYSLOG_PATTERN
Default layout pattern for syslog logs.
static const std::string DEFAULT_CONSOLE_PATTERN
Default layout pattern for console logs.