blob: 0e68092fbea37b8f342c468df63f7e0dcb883365 [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/*
Davide Pesavento1c9c96c2018-04-25 10:45:08 -04003 * Copyright (c) 2013-2018 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
Davide Pesavento1c9c96c2018-04-25 10:45:08 -040071 logger.setLevel(findLevel(moduleName));
72}
73
74void
75Logging::registerLoggerNameImpl(std::string name)
76{
77 std::lock_guard<std::mutex> lock(m_mutex);
78 m_loggers.emplace(std::move(name), nullptr);
Junxiao Shi7d054272016-08-04 17:00:41 +000079}
80
Alexander Afanasyev354f3822017-03-27 15:26:41 -050081std::set<std::string>
dmcoomese062a182017-06-12 11:10:31 -050082Logging::getLoggerNamesImpl() const
Alexander Afanasyev354f3822017-03-27 15:26:41 -050083{
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
dmcoomese062a182017-06-12 11:10:31 -050091LogLevel
Davide Pesavento1c9c96c2018-04-25 10:45:08 -040092Logging::findLevel(std::string mn) const
dmcoomese062a182017-06-12 11:10:31 -050093{
dmcoomese062a182017-06-12 11:10:31 -050094 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 }
Davide Pesavento1c9c96c2018-04-25 10:45:08 -0400117
dmcoomese062a182017-06-12 11:10:31 -0500118 auto it = m_enabledLevel.find(mn);
Davide Pesavento1c9c96c2018-04-25 10:45:08 -0400119 return it != m_enabledLevel.end() ? it->second : INITIAL_DEFAULT_LEVEL;
dmcoomese062a182017-06-12 11:10:31 -0500120}
121
Junxiao Shi7d054272016-08-04 17:00:41 +0000122#ifdef NDN_CXX_HAVE_TESTS
123bool
124Logging::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
138void
dmcoomese062a182017-06-12 11:10:31 -0500139Logging::setLevelImpl(const std::string& prefix, LogLevel level)
Junxiao Shi7d054272016-08-04 17:00:41 +0000140{
141 std::lock_guard<std::mutex> lock(m_mutex);
142
dmcoomese062a182017-06-12 11:10:31 -0500143 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
Davide Pesavento1c9c96c2018-04-25 10:45:08 -0400159 for (const auto& pair : m_loggers) {
160 if (pair.first.compare(0, p.size(), p) == 0 && pair.second != nullptr) {
161 pair.second->setLevel(level);
dmcoomese062a182017-06-12 11:10:31 -0500162 }
163 }
Junxiao Shi7d054272016-08-04 17:00:41 +0000164 }
dmcoomese062a182017-06-12 11:10:31 -0500165 else {
166 m_enabledLevel[prefix] = level;
167 auto range = boost::make_iterator_range(m_loggers.equal_range(prefix));
Davide Pesavento1c9c96c2018-04-25 10:45:08 -0400168 for (const auto& pair : range) {
169 if (pair.second != nullptr) {
170 pair.second->setLevel(level);
171 }
dmcoomese062a182017-06-12 11:10:31 -0500172 }
Junxiao Shi7d054272016-08-04 17:00:41 +0000173 }
174}
175
176void
177Logging::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 BOOST_THROW_EXCEPTION(std::invalid_argument("malformed logging config: '=' is missing"));
185 }
186
187 std::string moduleName = configModule.substr(0, ind);
dmcoomese062a182017-06-12 11:10:31 -0500188 LogLevel level = parseLogLevel(configModule.substr(ind + 1));
Junxiao Shi7d054272016-08-04 17:00:41 +0000189 this->setLevelImpl(moduleName, level);
190 }
191}
192
193#ifdef NDN_CXX_HAVE_TESTS
Junxiao Shi7d054272016-08-04 17:00:41 +0000194void
195Logging::resetLevels()
196{
dmcoomese062a182017-06-12 11:10:31 -0500197 this->setLevelImpl("*", INITIAL_DEFAULT_LEVEL);
Junxiao Shi7d054272016-08-04 17:00:41 +0000198 m_enabledLevel.clear();
199}
200#endif // NDN_CXX_HAVE_TESTS
201
202void
203Logging::setDestination(std::ostream& os)
204{
205 setDestination(shared_ptr<std::ostream>(&os, bind([]{})));
206}
207
208void
209Logging::setDestinationImpl(shared_ptr<std::ostream> os)
210{
211 std::lock_guard<std::mutex> lock(m_mutex);
212
dmcoomese062a182017-06-12 11:10:31 -0500213 m_destination = std::move(os);
Junxiao Shi7d054272016-08-04 17:00:41 +0000214
215 auto backend = boost::make_shared<boost::log::sinks::text_ostream_backend>();
216 backend->auto_flush(true);
dmcoomese062a182017-06-12 11:10:31 -0500217 backend->add_stream(boost::shared_ptr<std::ostream>(m_destination.get(), bind([]{})));
Junxiao Shi7d054272016-08-04 17:00:41 +0000218
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
231shared_ptr<std::ostream>
dmcoomese062a182017-06-12 11:10:31 -0500232Logging::getDestination() const
Junxiao Shi7d054272016-08-04 17:00:41 +0000233{
234 return m_destination;
235}
dmcoomese062a182017-06-12 11:10:31 -0500236
237void
238Logging::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
246const std::unordered_map<std::string, LogLevel>&
247Logging::getLevels() const
248{
249 return m_enabledLevel;
250}
Junxiao Shi7d054272016-08-04 17:00:41 +0000251#endif // NDN_CXX_HAVE_TESTS
252
253void
254Logging::flushImpl()
255{
256 m_sink->flush();
257}
258
259} // namespace util
260} // namespace ndn