blob: 21a573cf7af4d05f9a71347f0c1f802589cd1313 [file] [log] [blame]
Junxiao Shi7d054272016-08-04 17:00:41 +00001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesaventocdcde902017-08-23 15:40:22 -04002/*
Alexander Afanasyev354f3822017-03-27 15:26:41 -05003 * Copyright (c) 2013-2017 Regents of the University of California.
Junxiao Shi7d054272016-08-04 17:00:41 +00004 *
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>
Alexander Afanasyev354f3822017-03-27 15:26:41 -050026#include <boost/range/adaptor/map.hpp>
Davide Pesaventocdcde902017-08-23 15:40:22 -040027#include <boost/range/algorithm/copy.hpp>
dmcoomese062a182017-06-12 11:10:31 -050028#include <boost/range/iterator_range.hpp>
Davide Pesaventocdcde902017-08-23 15:40:22 -040029
Junxiao Shi7d054272016-08-04 17:00:41 +000030#include <cstdlib>
Davide Pesaventocdcde902017-08-23 15:40:22 -040031#include <iostream>
32#include <sstream>
Junxiao Shi7d054272016-08-04 17:00:41 +000033
Davide Pesavento88a0d812017-08-19 21:31:42 -040034// 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
Junxiao Shi7d054272016-08-04 17:00:41 +000039namespace ndn {
40namespace util {
41
42static const LogLevel INITIAL_DEFAULT_LEVEL = LogLevel::NONE;
43
44Logging&
45Logging::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
53Logging::Logging()
54{
55 this->setDestinationImpl(shared_ptr<std::ostream>(&std::clog, bind([]{})));
56
Junxiao Shi1fe7ce52016-08-08 05:48:02 +000057 const char* environ = std::getenv("NDN_LOG");
Junxiao Shi7d054272016-08-04 17:00:41 +000058 if (environ != nullptr) {
59 this->setLevelImpl(environ);
60 }
61}
62
63void
64Logging::addLoggerImpl(Logger& logger)
65{
66 std::lock_guard<std::mutex> lock(m_mutex);
67
68 const std::string& moduleName = logger.getModuleName();
dmcoomese062a182017-06-12 11:10:31 -050069 m_loggers.emplace(moduleName, &logger);
Junxiao Shi7d054272016-08-04 17:00:41 +000070
dmcoomese062a182017-06-12 11:10:31 -050071 LogLevel level = findLevel(moduleName);
Junxiao Shi7d054272016-08-04 17:00:41 +000072 logger.setLevel(level);
73}
74
Alexander Afanasyev354f3822017-03-27 15:26:41 -050075std::set<std::string>
dmcoomese062a182017-06-12 11:10:31 -050076Logging::getLoggerNamesImpl() const
Alexander Afanasyev354f3822017-03-27 15:26:41 -050077{
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
dmcoomese062a182017-06-12 11:10:31 -050085LogLevel
86Logging::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
Junxiao Shi7d054272016-08-04 17:00:41 +0000121#ifdef NDN_CXX_HAVE_TESTS
122bool
123Logging::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
137void
dmcoomese062a182017-06-12 11:10:31 -0500138Logging::setLevelImpl(const std::string& prefix, LogLevel level)
Junxiao Shi7d054272016-08-04 17:00:41 +0000139{
140 std::lock_guard<std::mutex> lock(m_mutex);
141
dmcoomese062a182017-06-12 11:10:31 -0500142 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 }
Junxiao Shi7d054272016-08-04 17:00:41 +0000163 }
dmcoomese062a182017-06-12 11:10:31 -0500164 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 }
Junxiao Shi7d054272016-08-04 17:00:41 +0000170 }
171}
172
173void
174Logging::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);
dmcoomese062a182017-06-12 11:10:31 -0500185 LogLevel level = parseLogLevel(configModule.substr(ind + 1));
Junxiao Shi7d054272016-08-04 17:00:41 +0000186 this->setLevelImpl(moduleName, level);
187 }
188}
189
190#ifdef NDN_CXX_HAVE_TESTS
Junxiao Shi7d054272016-08-04 17:00:41 +0000191void
192Logging::resetLevels()
193{
dmcoomese062a182017-06-12 11:10:31 -0500194 this->setLevelImpl("*", INITIAL_DEFAULT_LEVEL);
Junxiao Shi7d054272016-08-04 17:00:41 +0000195 m_enabledLevel.clear();
196}
197#endif // NDN_CXX_HAVE_TESTS
198
199void
200Logging::setDestination(std::ostream& os)
201{
202 setDestination(shared_ptr<std::ostream>(&os, bind([]{})));
203}
204
205void
206Logging::setDestinationImpl(shared_ptr<std::ostream> os)
207{
208 std::lock_guard<std::mutex> lock(m_mutex);
209
dmcoomese062a182017-06-12 11:10:31 -0500210 m_destination = std::move(os);
Junxiao Shi7d054272016-08-04 17:00:41 +0000211
212 auto backend = boost::make_shared<boost::log::sinks::text_ostream_backend>();
213 backend->auto_flush(true);
dmcoomese062a182017-06-12 11:10:31 -0500214 backend->add_stream(boost::shared_ptr<std::ostream>(m_destination.get(), bind([]{})));
Junxiao Shi7d054272016-08-04 17:00:41 +0000215
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
228shared_ptr<std::ostream>
dmcoomese062a182017-06-12 11:10:31 -0500229Logging::getDestination() const
Junxiao Shi7d054272016-08-04 17:00:41 +0000230{
231 return m_destination;
232}
dmcoomese062a182017-06-12 11:10:31 -0500233
234void
235Logging::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
243const std::unordered_map<std::string, LogLevel>&
244Logging::getLevels() const
245{
246 return m_enabledLevel;
247}
Junxiao Shi7d054272016-08-04 17:00:41 +0000248#endif // NDN_CXX_HAVE_TESTS
249
250void
251Logging::flushImpl()
252{
253 m_sink->flush();
254}
255
256} // namespace util
257} // namespace ndn