27 #include "ndn-cxx/util/impl/logger-android.hpp"
30 #include <boost/log/attributes/function.hpp>
31 #include <boost/log/expressions.hpp>
32 #include <boost/log/expressions/attr.hpp>
33 #include <boost/log/expressions/formatters/date_time.hpp>
34 #include <boost/log/support/date_time.hpp>
35 #include <boost/range/adaptor/map.hpp>
36 #include <boost/range/algorithm/copy.hpp>
37 #include <boost/range/iterator_range.hpp>
54 const auto sinceEpoch = system_clock::now().time_since_epoch();
55 BOOST_ASSERT(sinceEpoch.count() >= 0);
57 const auto usecs =
std::abs(duration_cast<microseconds>(sinceEpoch).count());
58 const auto usecsPerSec = microseconds::period::den;
61 std::string buffer(10 + 1 + 6 + 1,
'\0');
62 BOOST_ASSERT_MSG(usecs / usecsPerSec <= 9999999999,
"whole seconds cannot fit in 10 characters");
64 static_assert(std::is_same<microseconds::rep, int_least64_t>::value,
65 "PRIdLEAST64 is incompatible with microseconds::rep");
66 std::snprintf(&buffer.front(), buffer.size(),
"%" PRIdLEAST64
".%06" PRIdLEAST64,
67 usecs / usecsPerSec, usecs % usecsPerSec);
74 BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp,
"Timestamp", std::string)
85 static Logging instance;
92 bool wantAutoFlush = std::getenv(
"NDN_LOG_NOFLUSH") ==
nullptr;
93 auto destination = makeDefaultStreamDestination(shared_ptr<std::ostream>(&std::clog, [] (
auto&&) {}),
96 auto destination = detail::makeAndroidLogger();
100 this->setDestinationImpl(std::move(destination));
102 const char* env = std::getenv(
"NDN_LOG");
103 if (env !=
nullptr) {
104 this->setLevelImpl(env);
107 boost::log::core::get()->add_global_attribute(
"Timestamp",
108 boost::log::attributes::make_function(&log::makeTimestamp));
112 Logging::addLoggerImpl(Logger& logger)
114 std::lock_guard<std::mutex> lock(m_mutex);
116 const std::string& moduleName = logger.getModuleName();
117 m_loggers.emplace(moduleName, &logger);
119 logger.setLevel(findLevel(moduleName));
123 Logging::registerLoggerNameImpl(std::string name)
125 std::lock_guard<std::mutex> lock(m_mutex);
126 m_loggers.emplace(std::move(name),
nullptr);
129 std::set<std::string>
130 Logging::getLoggerNamesImpl()
const
132 std::lock_guard<std::mutex> lock(m_mutex);
134 std::set<std::string> loggerNames;
135 boost::copy(m_loggers | boost::adaptors::map_keys, std::inserter(loggerNames, loggerNames.end()));
140 Logging::findLevel(std::string mn)
const
142 while (!mn.empty()) {
143 auto it = m_enabledLevel.find(mn);
144 if (it != m_enabledLevel.end()) {
147 size_t pos = mn.find_last_of(
'.');
148 if (pos < mn.size() - 1) {
149 mn = mn.substr(0, pos + 1);
151 else if (pos == mn.size() - 1) {
153 pos = mn.find_last_of(
'.');
154 if (pos != std::string::npos) {
155 mn = mn.substr(0, pos + 1);
166 auto it = m_enabledLevel.find(mn);
167 return it != m_enabledLevel.end() ? it->second : INITIAL_DEFAULT_LEVEL;
170 #ifdef NDN_CXX_HAVE_TESTS
172 Logging::removeLogger(Logger& logger)
174 const std::string& moduleName = logger.getModuleName();
175 auto range = m_loggers.equal_range(moduleName);
176 for (
auto i = range.first; i != range.second; ++i) {
177 if (i->second == &logger) {
187 Logging::setLevelImpl(
const std::string& prefix,
LogLevel level)
189 std::lock_guard<std::mutex> lock(m_mutex);
191 if (prefix.empty() || prefix.back() ==
'*') {
192 std::string p = prefix;
197 for (
auto i = m_enabledLevel.begin(); i != m_enabledLevel.end();) {
198 if (i->first.compare(0, p.size(), p) == 0) {
199 i = m_enabledLevel.erase(i);
205 m_enabledLevel[p] = level;
207 for (
const auto& pair : m_loggers) {
208 if (pair.first.compare(0, p.size(), p) == 0 && pair.second !=
nullptr) {
209 pair.second->setLevel(level);
214 m_enabledLevel[prefix] = level;
215 auto range = boost::make_iterator_range(m_loggers.equal_range(prefix));
216 for (
const auto& pair : range) {
217 if (pair.second !=
nullptr) {
218 pair.second->setLevel(level);
225 Logging::setLevelImpl(
const std::string& config)
227 std::stringstream ss(config);
228 std::string configModule;
229 while (std::getline(ss, configModule,
':')) {
230 size_t ind = configModule.find(
'=');
231 if (ind == std::string::npos) {
232 NDN_THROW(std::invalid_argument(
"malformed logging config: '=' is missing"));
235 std::string moduleName = configModule.substr(0, ind);
237 this->setLevelImpl(moduleName, level);
241 #ifdef NDN_CXX_HAVE_TESTS
243 Logging::resetLevels()
245 this->setLevelImpl(
"*", INITIAL_DEFAULT_LEVEL);
246 m_enabledLevel.clear();
253 auto destination = makeDefaultStreamDestination(shared_ptr<std::ostream>(&os, [] (
auto&&) {}),
255 setDestination(std::move(destination));
258 class TextOstreamBackend :
public boost::log::sinks::text_ostream_backend
261 TextOstreamBackend(std::shared_ptr<std::ostream> os,
bool wantAutoFlush)
262 : m_stdPtr(std::move(os))
264 auto_flush(wantAutoFlush);
265 add_stream(boost::shared_ptr<std::ostream>(m_stdPtr.get(), [] (
auto&&) {}));
271 std::shared_ptr<std::ostream> m_stdPtr;
274 boost::shared_ptr<boost::log::sinks::sink>
277 auto backend = boost::make_shared<TextOstreamBackend>(std::move(os), wantAutoFlush);
278 auto destination = boost::make_shared<boost::log::sinks::asynchronous_sink<TextOstreamBackend>>(backend);
280 namespace expr = boost::log::expressions;
281 destination->set_formatter(expr::stream
282 << expr::attr<std::string>(log::timestamp.get_name())
283 <<
" " << std::setw(5) << expr::attr<LogLevel>(log::severity.get_name()) <<
": "
284 <<
"[" << expr::attr<std::string>(log::module.get_name()) <<
"] "
290 Logging::setDestinationImpl(boost::shared_ptr<boost::log::sinks::sink> destination)
292 std::lock_guard<std::mutex> lock(m_mutex);
294 if (destination == m_destination) {
298 if (m_destination !=
nullptr) {
299 boost::log::core::get()->remove_sink(m_destination);
300 m_destination->flush();
303 m_destination = std::move(destination);
305 if (m_destination !=
nullptr) {
306 boost::log::core::get()->add_sink(m_destination);
310 #ifdef NDN_CXX_HAVE_TESTS
311 boost::shared_ptr<boost::log::sinks::sink>
312 Logging::getDestination()
const
314 return m_destination;
318 Logging::setLevelImpl(
const std::unordered_map<std::string, LogLevel>& prefixRules)
321 for (
const auto& rule : prefixRules) {
322 setLevelImpl(rule.first, rule.second);
326 const std::unordered_map<std::string, LogLevel>&
327 Logging::getLevels()
const
329 return m_enabledLevel;
336 std::lock_guard<std::mutex> lock(m_mutex);
338 if (m_destination !=
nullptr) {
339 m_destination->flush();
static void setDestination(boost::shared_ptr< boost::log::sinks::sink > destination)
Set or replace log destination.
static boost::shared_ptr< boost::log::sinks::sink > makeDefaultStreamDestination(shared_ptr< std::ostream > os, bool wantAutoFlush=true)
Create stream log destination using default formatting.
constexpr duration< Rep, Period > abs(duration< Rep, Period > d)
LogLevel parseLogLevel(const std::string &s)
Parse LogLevel from a string.
LogLevel
Indicates the severity level of a log message.