lsst.log  13.0-2-g15de9a1+4
 All Classes Namespaces Files Functions Variables Macros Pages
Log.cc
Go to the documentation of this file.
1 // -*- LSST-C++ -*-
2 /*
3  * LSST Data Management System
4  * Copyright 2013-2014 LSST Corporation.
5  *
6  * This product includes software developed by the
7  * LSST Project (http://www.lsst.org/).
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the LSST License Statement and
20  * the GNU General Public License along with this program. If not,
21  * see <http://www.lsstcorp.org/LegalNotices/>.
22  */
23 
33 // System headers
34 #include <mutex>
35 #include <pthread.h>
36 #include <stdexcept>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <vector>
40 
41 // Third-party headers
42 #include <log4cxx/basicconfigurator.h>
43 #include <log4cxx/consoleappender.h>
44 #include <log4cxx/helpers/bytearrayinputstream.h>
45 #include <log4cxx/patternlayout.h>
46 #include <log4cxx/propertyconfigurator.h>
47 #include <log4cxx/xml/domconfigurator.h>
48 
49 // Local headers
50 #include "lsst/log/Log.h"
51 #include "lwpID.h"
52 
53 
54 // Max message length for varargs/printf style logging
55 #define MAX_LOG_MSG_LEN 1024
56 
57 namespace {
58 
59 // name of the env. variable pointing to logging config file
60 const char configEnv[] = "LSST_LOG_CONFIG";
61 
62 // dafault message layout pattern
63 const char layoutPattern[] = "%c %p: %m%n";
64 
65 /*
66  * Configure LOG4CXX from file, file must exist. If file extension is .xml
67  * then DOMConfigurator is used, otherwise PropertyConfigurator is called.
68  *
69  * If file parsing fails then error messages are printed to standard error,
70  * but execution continues. LOG4CXX will likely stay un-configured in this
71  * case.
72  */
73 void configFromFile(std::string const& filename) {
74  // find position of extension
75  size_t dotpos = filename.find_last_of(".");
76  if (dotpos != std::string::npos && filename.compare(dotpos, std::string::npos, ".xml") == 0) {
78  } else {
80  }
81 }
82 
83 /*
84  * Default configuration.
85  *
86  * If LSST_LOG_CONFIG envvar is defined and points to existing file then
87  * use that file to configure (can be either XML or Properties file).
88  * Otherwise use pre-defined configuration - add console appender to root
89  * logger using pattern layout with the above pattern, level set to INFO.
90  *
91  * IF LOG4CXX was initialized already and you want to reset it then call
92  * `log4cxx::BasicConfigurator::resetConfiguration()` first.
93  */
94 void defaultConfig() {
95 
96  // if LSST_LOG_CONFIG is set then use that file
97  if (const char* env = getenv(::configEnv)) {
98  if (env[0] and access(env, R_OK) == 0) {
99  configFromFile(env);
100  return;
101  }
102  }
103 
104  // use pre-defined configuration
105  log4cxx::LogString pattern(layoutPattern);
106  log4cxx::LayoutPtr layout(new log4cxx::PatternLayout(pattern));
107  log4cxx::AppenderPtr appender(new log4cxx::ConsoleAppender(layout));
108  auto root = log4cxx::Logger::getRootLogger();
109  root->addAppender(appender);
110  root->setLevel(log4cxx::Level::getInfo());
111 }
112 
113 /*
114  * This method is called exactly once to initialize LOG4CXX configuration.
115  * If LOG4CXX is already initialized then `skipInit` should be set to true.
116  */
117 log4cxx::LoggerPtr log4cxxInit(bool skipInit) {
118 
119  if (! skipInit) {
120  ::defaultConfig();
121  }
122 
123  // returns root logger to be used as default logger
124  return log4cxx::Logger::getRootLogger();
125 }
126 
127 // List of the MDC initialization functions
128 std::vector<std::function<void()>> mdcInitFunctions;
129 std::mutex mdcInitMutex;
130 
131 // more efficient alternative to pthread_once
132 struct PthreadKey {
133  PthreadKey() {
134  // we don't need destructor for a key
135  pthread_key_create(&key, nullptr);
136  }
137  pthread_key_t key;
138 } pthreadKey;
139 
140 }
141 
142 
143 namespace lsst {
144 namespace log {
145 
146 
147 // Log class
148 
152 log4cxx::LoggerPtr const& Log::_defaultLogger(log4cxx::LoggerPtr const& newDefault) {
153  bool isNull = newDefault == log4cxx::LoggerPtr();
154 
155  // initialize on the first call (skip initialization if someone else did that)
156  static log4cxx::LoggerPtr _default(::log4cxxInit(!isNull));
157 
158  if (!isNull) {
159  _default = newDefault;
160  }
161  return _default;
162 }
163 
174 
175  // This removes all defined appenders, resets level to DEBUG,
176  // existing loggers are not deleted, only reset.
177  log4cxx::BasicConfigurator::resetConfiguration();
178 
179  // Do default configuration (only if not configured already?)
180  ::defaultConfig();
181 
182  // reset default logger to the root logger
183  _defaultLogger(log4cxx::Logger::getRootLogger());
184 }
185 
196 void Log::configure(std::string const& filename) {
197  // This removes all defined appenders, resets level to DEBUG,
198  // existing loggers are not deleted, only reset.
199  log4cxx::BasicConfigurator::resetConfiguration();
200 
201  ::configFromFile(filename);
202 
203  // reset default logger to the root logger
204  _defaultLogger(log4cxx::Logger::getRootLogger());
205 }
206 
213 void Log::configure_prop(std::string const& properties) {
214  // This removes all defined appenders, resets level to DEBUG,
215  // existing loggers are not deleted, only reset.
216  log4cxx::BasicConfigurator::resetConfiguration();
217 
218  std::vector<unsigned char> data(properties.begin(), properties.end());
219  log4cxx::helpers::InputStreamPtr inStream(new log4cxx::helpers::ByteArrayInputStream(data));
220  log4cxx::helpers::Properties prop;
221  prop.load(inStream);
223 
224  // reset default logger to the root logger
225  _defaultLogger(log4cxx::Logger::getRootLogger());
226 }
227 
232  return getDefaultLogger().getName();
233 }
234 
238 std::string Log::getName() const {
239  std::string name = _logger->getName();
240  if (name == "root") {
241  name.clear();
242  }
243  return name;
244 }
245 
254 Log Log::getLogger(std::string const& loggername) {
255  if (loggername.empty()){
256  return getDefaultLogger();
257  } else {
258  return Log(log4cxx::Logger::getLogger(loggername));
259  }
260 }
261 
266 void Log::pushContext(std::string const& name) {
267  // can't handle empty names
268  if (name.empty()) {
269  throw std::invalid_argument("lsst::log::Log::pushContext(): "
270  "empty context name is not allowed");
271  }
272  // we do not allow multi-level context (logger1.logger2)
273  if (name.find('.') != std::string::npos) {
274  throw std::invalid_argument("lsst::log::Log::pushContext(): "
275  "multi-level contexts are not allowed: " + name);
276  }
277 
278  // Construct new default logger name
279  std::string newName = _defaultLogger()->getName();
280  if (newName == "root") {
281  newName = name;
282  } else {
283  newName += ".";
284  newName += name;
285  }
286  // Update defaultLogger
287  _defaultLogger(log4cxx::Logger::getLogger(newName));
288 }
289 
294  // switch to parent logger, this assumes that loggers are not
295  // re-parented between calls to push and pop
296  log4cxx::LoggerPtr parent = _defaultLogger()->getParent();
297  // root logger does not have parent, stay at root instead
298  if (parent) {
299  _defaultLogger(parent);
300  }
301 }
302 
311 void Log::MDC(std::string const& key, std::string const& value) {
312  log4cxx::MDC::put(key, value);
313 }
314 
319 void Log::MDCRemove(std::string const& key) {
320  log4cxx::MDC::remove(key);
321 }
322 
323 int Log::MDCRegisterInit(std::function<void()> function) {
324 
325  std::lock_guard<std::mutex> lock(mdcInitMutex);
326 
327  // logMsg may have been called already in this thread, to make sure that
328  // this function is executed in this thread call it explicitly
329  function();
330 
331  // store function for later use
332  ::mdcInitFunctions.push_back(std::move(function));
333 
334  // return arbitrary number
335  return 1;
336 }
337 
342 void Log::setLevel(int level) {
343  _logger->setLevel(log4cxx::Level::toLevel(level));
344 }
345 
349 int Log::getLevel() const {
350  log4cxx::LevelPtr level = _logger->getLevel();
351  int levelno = -1;
352  if (level != NULL) {
353  levelno = level->toInt();
354  }
355  return levelno;
356 }
357 
364 bool Log::isEnabledFor(int level) const {
365  if (_logger->isEnabledFor(log4cxx::Level::toLevel(level))) {
366  return true;
367  } else {
368  return false;
369  }
370 }
371 
375 void Log::log(log4cxx::LevelPtr level,
376  log4cxx::spi::LocationInfo const& location,
377  char const* fmt,
378  ...
379  ) {
380  va_list args;
381  va_start(args, fmt);
382  char msg[MAX_LOG_MSG_LEN];
383  vsnprintf(msg, MAX_LOG_MSG_LEN, fmt, args);
384  logMsg(level, location, msg);
385 }
386 
389 void Log::logMsg(log4cxx::LevelPtr level,
390  log4cxx::spi::LocationInfo const& location,
391  std::string const& msg
392  ) {
393 
394  // do one-time per-thread initialization, this was implemented
395  // with thread_local initially but clang on OS X did not support it
396  void *ptr = pthread_getspecific(::pthreadKey.key);
397  if (ptr == nullptr) {
398 
399  // use pointer value as a flag, don't care where it points to
400  ptr = static_cast<void*>(&::pthreadKey);
401  pthread_setspecific(::pthreadKey.key, ptr);
402 
403  std::lock_guard<std::mutex> lock(mdcInitMutex);
404  // call all functions in the MDC init list
405  for (auto& fun: mdcInitFunctions) {
406  fun();
407  }
408  }
409 
410  // forward everything to logger
411  _logger->forcedLog(level, msg, location);
412 }
413 
414 unsigned lwpID() {
415  return detail::lwpID();
416 }
417 
418 }} // namespace lsst::log
static Log getDefaultLogger()
Return default logger instance, same as default constructor.
Definition: Log.h:765
static void configure()
Explicitly configures log4cxx and initializes logging system.
Definition: Log.cc:173
static std::string getDefaultLoggerName()
Get the current default logger name.
Definition: Log.cc:231
void logMsg(log4cxx::LevelPtr level, log4cxx::spi::LocationInfo const &location, std::string const &msg)
Method used by LOGS_INFO and similar macros to process a log message.
Definition: Log.cc:389
This static class includes a variety of methods for interacting with the the logging module...
Definition: Log.h:723
unsigned lwpID()
Definition: lwpID.cc:40
void log(log4cxx::LevelPtr level, log4cxx::spi::LocationInfo const &location, char const *fmt,...)
Method used by LOG_INFO and similar macros to process a log message with variable arguments along wit...
Definition: Log.cc:375
bool isEnabledFor(int level) const
Return whether the logging threshold of the logger is less than or equal to LEVEL.
Definition: Log.cc:364
static void MDC(std::string const &key, std::string const &value)
Places a KEY/VALUE pair in the Mapped Diagnostic Context (MDC) for the current thread.
Definition: Log.cc:311
LSST DM logging module built on log4cxx.
static void MDCRemove(std::string const &key)
Remove the value associated with KEY within the MDC.
Definition: Log.cc:319
int getLevel() const
Retrieve the logging threshold.
Definition: Log.cc:349
void setLevel(int level)
Set the logging threshold to LEVEL.
Definition: Log.cc:342
static Log getLogger(Log const &logger)
Definition: Log.h:772
unsigned lwpID()
Function which returns LWP ID on platforms which support it.
Definition: Log.cc:414
std::string getName() const
Get the logger name associated with the Log object.
Definition: Log.cc:238
static void configure_prop(std::string const &properties)
Configures log4cxx using a string containing the list of properties, equivalent to configuring from a...
Definition: Log.cc:213
#define MAX_LOG_MSG_LEN
Definition: Log.cc:55
static void popContext()
Pops the last pushed name off the global hierarchical default logger name.
Definition: Log.cc:293
static void pushContext(std::string const &name)
Pushes NAME onto the global hierarchical default logger name.
Definition: Log.cc:266
static int MDCRegisterInit(std::function< void()> function)
Definition: Log.cc:323