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>
47 #pragma clang diagnostic ignored "-Wundefined-func-template"
59 const auto sinceEpoch = system_clock::now().time_since_epoch();
60 BOOST_ASSERT(sinceEpoch.count() >= 0);
62 const auto usecs =
std::abs(duration_cast<microseconds>(sinceEpoch).count());
63 const auto usecsPerSec = microseconds::period::den;
66 std::string buffer(10 + 1 + 6 + 1,
'\0');
67 BOOST_ASSERT_MSG(usecs / usecsPerSec <= 9999999999,
"whole seconds cannot fit in 10 characters");
69 static_assert(std::is_same<microseconds::rep, int_least64_t>::value,
70 "PRIdLEAST64 is incompatible with microseconds::rep");
72 ::snprintf(&buffer.front(), buffer.size(),
"%" PRIdLEAST64
".%06" PRIdLEAST64,
73 usecs / usecsPerSec, usecs % usecsPerSec);
80 BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp,
"Timestamp", std::string)
91 static Logging instance;
98 bool wantAutoFlush = std::getenv(
"NDN_LOG_NOFLUSH") ==
nullptr;
99 auto destination = makeDefaultStreamDestination(shared_ptr<std::ostream>(&std::clog, [] (
auto&&) {}),
102 auto destination = detail::makeAndroidLogger();
106 this->setDestinationImpl(std::move(destination));
108 const char* env = std::getenv(
"NDN_LOG");
109 if (env !=
nullptr) {
110 this->setLevelImpl(env);
113 boost::log::core::get()->add_global_attribute(
"Timestamp",
114 boost::log::attributes::make_function(&log::makeTimestamp));
118 Logging::addLoggerImpl(Logger& logger)
120 std::lock_guard<std::mutex> lock(m_mutex);
122 const std::string& moduleName = logger.getModuleName();
123 m_loggers.emplace(moduleName, &logger);
125 logger.setLevel(findLevel(moduleName));
129 Logging::registerLoggerNameImpl(std::string name)
131 std::lock_guard<std::mutex> lock(m_mutex);
132 m_loggers.emplace(std::move(name),
nullptr);
135 std::set<std::string>
136 Logging::getLoggerNamesImpl()
const
138 std::lock_guard<std::mutex> lock(m_mutex);
140 std::set<std::string> loggerNames;
141 boost::copy(m_loggers | boost::adaptors::map_keys, std::inserter(loggerNames, loggerNames.end()));
146 Logging::findLevel(std::string mn)
const
148 while (!mn.empty()) {
149 auto it = m_enabledLevel.find(mn);
150 if (it != m_enabledLevel.end()) {
153 size_t pos = mn.find_last_of(
'.');
154 if (pos < mn.size() - 1) {
155 mn = mn.substr(0, pos + 1);
157 else if (pos == mn.size() - 1) {
159 pos = mn.find_last_of(
'.');
160 if (pos != std::string::npos) {
161 mn = mn.substr(0, pos + 1);
172 auto it = m_enabledLevel.find(mn);
173 return it != m_enabledLevel.end() ? it->second : INITIAL_DEFAULT_LEVEL;
176 #ifdef NDN_CXX_HAVE_TESTS
178 Logging::removeLogger(Logger& logger)
180 const std::string& moduleName = logger.getModuleName();
181 auto range = m_loggers.equal_range(moduleName);
182 for (
auto i = range.first; i != range.second; ++i) {
183 if (i->second == &logger) {
193 Logging::setLevelImpl(
const std::string& prefix,
LogLevel level)
195 std::lock_guard<std::mutex> lock(m_mutex);
197 if (prefix.empty() || prefix.back() ==
'*') {
198 std::string p = prefix;
203 for (
auto i = m_enabledLevel.begin(); i != m_enabledLevel.end();) {
204 if (i->first.compare(0, p.size(), p) == 0) {
205 i = m_enabledLevel.erase(i);
211 m_enabledLevel[p] = level;
213 for (
const auto& pair : m_loggers) {
214 if (pair.first.compare(0, p.size(), p) == 0 && pair.second !=
nullptr) {
215 pair.second->setLevel(level);
220 m_enabledLevel[prefix] = level;
221 auto range = boost::make_iterator_range(m_loggers.equal_range(prefix));
222 for (
const auto& pair : range) {
223 if (pair.second !=
nullptr) {
224 pair.second->setLevel(level);
231 Logging::setLevelImpl(
const std::string& config)
233 std::stringstream ss(config);
234 std::string configModule;
235 while (std::getline(ss, configModule,
':')) {
236 size_t ind = configModule.find(
'=');
237 if (ind == std::string::npos) {
238 NDN_THROW(std::invalid_argument(
"malformed logging config: '=' is missing"));
241 std::string moduleName = configModule.substr(0, ind);
243 this->setLevelImpl(moduleName, level);
247 #ifdef NDN_CXX_HAVE_TESTS
249 Logging::resetLevels()
251 this->setLevelImpl(
"*", INITIAL_DEFAULT_LEVEL);
252 m_enabledLevel.clear();
259 auto destination = makeDefaultStreamDestination(shared_ptr<std::ostream>(&os, [] (
auto&&) {}),
261 setDestination(std::move(destination));
264 class TextOstreamBackend :
public boost::log::sinks::text_ostream_backend
267 TextOstreamBackend(std::shared_ptr<std::ostream> os,
bool wantAutoFlush)
268 : m_stdPtr(std::move(os))
270 auto_flush(wantAutoFlush);
271 add_stream(boost::shared_ptr<std::ostream>(m_stdPtr.get(), [] (
auto&&) {}));
277 std::shared_ptr<std::ostream> m_stdPtr;
280 boost::shared_ptr<boost::log::sinks::sink>
283 auto backend = boost::make_shared<TextOstreamBackend>(std::move(os), wantAutoFlush);
284 auto destination = boost::make_shared<boost::log::sinks::asynchronous_sink<TextOstreamBackend>>(backend);
286 namespace expr = boost::log::expressions;
287 destination->set_formatter(expr::stream
288 << expr::attr<std::string>(log::timestamp.get_name())
289 <<
" " << std::setw(5) << expr::attr<LogLevel>(log::severity.get_name()) <<
": "
290 <<
"[" << expr::attr<std::string>(log::module.get_name()) <<
"] "
296 Logging::setDestinationImpl(boost::shared_ptr<boost::log::sinks::sink> destination)
298 std::lock_guard<std::mutex> lock(m_mutex);
300 if (destination == m_destination) {
304 if (m_destination !=
nullptr) {
305 boost::log::core::get()->remove_sink(m_destination);
306 m_destination->flush();
309 m_destination = std::move(destination);
311 if (m_destination !=
nullptr) {
312 boost::log::core::get()->add_sink(m_destination);
316 #ifdef NDN_CXX_HAVE_TESTS
317 boost::shared_ptr<boost::log::sinks::sink>
318 Logging::getDestination()
const
320 return m_destination;
324 Logging::setLevelImpl(
const std::unordered_map<std::string, LogLevel>& prefixRules)
327 for (
const auto& rule : prefixRules) {
328 setLevelImpl(rule.first, rule.second);
332 const std::unordered_map<std::string, LogLevel>&
333 Logging::getLevels()
const
335 return m_enabledLevel;
342 std::lock_guard<std::mutex> lock(m_mutex);
344 if (m_destination !=
nullptr) {
345 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.