Kea  2.3.7
logger_manager_impl.cc
Go to the documentation of this file.
1 // Copyright (C) 2011-2022 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>
24 #include <log/logger_level_impl.h>
25 #include <log/logger_manager.h>
27 #include <log/log_messages.h>
28 #include <log/logger_name.h>
31 
32 #include <exceptions/isc_assert.h>
33 
34 #include <boost/lexical_cast.hpp>
35 
36 using namespace std;
37 using boost::lexical_cast;
38 
39 namespace isc {
40 namespace 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.)
47 void
48 LoggerManagerImpl::processInit() {
49  storeBufferAppenders();
50 
51  log4cplus::Logger::getDefaultHierarchy().resetConfiguration();
52  initRootLogger();
53 }
54 
55 // Flush the BufferAppenders at the end of processing a new specification
56 void
57 LoggerManagerImpl::processEnd() {
58  flushBufferAppenders();
59 }
60 
61 // Process logging specification. Set up the common states then dispatch to
62 // add output specifications.
63 void
64 LoggerManagerImpl::processSpecification(const LoggerSpecification& spec) {
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.
71  logger.setLogLevel(LoggerLevelImpl::convertFromBindLevel(
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 
97 void
98 LoggerManagerImpl::appenderFactory(log4cplus::Logger& logger,
99  LoggerSpecification const& spec) {
100  for (OutputOption const& i : spec) {
101  switch (i.destination) {
102  case OutputOption::DEST_CONSOLE:
103  createConsoleAppender(logger, i);
104  break;
105 
106  case OutputOption::DEST_FILE:
107  createFileAppender(logger, i);
108  break;
109 
110  case OutputOption::DEST_SYSLOG:
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.
126 void
127 LoggerManagerImpl::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() ?
135  OutputOption::DEFAULT_CONSOLE_PATTERN : opt.pattern));
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.
146 void
147 LoggerManagerImpl::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() ?
190  OutputOption::DEFAULT_FILE_PATTERN : opt.pattern));
191  logger.addAppender(fileapp);
192 }
193 
194 void
195 LoggerManagerImpl::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.
205 void
206 LoggerManagerImpl::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() ?
215  OutputOption::DEFAULT_SYSLOG_PATTERN : opt.pattern));
216  logger.addAppender(syslogapp);
217 }
218 
219 
220 // One-time initialization of the log4cplus system
221 void
222 LoggerManagerImpl::init(isc::log::Severity severity, int dbglevel,
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
233  LoggerLevelImpl::init();
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.
241 void
242 LoggerManagerImpl::reset(isc::log::Severity severity, int dbglevel)
243 {
244  // Initialize the root logger
245  initRootLogger(severity, dbglevel);
246 }
247 
248 // Initialize the root logger
249 void 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 
281 void 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 
295 void LoggerManagerImpl::storeBufferAppenders() {
296  // Walk through all loggers, and find any buffer appenders there
297  log4cplus::LoggerList loggers = log4cplus::Logger::getCurrentLoggers();
298  log4cplus::LoggerList::iterator it;
299  for (it = loggers.begin(); it != loggers.end(); ++it) {
300  log4cplus::SharedAppenderPtr buffer_appender =
301  it->getAppender("buffer");
302  if (buffer_appender) {
303  buffer_appender_store_.push_back(buffer_appender);
304  }
305  }
306 }
307 
308 void LoggerManagerImpl::flushBufferAppenders() {
309  std::vector<log4cplus::SharedAppenderPtr> copy;
310  buffer_appender_store_.swap(copy);
311 
312  std::vector<log4cplus::SharedAppenderPtr>::iterator it;
313  for (it = copy.begin(); it != copy.end(); ++it) {
314  internal::BufferAppender* app =
315  dynamic_cast<internal::BufferAppender*>(it->get());
316  isc_throw_assert(app);
317  app->flush();
318  }
319 }
320 
321 } // namespace log
322 } // namespace isc
isc::log::Severity getSeverity() const
#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:1360
const std::string & getRootLoggerName()
Get root logger name.
Definition: logger_name.cc:33
std::string expandLoggerName(const std::string &name)
Expand logger name.
Definition: logger_name.cc:42
Severity
Severity Levels.
Definition: logger_level.h:23
Defines the logger used by the top-level component of kea-lfc.
Log level structure.
Definition: logger_level.h:42