LSSTApplications  16.0-11-g09ed895+2,16.0-11-g12e47bd,16.0-11-g9bb73b2+6,16.0-12-g5c924a4+6,16.0-14-g9a974b3+1,16.0-15-g1417920+1,16.0-15-gdd5ca33+1,16.0-16-gf0259e2,16.0-17-g31abd91+7,16.0-17-g7d7456e+7,16.0-17-ga3d2e9f+13,16.0-18-ga4d4bcb+1,16.0-18-gd06566c+1,16.0-2-g0febb12+21,16.0-2-g9d5294e+69,16.0-2-ga8830df+6,16.0-20-g21842373+7,16.0-24-g3eae5ec,16.0-28-gfc9ea6c+4,16.0-29-ge8801f9,16.0-3-ge00e371+34,16.0-4-g18f3627+13,16.0-4-g5f3a788+20,16.0-4-ga3eb747+10,16.0-4-gabf74b7+29,16.0-4-gb13d127+6,16.0-49-g42e581f7+6,16.0-5-g27fb78a+7,16.0-5-g6a53317+34,16.0-5-gb3f8a4b+87,16.0-6-g9321be7+4,16.0-6-gcbc7b31+42,16.0-6-gf49912c+29,16.0-7-gd2eeba5+51,16.0-71-ge89f8615e,16.0-8-g21fd5fe+29,16.0-8-g3a9f023+20,16.0-8-g4734f7a+1,16.0-8-g5858431+3,16.0-9-gf5c1f43+8,master-gd73dc1d098+1,w.2019.01
LSSTDataManagementBasePackage
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 
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 
312 void Log::MDC(std::string const& key, std::string const& value) {
313  // put() does not remove existing mapping, to make it less confusing
314  // for clients which expect that MDC() always overwrites existing mapping
315  // we explicitly remove it first if it exists.
316  log4cxx::MDC::remove(key);
317  log4cxx::MDC::put(key, value);
318 }
319 
325  log4cxx::MDC::remove(key);
326 }
327 
328 int Log::MDCRegisterInit(std::function<void()> function) {
329 
330  std::lock_guard<std::mutex> lock(mdcInitMutex);
331 
332  // logMsg may have been called already in this thread, to make sure that
333  // this function is executed in this thread call it explicitly
334  function();
335 
336  // store function for later use
337  ::mdcInitFunctions.push_back(std::move(function));
338 
339  // return arbitrary number
340  return 1;
341 }
342 
347 void Log::setLevel(int level) {
348  _logger->setLevel(log4cxx::Level::toLevel(level));
349 }
350 
354 int Log::getLevel() const {
355  log4cxx::LevelPtr level = _logger->getLevel();
356  int levelno = -1;
357  if (level != NULL) {
358  levelno = level->toInt();
359  }
360  return levelno;
361 }
362 
369 bool Log::isEnabledFor(int level) const {
370  if (_logger->isEnabledFor(log4cxx::Level::toLevel(level))) {
371  return true;
372  } else {
373  return false;
374  }
375 }
376 
380 void Log::log(log4cxx::LevelPtr level,
381  log4cxx::spi::LocationInfo const& location,
382  char const* fmt,
383  ...
384  ) {
385  va_list args;
386  va_start(args, fmt);
387  char msg[MAX_LOG_MSG_LEN];
388  vsnprintf(msg, MAX_LOG_MSG_LEN, fmt, args);
389  logMsg(level, location, msg);
390 }
391 
394 void Log::logMsg(log4cxx::LevelPtr level,
395  log4cxx::spi::LocationInfo const& location,
396  std::string const& msg
397  ) {
398 
399  // do one-time per-thread initialization, this was implemented
400  // with thread_local initially but clang on OS X did not support it
401  void *ptr = pthread_getspecific(::pthreadKey.key);
402  if (ptr == nullptr) {
403 
404  // use pointer value as a flag, don't care where it points to
405  ptr = static_cast<void*>(&::pthreadKey);
406  pthread_setspecific(::pthreadKey.key, ptr);
407 
408  std::lock_guard<std::mutex> lock(mdcInitMutex);
409  // call all functions in the MDC init list
410  for (auto& fun: mdcInitFunctions) {
411  fun();
412  }
413  }
414 
415  // forward everything to logger
416  _logger->forcedLog(level, msg, location);
417 }
418 
419 unsigned lwpID() {
420  return detail::lwpID();
421 }
422 
423 }} // namespace lsst::log
uint64_t * ptr
Definition: RangeSet.cc:88
T empty(T... args)
char * data
Definition: BaseTable.cc:205
This static class includes a variety of methods for interacting with the the logging module...
Definition: Log.h:723
T end(T... args)
STL class.
LSST DM logging module built on log4cxx.
T find_last_of(T... args)
A base class for image defects.
Definition: cameraGeom.dox:3
def getLevel(loggername)
T clear(T... args)
def setLevel(loggername, level)
T move(T... args)
T find(T... args)
STL class.
unsigned lwpID()
Function which returns LWP ID on platforms which support it.
Definition: Log.cc:419
T begin(T... args)
Key< U > key
Definition: Schema.cc:281
#define MAX_LOG_MSG_LEN
Definition: Log.cc:55
def isEnabledFor(logger, level)
def configure_prop(properties)
Definition: logContinued.py:99
T compare(T... args)