diff --git a/example/example.cpp b/example/example.cpp index f723018d..a8c6e3f2 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -394,15 +394,33 @@ void replace_default_logger_example() } void attribute_example() { - spdlog::default_logger_raw()->warn("EXPERIMENTAL: log with attributes", {{"attribute_key", "attribute value"}}); - - // logfmt structured logging using attributes - - // auto logfmt_logger = spdlog::basic_logger_mt("logfmt_logger", "logs/mylog.txt"); - auto logfmt_logger = spdlog::stdout_color_mt("logfmt_logger"); - - std::string logfmt_pattern = "time=%Y-%m-%dT%H:%M:%S.%f%z name=%n level=%^%l%$ process=%P thread=%t message=\"%v\"%( %K=\"%V\"%)"; - logfmt_logger->set_pattern(logfmt_pattern); - - logfmt_logger->info("logfmt structured logging", spdlog::attribute_list{{"key\n1", "value\n1"}, {"key\r\n2", "value\r\n2"}}); + auto custom_logger = spdlog::stdout_color_mt("custom_logger"); + + custom_logger->push_context(spdlog::attribute_list{{"attribute_key", "attribute value"}}); + custom_logger->warn("EXPERIMENTAL: log with attributes"); + custom_logger->clear_context(); + + // structured logging using attributes + + // auto s_logger = spdlog::basic_logger_mt("structured logger", "logs/mylog.txt"); + auto s_logger = spdlog::stdout_color_mt("structured logger"); + + #if 0 + std::string json_pattern = "{\"time\":\"%Y-%m-%dT%H:%M:%S.%e%z\",\"name\":\"%n\",\"level\":\"%l\",\"message\":\"%v\"%(,\"%K\":\"%V\"%)}"; + s_logger->set_pattern(std::move(json_pattern)); + #endif + #if 1 + std::string logfmt_pattern = "time=%Y-%m-%dT%H:%M:%S.%f%z name=\"%n\" level=%^%l%$ process=%P thread=%t message=\"%v\"%( %K=\"%V\"%)"; + s_logger->set_pattern(std::move(logfmt_pattern)); + #endif + + s_logger->push_context(spdlog::attribute_list{{"key\n1", "value\n1"}}); + s_logger->info("structured logging: test 1"); + s_logger->push_context(spdlog::attribute_list{{"key\n2", "value\n2"}}); + s_logger->info("structured logging: test 2"); + s_logger->pop_context(); + s_logger->info("structured logging: test 3"); + s_logger->pop_context(); + s_logger->info("structured logging: test 4"); + s_logger->clear_context(); } \ No newline at end of file diff --git a/include/spdlog/logger.h b/include/spdlog/logger.h index 6160cef8..0697b072 100644 --- a/include/spdlog/logger.h +++ b/include/spdlog/logger.h @@ -77,6 +77,7 @@ public: : logger(std::move(name), sinks.begin(), sinks.end()) {} + virtual ~logger() = default; logger(const logger &other); @@ -84,10 +85,25 @@ public: logger &operator=(logger other) SPDLOG_NOEXCEPT; void swap(spdlog::logger &other) SPDLOG_NOEXCEPT; - template - void log(level::level_enum lvl, const T &msg, attribute_list attrs) - { - log(source_loc{}, lvl, msg, attrs); + void push_context(attribute_list attrs) { + attributes.insert(attributes.end(), attrs.begin(), attrs.end()); + attr_stack.push_back(static_cast(attributes.size())); + } + + void pop_context() { + if (attr_stack.size() > 1) { + attributes.erase(attributes.begin() + attr_stack[attr_stack.size() - 2], attributes.begin() + attr_stack[attr_stack.size() - 1]); + attr_stack.pop_back(); + } else { + // is first element in stack, or empty. Delete the whole thing. + clear_context(); + } + } + + // removes context on loggers by clearing the attribute list + void clear_context() { + attributes.clear(); + attr_stack.clear(); } template @@ -125,23 +141,10 @@ public: } details::log_msg log_msg(log_time, loc, name_, lvl, msg); + log_msg.attributes.insert(log_msg.attributes.end(), attributes.begin(), attributes.end()); log_it_(log_msg, log_enabled, traceback_enabled); } - void log(source_loc loc, level::level_enum lvl, string_view_t msg, attribute_list attrs) - { - bool log_enabled = should_log(lvl); - bool traceback_enabled = tracer_.enabled(); - if (!log_enabled && !traceback_enabled) - { - return; - } - - details::log_msg log_msg(loc, name_, lvl, msg); - // log_msg.attributes.push_back(attrs); - log_msg.attributes.insert(log_msg.attributes.end(), attrs.begin(), attrs.end()); - log_it_(log_msg, log_enabled, traceback_enabled); - } void log(source_loc loc, level::level_enum lvl, string_view_t msg) { bool log_enabled = should_log(lvl); @@ -152,6 +155,7 @@ public: } details::log_msg log_msg(loc, name_, lvl, msg); + log_msg.attributes.insert(log_msg.attributes.end(), attributes.begin(), attributes.end()); log_it_(log_msg, log_enabled, traceback_enabled); } @@ -221,6 +225,7 @@ public: memory_buf_t buf; details::os::wstr_to_utf8buf(wstring_view_t(msg.data(), msg.size()), buf); details::log_msg log_msg(log_time, loc, name_, lvl, string_view_t(buf.data(), buf.size())); + log_msg.attributes.insert(log_msg.attributes.end(), attributes.begin(), attributes.end()); log_it_(log_msg, log_enabled, traceback_enabled); } @@ -236,6 +241,7 @@ public: memory_buf_t buf; details::os::wstr_to_utf8buf(wstring_view_t(msg.data(), msg.size()), buf); details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size())); + log_msg.attributes.insert(log_msg.attributes.end(), attributes.begin(), attributes.end()); log_it_(log_msg, log_enabled, traceback_enabled); } @@ -281,43 +287,6 @@ public: } #endif - // attributes endpoint - template - void trace(const T &msg, attribute_list attrs) - { - log(level::trace, msg, attrs); - } - - template - void debug(const T &msg, attribute_list attrs) - { - log(level::debug, msg, attrs); - } - - template - void info(const T &msg, attribute_list attrs) - { - log(level::info, msg, attrs); - } - - template - void warn(const T &msg, attribute_list attrs) - { - log(level::warn, msg, attrs); - } - - template - void error(const T &msg, attribute_list attrs) - { - log(level::err, msg, attrs); - } - - template - void critical(const T &msg, attribute_list attrs) - { - log(level::critical, msg, attrs); - } - // default endpoint template void trace(const T &msg) @@ -413,6 +382,9 @@ protected: err_handler custom_err_handler_{nullptr}; details::backtracer tracer_; + attribute_list attributes; + std::vector attr_stack; // used to push/pop nested contexts + // common implementation for after templated public api has been resolved template void log_(source_loc loc, level::level_enum lvl, string_view_t fmt, Args &&... args) @@ -433,6 +405,7 @@ protected: #endif details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size())); + log_msg.attributes.insert(log_msg.attributes.end(), attributes.begin(), attributes.end()); log_it_(log_msg, log_enabled, traceback_enabled); } SPDLOG_LOGGER_CATCH(loc) @@ -458,7 +431,8 @@ protected: memory_buf_t buf; details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf); details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size())); - log_it_(log_msg, log_enabled, traceback_enabled); + log_msg.attributes.insert(log_msg.attributes.end(), attributes.begin(), attributes.end()); + log_og_msg, log_enabled, traceback_enabled); } SPDLOG_LOGGER_CATCH(loc) } diff --git a/include/spdlog/pattern_formatter-inl.h b/include/spdlog/pattern_formatter-inl.h index ec881110..e9b0c92e 100644 --- a/include/spdlog/pattern_formatter-inl.h +++ b/include/spdlog/pattern_formatter-inl.h @@ -1145,8 +1145,8 @@ SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory f->format(msg, cached_tm_, dest); } } else { - // ugly formatter iterator, due to needing to go back to previous iterators to repeat kv pairs auto it_end = formatters_.begin(); + // ugly formatter iterator, due to needing to go back to previous iterators to repeat kv pairs for (auto it = formatters_.begin(); it != formatters_.end(); ++it) { if ((*it)->flag_ == details::attr_flags::start) {