26 #include <boost/log/attributes/function.hpp> 27 #include <boost/log/expressions.hpp> 28 #include <boost/log/expressions/attr.hpp> 29 #include <boost/log/expressions/formatters/date_time.hpp> 30 #include <boost/log/support/date_time.hpp> 31 #include <boost/range/adaptor/map.hpp> 32 #include <boost/range/algorithm/copy.hpp> 33 #include <boost/range/iterator_range.hpp> 43 #pragma clang diagnostic ignored "-Wundefined-func-template" 55 const auto sinceEpoch = system_clock::now().time_since_epoch();
56 BOOST_ASSERT(sinceEpoch.count() >= 0);
58 const auto usecs =
std::abs(duration_cast<microseconds>(sinceEpoch).count());
59 const auto usecsPerSec = microseconds::period::den;
62 std::string buffer(10 + 1 + 6 + 1,
'\0');
63 BOOST_ASSERT_MSG(usecs / usecsPerSec <= 9999999999,
"whole seconds cannot fit in 10 characters");
65 static_assert(std::is_same<microseconds::rep, int_least64_t>::value,
66 "PRIdLEAST64 is incompatible with microseconds::rep");
68 ::snprintf(&buffer.front(), buffer.size(),
"%" PRIdLEAST64
".%06" PRIdLEAST64,
69 usecs / usecsPerSec, usecs % usecsPerSec);
76 BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp,
"Timestamp", std::string)
93 bool wantAutoFlush =
true;
94 const char* environ = std::getenv(
"NDN_LOG_NOFLUSH");
95 if (environ !=
nullptr) {
96 wantAutoFlush =
false;
100 auto destination = makeDefaultStreamDestination(shared_ptr<std::ostream>(&std::clog, [] (
auto) {}), wantAutoFlush);
101 this->setDestinationImpl(std::move(destination));
103 environ = std::getenv(
"NDN_LOG");
104 if (environ !=
nullptr) {
105 this->setLevelImpl(environ);
108 boost::log::core::get()->add_global_attribute(
"Timestamp", boost::log::attributes::make_function(&
log::makeTimestamp));
112 Logging::addLoggerImpl(
Logger& logger)
114 std::lock_guard<std::mutex> lock(m_mutex);
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);
170 #ifdef NDN_CXX_HAVE_TESTS 172 Logging::removeLogger(
Logger& logger)
175 auto range = m_loggers.equal_range(moduleName);
176 for (
auto i = range.first; i != range.second; ++i) {
177 if (i->second == &logger) {
184 #endif // NDN_CXX_HAVE_TESTS 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();
248 #endif // NDN_CXX_HAVE_TESTS 253 auto destination = makeDefaultStreamDestination(shared_ptr<std::ostream>(&os, [] (
auto) {}), wantAutoFlush);
254 setDestination(std::move(destination));
257 class TextOstreamBackend :
public boost::log::sinks::text_ostream_backend
260 TextOstreamBackend(std::shared_ptr<std::ostream> os,
bool wantAutoFlush)
261 : m_stdPtr(std::move(os))
263 auto_flush(wantAutoFlush);
264 add_stream(boost::shared_ptr<std::ostream>(m_stdPtr.get(), [] (
auto) {}));
270 std::shared_ptr<std::ostream> m_stdPtr;
273 boost::shared_ptr<boost::log::sinks::sink>
276 auto backend = boost::make_shared<TextOstreamBackend>(std::move(os), wantAutoFlush);
277 auto destination = boost::make_shared<boost::log::sinks::asynchronous_sink<TextOstreamBackend>>(backend);
279 namespace expr = boost::log::expressions;
280 destination->set_formatter(expr::stream
281 << expr::attr<std::string>(log::timestamp.get_name())
282 <<
" " << std::setw(5) << expr::attr<LogLevel>(log::severity.get_name()) <<
": " 283 <<
"[" << expr::attr<std::string>(log::module.get_name()) <<
"] " 289 Logging::setDestinationImpl(boost::shared_ptr<boost::log::sinks::sink> destination)
291 std::lock_guard<std::mutex> lock(m_mutex);
293 if (destination == m_destination) {
297 if (m_destination !=
nullptr) {
298 boost::log::core::get()->remove_sink(m_destination);
299 m_destination->flush();
302 m_destination = std::move(destination);
304 if (m_destination !=
nullptr) {
305 boost::log::core::get()->add_sink(m_destination);
309 #ifdef NDN_CXX_HAVE_TESTS 310 boost::shared_ptr<boost::log::sinks::sink>
311 Logging::getDestination()
const 313 return m_destination;
317 Logging::setLevelImpl(
const std::unordered_map<std::string, LogLevel>& prefixRules)
320 for (
const auto& rule : prefixRules) {
321 setLevelImpl(rule.first, rule.second);
325 const std::unordered_map<std::string, LogLevel>&
326 Logging::getLevels()
const 328 return m_enabledLevel;
330 #endif // NDN_CXX_HAVE_TESTS 335 std::lock_guard<std::mutex> lock(m_mutex);
337 if (m_destination !=
nullptr) {
338 m_destination->flush();
Controls the logging facility.
constexpr duration< Rep, Period > abs(duration< Rep, Period > d)
LogLevel
Indicates the severity level of a log message.
static std::string makeTimestamp()
LogLevel parseLogLevel(const std::string &s)
Parse LogLevel from a string.
static const LogLevel INITIAL_DEFAULT_LEVEL
static boost::shared_ptr< boost::log::sinks::sink > makeDefaultStreamDestination(shared_ptr< std::ostream > os, bool wantAutoFlush=true)
Create stream log destination using default formatting.
const std::string & getModuleName() const
Represents a log module in the logging facility.
static void setDestination(boost::shared_ptr< boost::log::sinks::sink > destination)
Set or replace log destination.
void setLevel(LogLevel level)