blob: 06948c17a8d6522253211ec0943648a985f36f84 [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>
28
Junxiao Shi7d054272016-08-04 17:00:41 +000029#include <cstdlib>
Davide Pesaventocdcde902017-08-23 15:40:22 -040030#include <iostream>
31#include <sstream>
Junxiao Shi7d054272016-08-04 17:00:41 +000032
Davide Pesavento88a0d812017-08-19 21:31:42 -040033// suppress warning caused by <boost/log/sinks/text_ostream_backend.hpp>
34#ifdef __clang__
35#pragma clang diagnostic ignored "-Wundefined-func-template"
36#endif
37
Junxiao Shi7d054272016-08-04 17:00:41 +000038namespace ndn {
39namespace util {
40
41static const LogLevel INITIAL_DEFAULT_LEVEL = LogLevel::NONE;
42
43Logging&
44Logging::get()
45{
46 // Initialization of block-scope variables with static storage duration is thread-safe.
47 // See ISO C++ standard [stmt.dcl]/4
48 static Logging instance;
49 return instance;
50}
51
52Logging::Logging()
53{
54 this->setDestinationImpl(shared_ptr<std::ostream>(&std::clog, bind([]{})));
55
Junxiao Shi1fe7ce52016-08-08 05:48:02 +000056 const char* environ = std::getenv("NDN_LOG");
Junxiao Shi7d054272016-08-04 17:00:41 +000057 if (environ != nullptr) {
58 this->setLevelImpl(environ);
59 }
60}
61
62void
63Logging::addLoggerImpl(Logger& logger)
64{
65 std::lock_guard<std::mutex> lock(m_mutex);
66
67 const std::string& moduleName = logger.getModuleName();
68 m_loggers.insert({moduleName, &logger});
69
70 auto levelIt = m_enabledLevel.find(moduleName);
71 if (levelIt == m_enabledLevel.end()) {
72 levelIt = m_enabledLevel.find("*");
73 }
74 LogLevel level = levelIt == m_enabledLevel.end() ? INITIAL_DEFAULT_LEVEL : levelIt->second;
75 logger.setLevel(level);
76}
77
Alexander Afanasyev354f3822017-03-27 15:26:41 -050078std::set<std::string>
79Logging::getLoggerNamesImpl()
80{
81 std::lock_guard<std::mutex> lock(m_mutex);
82
83 std::set<std::string> loggerNames;
84 boost::copy(m_loggers | boost::adaptors::map_keys, std::inserter(loggerNames, loggerNames.end()));
85 return loggerNames;
86}
87
Junxiao Shi7d054272016-08-04 17:00:41 +000088#ifdef NDN_CXX_HAVE_TESTS
89bool
90Logging::removeLogger(Logger& logger)
91{
92 const std::string& moduleName = logger.getModuleName();
93 auto range = m_loggers.equal_range(moduleName);
94 for (auto i = range.first; i != range.second; ++i) {
95 if (i->second == &logger) {
96 m_loggers.erase(i);
97 return true;
98 }
99 }
100 return false;
101}
102#endif // NDN_CXX_HAVE_TESTS
103
104void
105Logging::setLevelImpl(const std::string& moduleName, LogLevel level)
106{
107 std::lock_guard<std::mutex> lock(m_mutex);
108
109 if (moduleName == "*") {
110 this->setDefaultLevel(level);
111 return;
112 }
113
114 m_enabledLevel[moduleName] = level;
115 auto range = m_loggers.equal_range(moduleName);
116 for (auto i = range.first; i != range.second; ++i) {
117 i->second->setLevel(level);
118 }
119}
120
121void
122Logging::setDefaultLevel(LogLevel level)
123{
124 m_enabledLevel.clear();
125 m_enabledLevel["*"] = level;
126
127 for (auto i = m_loggers.begin(); i != m_loggers.end(); ++i) {
128 i->second->setLevel(level);
129 }
130}
131
132void
133Logging::setLevelImpl(const std::string& config)
134{
135 std::stringstream ss(config);
136 std::string configModule;
137 while (std::getline(ss, configModule, ':')) {
138 size_t ind = configModule.find('=');
139 if (ind == std::string::npos) {
140 BOOST_THROW_EXCEPTION(std::invalid_argument("malformed logging config: '=' is missing"));
141 }
142
143 std::string moduleName = configModule.substr(0, ind);
144 LogLevel level = parseLogLevel(configModule.substr(ind+1));
145
146 this->setLevelImpl(moduleName, level);
147 }
148}
149
150#ifdef NDN_CXX_HAVE_TESTS
151std::string
152Logging::getLevels() const
153{
154 std::ostringstream os;
155
156 auto defaultLevelIt = m_enabledLevel.find("*");
157 if (defaultLevelIt != m_enabledLevel.end()) {
158 os << "*=" << defaultLevelIt->second << ':';
159 }
160
161 for (auto it = m_enabledLevel.begin(); it != m_enabledLevel.end(); ++it) {
162 if (it->first == "*") {
163 continue;
164 }
165 os << it->first << '=' << it->second << ':';
166 }
167
168 std::string s = os.str();
169 if (!s.empty()) {
170 s.pop_back(); // delete last ':'
171 }
172 return s;
173}
174#endif // NDN_CXX_HAVE_TESTS
175
176#ifdef NDN_CXX_HAVE_TESTS
177void
178Logging::resetLevels()
179{
180 this->setDefaultLevel(INITIAL_DEFAULT_LEVEL);
181 m_enabledLevel.clear();
182}
183#endif // NDN_CXX_HAVE_TESTS
184
185void
186Logging::setDestination(std::ostream& os)
187{
188 setDestination(shared_ptr<std::ostream>(&os, bind([]{})));
189}
190
191void
192Logging::setDestinationImpl(shared_ptr<std::ostream> os)
193{
194 std::lock_guard<std::mutex> lock(m_mutex);
195
196 m_destination = os;
197
198 auto backend = boost::make_shared<boost::log::sinks::text_ostream_backend>();
199 backend->auto_flush(true);
200 backend->add_stream(boost::shared_ptr<std::ostream>(os.get(), bind([]{})));
201
202 if (m_sink != nullptr) {
203 boost::log::core::get()->remove_sink(m_sink);
204 m_sink->flush();
205 m_sink.reset();
206 }
207
208 m_sink = boost::make_shared<Sink>(backend);
209 m_sink->set_formatter(boost::log::expressions::stream << boost::log::expressions::message);
210 boost::log::core::get()->add_sink(m_sink);
211}
212
213#ifdef NDN_CXX_HAVE_TESTS
214shared_ptr<std::ostream>
215Logging::getDestination()
216{
217 return m_destination;
218}
219#endif // NDN_CXX_HAVE_TESTS
220
221void
222Logging::flushImpl()
223{
224 m_sink->flush();
225}
226
227} // namespace util
228} // namespace ndn