logging.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013-2017 Regents of the University of California.
4  *
5  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6  *
7  * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8  * terms of the GNU Lesser General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later version.
10  *
11  * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13  * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14  *
15  * You should have received copies of the GNU General Public License and GNU Lesser
16  * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17  * <http://www.gnu.org/licenses/>.
18  *
19  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20  */
21 
22 #include "logging.hpp"
23 #include "logger.hpp"
24 
25 #include <boost/log/expressions.hpp>
26 #include <boost/range/adaptor/map.hpp>
27 #include <boost/range/algorithm/copy.hpp>
28 #include <boost/range/iterator_range.hpp>
29 
30 #include <cstdlib>
31 #include <iostream>
32 #include <sstream>
33 
34 // suppress warning caused by <boost/log/sinks/text_ostream_backend.hpp>
35 #ifdef __clang__
36 #pragma clang diagnostic ignored "-Wundefined-func-template"
37 #endif
38 
39 namespace ndn {
40 namespace util {
41 
43 
44 Logging&
45 Logging::get()
46 {
47  // Initialization of block-scope variables with static storage duration is thread-safe.
48  // See ISO C++ standard [stmt.dcl]/4
49  static Logging instance;
50  return instance;
51 }
52 
53 Logging::Logging()
54 {
55  this->setDestinationImpl(shared_ptr<std::ostream>(&std::clog, bind([]{})));
56 
57  const char* environ = std::getenv("NDN_LOG");
58  if (environ != nullptr) {
59  this->setLevelImpl(environ);
60  }
61 }
62 
63 void
64 Logging::addLoggerImpl(Logger& logger)
65 {
66  std::lock_guard<std::mutex> lock(m_mutex);
67 
68  const std::string& moduleName = logger.getModuleName();
69  m_loggers.emplace(moduleName, &logger);
70 
71  LogLevel level = findLevel(moduleName);
72  logger.setLevel(level);
73 }
74 
75 std::set<std::string>
76 Logging::getLoggerNamesImpl() const
77 {
78  std::lock_guard<std::mutex> lock(m_mutex);
79 
80  std::set<std::string> loggerNames;
81  boost::copy(m_loggers | boost::adaptors::map_keys, std::inserter(loggerNames, loggerNames.end()));
82  return loggerNames;
83 }
84 
86 Logging::findLevel(const std::string& moduleName) const
87 {
88  std::string mn = moduleName;
89  while (!mn.empty()) {
90  auto it = m_enabledLevel.find(mn);
91  if (it != m_enabledLevel.end()) {
92  return it->second;
93  }
94  size_t pos = mn.find_last_of('.');
95  if (pos < mn.size() - 1) {
96  mn = mn.substr(0, pos + 1);
97  }
98  else if (pos == mn.size() - 1) {
99  mn.pop_back();
100  pos = mn.find_last_of('.');
101  if (pos != std::string::npos) {
102  mn = mn.substr(0, pos + 1);
103  }
104  else {
105  mn = "";
106  }
107  }
108  else {
109  mn = "";
110  }
111  }
112  auto it = m_enabledLevel.find(mn);
113  if (it != m_enabledLevel.end()) {
114  return it->second;
115  }
116  else {
117  return INITIAL_DEFAULT_LEVEL;
118  }
119 }
120 
121 #ifdef NDN_CXX_HAVE_TESTS
122 bool
123 Logging::removeLogger(Logger& logger)
124 {
125  const std::string& moduleName = logger.getModuleName();
126  auto range = m_loggers.equal_range(moduleName);
127  for (auto i = range.first; i != range.second; ++i) {
128  if (i->second == &logger) {
129  m_loggers.erase(i);
130  return true;
131  }
132  }
133  return false;
134 }
135 #endif // NDN_CXX_HAVE_TESTS
136 
137 void
138 Logging::setLevelImpl(const std::string& prefix, LogLevel level)
139 {
140  std::lock_guard<std::mutex> lock(m_mutex);
141 
142  if (prefix.empty() || prefix.back() == '*') {
143  std::string p = prefix;
144  if (!p.empty()) {
145  p.pop_back();
146  }
147 
148  for (auto i = m_enabledLevel.begin(); i != m_enabledLevel.end();) {
149  if (i->first.compare(0, p.size(), p) == 0) {
150  i = m_enabledLevel.erase(i);
151  }
152  else {
153  ++i;
154  }
155  }
156  m_enabledLevel[p] = level;
157 
158  for (auto&& it : m_loggers) {
159  if (it.first.compare(0, p.size(), p) == 0) {
160  it.second->setLevel(level);
161  }
162  }
163  }
164  else {
165  m_enabledLevel[prefix] = level;
166  auto range = boost::make_iterator_range(m_loggers.equal_range(prefix));
167  for (auto&& it : range) {
168  it.second->setLevel(level);
169  }
170  }
171 }
172 
173 void
174 Logging::setLevelImpl(const std::string& config)
175 {
176  std::stringstream ss(config);
177  std::string configModule;
178  while (std::getline(ss, configModule, ':')) {
179  size_t ind = configModule.find('=');
180  if (ind == std::string::npos) {
181  BOOST_THROW_EXCEPTION(std::invalid_argument("malformed logging config: '=' is missing"));
182  }
183 
184  std::string moduleName = configModule.substr(0, ind);
185  LogLevel level = parseLogLevel(configModule.substr(ind + 1));
186  this->setLevelImpl(moduleName, level);
187  }
188 }
189 
190 #ifdef NDN_CXX_HAVE_TESTS
191 void
192 Logging::resetLevels()
193 {
194  this->setLevelImpl("*", INITIAL_DEFAULT_LEVEL);
195  m_enabledLevel.clear();
196 }
197 #endif // NDN_CXX_HAVE_TESTS
198 
199 void
200 Logging::setDestination(std::ostream& os)
201 {
202  setDestination(shared_ptr<std::ostream>(&os, bind([]{})));
203 }
204 
205 void
206 Logging::setDestinationImpl(shared_ptr<std::ostream> os)
207 {
208  std::lock_guard<std::mutex> lock(m_mutex);
209 
210  m_destination = std::move(os);
211 
212  auto backend = boost::make_shared<boost::log::sinks::text_ostream_backend>();
213  backend->auto_flush(true);
214  backend->add_stream(boost::shared_ptr<std::ostream>(m_destination.get(), bind([]{})));
215 
216  if (m_sink != nullptr) {
217  boost::log::core::get()->remove_sink(m_sink);
218  m_sink->flush();
219  m_sink.reset();
220  }
221 
222  m_sink = boost::make_shared<Sink>(backend);
223  m_sink->set_formatter(boost::log::expressions::stream << boost::log::expressions::message);
224  boost::log::core::get()->add_sink(m_sink);
225 }
226 
227 #ifdef NDN_CXX_HAVE_TESTS
228 shared_ptr<std::ostream>
229 Logging::getDestination() const
230 {
231  return m_destination;
232 }
233 
234 void
235 Logging::setLevelImpl(const std::unordered_map<std::string, LogLevel>& prefixRules)
236 {
237  resetLevels();
238  for (const auto& rule : prefixRules) {
239  setLevelImpl(rule.first, rule.second);
240  }
241 }
242 
243 const std::unordered_map<std::string, LogLevel>&
244 Logging::getLevels() const
245 {
246  return m_enabledLevel;
247 }
248 #endif // NDN_CXX_HAVE_TESTS
249 
250 void
251 Logging::flushImpl()
252 {
253  m_sink->flush();
254 }
255 
256 } // namespace util
257 } // namespace ndn
controls the logging facility
Definition: logging.hpp:46
Copyright (c) 2013-2017 Regents of the University of California.
Definition: common.hpp:66
static void setDestination(shared_ptr< std::ostream > os)
set log destination
Definition: logging.hpp:204
LogLevel
indicates the severity level of a log message
Definition: logger.hpp:40
LogLevel parseLogLevel(const std::string &s)
parse LogLevel from string
Definition: logger.cpp:59
static const LogLevel INITIAL_DEFAULT_LEVEL
Definition: logging.cpp:42
represents a logger in logging facility
Definition: logger.hpp:66
const std::string & getModuleName() const
Definition: logger.hpp:73
void setLevel(LogLevel level)
Definition: logger.hpp:85