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-2019 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 "ndn-cxx/util/logging.hpp"
23 #include "ndn-cxx/util/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, [] (auto) {}));
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  logger.setLevel(findLevel(moduleName));
72 }
73 
74 void
75 Logging::registerLoggerNameImpl(std::string name)
76 {
77  std::lock_guard<std::mutex> lock(m_mutex);
78  m_loggers.emplace(std::move(name), nullptr);
79 }
80 
81 std::set<std::string>
82 Logging::getLoggerNamesImpl() const
83 {
84  std::lock_guard<std::mutex> lock(m_mutex);
85 
86  std::set<std::string> loggerNames;
87  boost::copy(m_loggers | boost::adaptors::map_keys, std::inserter(loggerNames, loggerNames.end()));
88  return loggerNames;
89 }
90 
92 Logging::findLevel(std::string mn) const
93 {
94  while (!mn.empty()) {
95  auto it = m_enabledLevel.find(mn);
96  if (it != m_enabledLevel.end()) {
97  return it->second;
98  }
99  size_t pos = mn.find_last_of('.');
100  if (pos < mn.size() - 1) {
101  mn = mn.substr(0, pos + 1);
102  }
103  else if (pos == mn.size() - 1) {
104  mn.pop_back();
105  pos = mn.find_last_of('.');
106  if (pos != std::string::npos) {
107  mn = mn.substr(0, pos + 1);
108  }
109  else {
110  mn = "";
111  }
112  }
113  else {
114  mn = "";
115  }
116  }
117 
118  auto it = m_enabledLevel.find(mn);
119  return it != m_enabledLevel.end() ? it->second : INITIAL_DEFAULT_LEVEL;
120 }
121 
122 #ifdef NDN_CXX_HAVE_TESTS
123 bool
124 Logging::removeLogger(Logger& logger)
125 {
126  const std::string& moduleName = logger.getModuleName();
127  auto range = m_loggers.equal_range(moduleName);
128  for (auto i = range.first; i != range.second; ++i) {
129  if (i->second == &logger) {
130  m_loggers.erase(i);
131  return true;
132  }
133  }
134  return false;
135 }
136 #endif // NDN_CXX_HAVE_TESTS
137 
138 void
139 Logging::setLevelImpl(const std::string& prefix, LogLevel level)
140 {
141  std::lock_guard<std::mutex> lock(m_mutex);
142 
143  if (prefix.empty() || prefix.back() == '*') {
144  std::string p = prefix;
145  if (!p.empty()) {
146  p.pop_back();
147  }
148 
149  for (auto i = m_enabledLevel.begin(); i != m_enabledLevel.end();) {
150  if (i->first.compare(0, p.size(), p) == 0) {
151  i = m_enabledLevel.erase(i);
152  }
153  else {
154  ++i;
155  }
156  }
157  m_enabledLevel[p] = level;
158 
159  for (const auto& pair : m_loggers) {
160  if (pair.first.compare(0, p.size(), p) == 0 && pair.second != nullptr) {
161  pair.second->setLevel(level);
162  }
163  }
164  }
165  else {
166  m_enabledLevel[prefix] = level;
167  auto range = boost::make_iterator_range(m_loggers.equal_range(prefix));
168  for (const auto& pair : range) {
169  if (pair.second != nullptr) {
170  pair.second->setLevel(level);
171  }
172  }
173  }
174 }
175 
176 void
177 Logging::setLevelImpl(const std::string& config)
178 {
179  std::stringstream ss(config);
180  std::string configModule;
181  while (std::getline(ss, configModule, ':')) {
182  size_t ind = configModule.find('=');
183  if (ind == std::string::npos) {
184  NDN_THROW(std::invalid_argument("malformed logging config: '=' is missing"));
185  }
186 
187  std::string moduleName = configModule.substr(0, ind);
188  LogLevel level = parseLogLevel(configModule.substr(ind + 1));
189  this->setLevelImpl(moduleName, level);
190  }
191 }
192 
193 #ifdef NDN_CXX_HAVE_TESTS
194 void
195 Logging::resetLevels()
196 {
197  this->setLevelImpl("*", INITIAL_DEFAULT_LEVEL);
198  m_enabledLevel.clear();
199 }
200 #endif // NDN_CXX_HAVE_TESTS
201 
202 void
203 Logging::setDestination(std::ostream& os)
204 {
205  setDestination(shared_ptr<std::ostream>(&os, [] (auto) {}));
206 }
207 
208 void
209 Logging::setDestinationImpl(shared_ptr<std::ostream> os)
210 {
211  std::lock_guard<std::mutex> lock(m_mutex);
212 
213  m_destination = std::move(os);
214 
215  auto backend = boost::make_shared<boost::log::sinks::text_ostream_backend>();
216  backend->auto_flush(true);
217  backend->add_stream(boost::shared_ptr<std::ostream>(m_destination.get(), [] (auto) {}));
218 
219  if (m_sink != nullptr) {
220  boost::log::core::get()->remove_sink(m_sink);
221  m_sink->flush();
222  m_sink.reset();
223  }
224 
225  m_sink = boost::make_shared<Sink>(backend);
226  m_sink->set_formatter(boost::log::expressions::stream << boost::log::expressions::message);
227  boost::log::core::get()->add_sink(m_sink);
228 }
229 
230 #ifdef NDN_CXX_HAVE_TESTS
231 shared_ptr<std::ostream>
232 Logging::getDestination() const
233 {
234  return m_destination;
235 }
236 
237 void
238 Logging::setLevelImpl(const std::unordered_map<std::string, LogLevel>& prefixRules)
239 {
240  resetLevels();
241  for (const auto& rule : prefixRules) {
242  setLevelImpl(rule.first, rule.second);
243  }
244 }
245 
246 const std::unordered_map<std::string, LogLevel>&
247 Logging::getLevels() const
248 {
249  return m_enabledLevel;
250 }
251 #endif // NDN_CXX_HAVE_TESTS
252 
253 void
254 Logging::flushImpl()
255 {
256  m_sink->flush();
257 }
258 
259 } // namespace util
260 } // namespace ndn
Controls the logging facility.
Definition: logging.hpp:46
Definition: data.cpp:26
static void setDestination(shared_ptr< std::ostream > os)
Set log destination.
Definition: logging.hpp:198
#define NDN_THROW(e)
Definition: exception.hpp:61
LogLevel
Indicates the severity level of a log message.
Definition: logger.hpp:40
R & get(variant< T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15 > &v, nonstd::in_place_t(&)(nonstd::detail::in_place_type_tag< R >)=nonstd::in_place_type< R >)
Definition: variant.hpp:1753
LogLevel parseLogLevel(const std::string &s)
Parse LogLevel from a string.
Definition: logger.cpp:60
static const LogLevel INITIAL_DEFAULT_LEVEL
Definition: logging.cpp:42
Represents a log module in the logging facility.
Definition: logger.hpp:68
const std::string & getModuleName() const
Definition: logger.hpp:78
void setLevel(LogLevel level)
Definition: logger.hpp:90