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