Kea  2.1.7-git
logger_manager_impl.cc
Go to the documentation of this file.
1 // Copyright (C) 2011-2021 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  log4cplus::Logger logger = log4cplus::Logger::getInstance(
66  expandLoggerName(spec.getName()));
67 
68  // Set severity level according to specification entry.
69  logger.setLogLevel(LoggerLevelImpl::convertFromBindLevel(
70  Level(spec.getSeverity(), spec.getDbglevel())));
71 
72  // Set the additive flag.
73  logger.setAdditivity(spec.getAdditive());
74 
75  // Output options given?
76  if (spec.optionCount() > 0) {
77  // Replace all appenders for this logger.
78  logger.removeAllAppenders();
79 
80  // Now process output specifications.
82  i != spec.end(); ++i) {
83  switch (i->destination) {
84  case OutputOption::DEST_CONSOLE:
85  createConsoleAppender(logger, *i);
86  break;
87 
88  case OutputOption::DEST_FILE:
89  createFileAppender(logger, *i);
90  break;
91 
92  case OutputOption::DEST_SYSLOG:
93  createSyslogAppender(logger, *i);
94  break;
95 
96  default:
97  // Not a valid destination. As we are in the middle of updating
98  // logging destinations, we could be in the situation where
99  // there are no valid appenders. For this reason, throw an
100  // exception.
102  "Unknown logging destination, code = " <<
103  i->destination);
104  }
105  }
106  }
107 }
108 
109 // Console appender - log to either stdout or stderr.
110 void
111 LoggerManagerImpl::createConsoleAppender(log4cplus::Logger& logger,
112  const OutputOption& opt)
113 {
114  log4cplus::SharedAppenderPtr console(
115  new log4cplus::ConsoleAppender(
116  (opt.stream == OutputOption::STR_STDERR), opt.flush));
117 
118  setAppenderLayout(console, (opt.pattern.empty() ?
119  OutputOption::DEFAULT_CONSOLE_PATTERN : opt.pattern));
120  logger.addAppender(console);
121 }
122 
123 // File appender. Depending on whether a maximum size is given, either
124 // a standard file appender or a rolling file appender will be created.
125 // In the case of the latter, we set "UseLockFile" to true so that
126 // log4cplus internally avoids race in rolling over the files by multiple
127 // processes. This feature isn't supported in log4cplus 1.0.x, but setting
128 // the property unconditionally is okay as unknown properties are simply
129 // ignored.
130 void
131 LoggerManagerImpl::createFileAppender(log4cplus::Logger& logger,
132  const OutputOption& opt)
133 {
134  // Append to existing file
135  const std::ios::openmode mode = std::ios::app;
136 
137  log4cplus::SharedAppenderPtr fileapp;
138  if (opt.maxsize == 0) {
139  fileapp = log4cplus::SharedAppenderPtr(new log4cplus::FileAppender(
140  opt.filename, mode, opt.flush));
141  } else {
142  log4cplus::helpers::Properties properties;
143  properties.setProperty("File", opt.filename);
144 
145  // log4cplus supports file sizes past INT_MAX only in suffixed format.
146  // Convert from integer.
147  uint64_t maxsize(opt.maxsize);
148  size_t i(0);
149  while (std::numeric_limits<int32_t>::max() < maxsize && i < 2) {
150  maxsize /= 1000;
151  ++i;
152  }
153  std::array<std::string, 3> const suffixes({"", "KB", "MB"});
154  std::string const max_file_size(to_string(maxsize) + suffixes[i]);
155 
156  // If maxsize is still past INT_MAX, it will overflow in log4cplus,
157  // so stop here instead.
158  if (std::numeric_limits<int32_t>::max() < maxsize) {
159  isc_throw(BadValue, "expected maxsize < "
160  << std::numeric_limits<int32_t>::max()
161  << "MB, but instead got " << max_file_size);
162  }
163 
164  properties.setProperty("MaxFileSize", max_file_size);
165  properties.setProperty("MaxBackupIndex",
166  lexical_cast<string>(opt.maxver));
167  properties.setProperty("ImmediateFlush", opt.flush ? "true" : "false");
168  properties.setProperty("UseLockFile", "true");
169  fileapp = log4cplus::SharedAppenderPtr(
170  new log4cplus::RollingFileAppender(properties));
171  }
172 
173  setAppenderLayout(fileapp, (opt.pattern.empty() ?
174  OutputOption::DEFAULT_FILE_PATTERN : opt.pattern));
175  logger.addAppender(fileapp);
176 }
177 
178 void
179 LoggerManagerImpl::createBufferAppender(log4cplus::Logger& logger) {
180  log4cplus::SharedAppenderPtr bufferapp(new internal::BufferAppender());
181  bufferapp->setName("buffer");
182  logger.addAppender(bufferapp);
183  // Since we do not know at what level the loggers will end up
184  // running, set it to the highest for now
185  logger.setLogLevel(log4cplus::TRACE_LOG_LEVEL);
186 }
187 
188 // Syslog appender.
189 void
190 LoggerManagerImpl::createSyslogAppender(log4cplus::Logger& logger,
191  const OutputOption& opt)
192 {
193  log4cplus::helpers::Properties properties;
194  properties.setProperty("ident", getRootLoggerName());
195  properties.setProperty("facility", opt.facility);
196  log4cplus::SharedAppenderPtr syslogapp(
197  new log4cplus::SysLogAppender(properties));
198  setAppenderLayout(syslogapp, (opt.pattern.empty() ?
199  OutputOption::DEFAULT_SYSLOG_PATTERN : opt.pattern));
200  logger.addAppender(syslogapp);
201 }
202 
203 
204 // One-time initialization of the log4cplus system
205 void
206 LoggerManagerImpl::init(isc::log::Severity severity, int dbglevel,
207  bool buffer)
208 {
209  // Set up basic configurator. This attaches a ConsoleAppender to the
210  // root logger with suitable output. This is used until we we have
211  // actually read the logging configuration, in which case the output
212  // may well be changed.
213  log4cplus::BasicConfigurator config;
214  config.configure();
215 
216  // Add the additional debug levels
217  LoggerLevelImpl::init();
218 
219  initRootLogger(severity, dbglevel, buffer);
220 }
221 
222 // Reset logging to default configuration. This closes all appenders
223 // and resets the root logger to output INFO messages to the console.
224 // It is principally used in testing.
225 void
226 LoggerManagerImpl::reset(isc::log::Severity severity, int dbglevel)
227 {
228  // Initialize the root logger
229  initRootLogger(severity, dbglevel);
230 }
231 
232 // Initialize the root logger
233 void LoggerManagerImpl::initRootLogger(isc::log::Severity severity,
234  int dbglevel, bool buffer)
235 {
236  log4cplus::Logger::getDefaultHierarchy().resetConfiguration();
237 
238  // Disable log4cplus' own logging, unless --enable-debug was
239  // specified to configure. Note that this does not change
240  // LogLog's levels (that is still just INFO).
241 #ifndef ENABLE_DEBUG
242  log4cplus::helpers::LogLog::getLogLog()->setQuietMode(true);
243 #endif
244 
245  // Set the log4cplus root to not output anything - effectively we are
246  // ignoring it.
247  log4cplus::Logger::getRoot().setLogLevel(log4cplus::OFF_LOG_LEVEL);
248 
249  // Set the level for the Kea root logger to the given severity and
250  // debug level.
251  log4cplus::Logger kea_root = log4cplus::Logger::getInstance(
253  kea_root.setLogLevel(LoggerLevelImpl::convertFromBindLevel(
254  Level(severity, dbglevel)));
255 
256  if (buffer) {
257  createBufferAppender(kea_root);
258  } else {
259  OutputOption opt;
260  createConsoleAppender(kea_root, opt);
261  }
262 }
263 
264 
265 void LoggerManagerImpl::setAppenderLayout(
266  log4cplus::SharedAppenderPtr& appender,
267  std::string pattern)
268 {
269  // Finally the text of the message
270  appender->setLayout(
271 #if LOG4CPLUS_VERSION < LOG4CPLUS_MAKE_VERSION(2, 0, 0)
272  auto_ptr<log4cplus::Layout>
273 #else
274  unique_ptr<log4cplus::Layout>
275 #endif
276  (new log4cplus::PatternLayout(pattern)));
277 }
278 
279 void LoggerManagerImpl::storeBufferAppenders() {
280  // Walk through all loggers, and find any buffer appenders there
281  log4cplus::LoggerList loggers = log4cplus::Logger::getCurrentLoggers();
282  log4cplus::LoggerList::iterator it;
283  for (it = loggers.begin(); it != loggers.end(); ++it) {
284  log4cplus::SharedAppenderPtr buffer_appender =
285  it->getAppender("buffer");
286  if (buffer_appender) {
287  buffer_appender_store_.push_back(buffer_appender);
288  }
289  }
290 }
291 
292 void LoggerManagerImpl::flushBufferAppenders() {
293  std::vector<log4cplus::SharedAppenderPtr> copy;
294  buffer_appender_store_.swap(copy);
295 
296  std::vector<log4cplus::SharedAppenderPtr>::iterator it;
297  for (it = copy.begin(); it != copy.end(); ++it) {
299  dynamic_cast<internal::BufferAppender*>(it->get());
300  isc_throw_assert(app);
301  app->flush();
302  }
303 }
304 
305 } // namespace log
306 } // namespace isc
const std::string & getRootLoggerName()
Get root logger name.
Definition: logger_name.cc:33
#define isc_throw_assert(expr)
Replacement for assert() that throws if the expression is false.
Definition: isc_assert.h:18
bool flush
true to flush after each message
Definition: output_option.h:70
STL namespace.
Stream stream
stdout/stderr if console output
Definition: output_option.h:69
uint64_t maxsize
0 if no maximum size
Definition: output_option.h:73
std::string facility
syslog facility
Definition: output_option.h:71
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition: data.cc:1152
std::string pattern
log content pattern
Definition: output_option.h:75
Severity
Severity Levels.
Definition: logger_level.h:23
std::string expandLoggerName(const std::string &name)
Expand logger name.
Definition: logger_name.cc:42
Defines the logger used by the top-level component of kea-lfc.
isc::log::Severity getSeverity() const
isc::log::Logger logger("asiodns")
Use the ASIO logger.
std::vector< OutputOption >::const_iterator const_iterator
Log level structure.
Definition: logger_level.h:42
Logging initialization functions.
void flush()
Flush the internal buffer.
unsigned int maxver
Maximum versions (none if <= 0)
Definition: output_option.h:74
std::string filename
Filename if file output.
Definition: output_option.h:72