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