问题描述
我有一个类似的代码:const auto jsonFormatter = boost::log::expressions::stream << boost::log::expressions::smessage;
我想使用 nlohmann::json
对消息进行转义,例如:nlohmann::json json{boost::log::expressions::smessage};
我可以执行以下操作将 boost::log::expressions::smessage
转换为 std::string
:
std::stringstream ss;
ss << boost::log::expressions::smessage;
std::string message = ss.str();
nlohmann::json json{message};
,但我需要把它放在格式化程序中,因为const auto jsonFormatter = boost::log::expressions::stream << nlohmann::json{boost::log::expressions::smessage};
无法将 boost::log::expressions::smessage
参数转换为任何 nlohmann::json
构造函数。
有什么建议可以让它发挥作用吗?
解决方法
日志格式化程序看起来像普通的 C++,但表达式模板组成了执行相应操作的延迟可调用对象。
以下是如何制作一个知道如何执行此操作的包装器表达式:
namespace {
struct as_json_t {
template <typename E> auto operator[](E fmt) const {
return expr::wrap_formatter(
[fmt](logging::record_view const& rec,logging::formatting_ostream& strm) {
logging::formatting_ostream tmp;
std::string text;
tmp.attach(text);
fmt(rec,tmp);
strm << nlohmann::json{text};
});
}
};
inline constexpr as_json_t as_json;
} // namespace
现在您可以制作格式化程序,例如
logging::formatter formatter = expr::stream
<< expr::format_date_time(timestamp,"%Y-%m-%d,%H:%M:%S.%f") << " "
<< logging::trivial::severity
<< " - " << as_json[expr::stream << expr::smessage]
;
结果是例如
2021-01-15,23:34:08.489173 error - ["this is an error message"]
现场演示
-
文件
simpleLogger.h
#ifndef _HOME_SEHE_PROJECTS_STACKOVERFLOW_SIMPLELOGGER_H #define _HOME_SEHE_PROJECTS_STACKOVERFLOW_SIMPLELOGGER_H #pragma once #define BOOST_LOG_DYN_LINK \ 1 // necessary when linking the boost_log library dynamically #include <boost/log/sources/global_logger_storage.hpp> #include <boost/log/trivial.hpp> // the logs are also written to LOGFILE #define LOGFILE "logfile.log" // just log messages with severity >= SEVERITY_THRESHOLD are written #define SEVERITY_THRESHOLD logging::trivial::warning // register a global logger BOOST_LOG_GLOBAL_LOGGER(logger,boost::log::sources::severity_logger_mt< boost::log::trivial::severity_level>) // just a helper macro used by the macros below - don't use it in your code #define LOG(severity) \ BOOST_LOG_SEV(logger::get(),boost::log::trivial::severity) // ===== log macros ===== #define LOG_TRACE LOG(trace) #define LOG_DEBUG LOG(debug) #define LOG_INFO LOG(info) #define LOG_WARNING LOG(warning) #define LOG_ERROR LOG(error) #define LOG_FATAL LOG(fatal) #endif
-
文件
simpleLogger.cpp
#include "simpleLogger.h" #include <boost/core/null_deleter.hpp> #include <boost/log/core/core.hpp> #include <boost/log/expressions.hpp> #include <boost/log/expressions/formatters/char_decorator.hpp> #include <boost/log/expressions/formatters/date_time.hpp> #include <boost/log/sinks/sync_frontend.hpp> #include <boost/log/sinks/text_ostream_backend.hpp> #include <boost/log/sources/severity_logger.hpp> #include <boost/log/support/date_time.hpp> #include <boost/log/trivial.hpp> #include <boost/log/utility/setup/common_attributes.hpp> #include <nlohmann/json.hpp> #include <fstream> namespace logging = boost::log; namespace src = boost::log::sources; namespace expr = boost::log::expressions; namespace sinks = boost::log::sinks; namespace attrs = boost::log::attributes; BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp,"TimeStamp",boost::posix_time::ptime) BOOST_LOG_ATTRIBUTE_KEYWORD(severity,"Severity",logging::trivial::severity_level) namespace { struct as_json_t { template <typename E> auto operator[](E fmt) const { return expr::wrap_formatter( [fmt](logging::record_view const& rec,logging::formatting_ostream& strm) { logging::formatting_ostream tmp; std::string text; tmp.attach(text); fmt(rec,tmp); strm << nlohmann::json{text}; }); } }; inline constexpr as_json_t as_json; } // namespace BOOST_LOG_GLOBAL_LOGGER_INIT(logger,src::severity_logger_mt) { src::severity_logger_mt<boost::log::trivial::severity_level> logger; // add attributes logger.add_attribute("TimeStamp",attrs::local_clock()); // each log line gets a timestamp // add a text sink using text_sink = sinks::synchronous_sink<sinks::text_ostream_backend>; boost::shared_ptr<text_sink> sink = boost::make_shared<text_sink>(); // add a logfile stream to our sink sink->locked_backend()->add_stream( boost::make_shared<std::ofstream>(LOGFILE)); // add "console" output stream to our sink sink->locked_backend()->add_stream( boost::shared_ptr<std::ostream>(&std::clog,boost::null_deleter())); // specify the format of the log message logging::formatter formatter = expr::stream << expr::format_date_time(timestamp,%H:%M:%S.%f") << " " << logging::trivial::severity << " - " << as_json[expr::stream << expr::smessage] ; sink->set_formatter(formatter); // only messages with severity >= SEVERITY_THRESHOLD are written sink->set_filter(severity >= SEVERITY_THRESHOLD); // "register" our sink logging::core::get()->add_sink(sink); return logger; }
-
文件
test.cpp
#include "simpleLogger.h" int main() { LOG_TRACE << "this is a trace message"; LOG_DEBUG << "this is a debug message"; LOG_WARNING << "this is a warning message"; LOG_ERROR << "this is an error message"; LOG_FATAL << "this is a fatal error message"; return 0; }
印刷品
2021-01-15,23:50:03.130250 warning - ["this is a warning message"]
2021-01-15,23:50:03.130327 error - ["this is an error message"]
2021-01-15,23:50:03.130354 fatal - ["this is a fatal error message"]
,
除了 sehe 的回答之外,您还可以使用 Boost.Log 组件实现类似 JSON 的格式。关键部分是 c_decor
character decorator,它确保其输出可以用作 C 风格的字符串文字。
namespace expr = boost::log::expressions;
const auto jsonFormatter =
expr::stream << "[\""
<< expr::c_decor[ expr::stream << expr::smessage ]
<< "\"]";
首先,c_decor
会将消息中的任何控制字符转义为 C 风格的转义序列,如 \n、\t。它还将转义双引号字符。然后,添加周围的括号和双引号,使输出与 JSON 格式兼容。
如果您的日志消息中有非 ASCII 字符,并且您希望格式化的日志记录为严格的 ASCII 格式,您可以使用 c_ascii_decor
而不是 c_decor
。除了 c_decor
的作用外,它还会将任何大于 127 的字节转义为其十六进制转义序列,例如\x8c。
我真的很感谢帮助和其他答案,这在其他情况下可能会更好,但长话短说,我尝试了很多解决方案并最终采用了这个解决方案(如果可以改进,我可以更新它) :
#include <boost/phoenix/bind/bind_function.hpp>
..
nlohmann::json EscapeMessage(
boost::log::value_ref<std::string,boost::log::expressions::tag::smessage> const& message)
{
return message ? nlohmann::json(message.get()) : nlohmann::json();
}
..
const auto jsonFormatter = boost::log::expressions::stream << boost::phoenix::bind(&EscapeMessage,boost::log::expressions::smessage.or_none())
boost::log::add_console_log(std::cout,boost::log::keywords::format = jsonFormatter);