如何将 boost::log::expressions::smessage 传递给 nlohmann::json 构造函数?
How to pass boost::log::expressions::smessage to nlohmann::json constructor?
我有一个类似这样的代码:
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_Whosebug_SIMPLELOGGER_H
#define _HOME_SEHE_PROJECTS_Whosebug_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, "%Y-%m-%d, %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);
我有一个类似这样的代码:
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_Whosebug_SIMPLELOGGER_H #define _HOME_SEHE_PROJECTS_Whosebug_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, "%Y-%m-%d, %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);