Вы можете определить «ленивого актера», которого вы можете поместить в упакованное выражение форматера. Это в значительной степени ракетостроение Boost Log, но, возможно, этот простой пример поможет вам:
auto reverse_expr = [](auto fmt) {
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);
std::reverse(text.begin(), text.end());
strm << text;
});
};
// specify the format of the log message
logging::formatter formatter = expr::stream
<< reverse_expr(expr::stream << std::setw(7) << std::setfill('0') << line_id)
<< " | "
<< expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
<< "[" << logging::trivial::severity << "]"
<< " - "
<< reverse_expr(expr::stream << expr::smessage);
sink->set_formatter(formatter);
Результаты:
3000000 | 2020-04-28, 16:15:15.779204 [warning] - egassem gninraw a si siht
4000000 | 2020-04-28, 16:15:15.779308 [error] - egassem rorre na si siht
5000000 | 2020-04-28, 16:15:15.779324 [fatal] - egassem rorre lataf a si siht
Примечания: это не будет чрезмерно эффективен, потому что включает в себя «двойную буферизацию» с временным потоком, но он очень гибок, как вы можете видеть.
ОБНОВЛЕНИЕ Улучшенный метод ниже (см. раздел БОНУС )
Несколько более обобщенная c реализация может выглядеть следующим образом:
template <typename F> struct Xfrm {
Xfrm(F f) : _f(f) {}
F _f;
template <typename E> auto operator[](E fmt) const {
return expr::wrap_formatter(
[f=_f,fmt](logging::record_view const& rec, logging::formatting_ostream& strm) {
logging::formatting_ostream tmp;
std::string text;
tmp.attach(text);
fmt(rec, tmp);
strm << f(text);
});
}
};
Таким образом, вы можете использовать ее для других функций:
std::string reversed(std::string v) {
std::reverse(v.begin(), v.end());
return v;
}
std::string to_uppercase(std::string v) {
for (auto& ch : v) ch = std::toupper(ch);
return v;
}
Который вы могли бы затем использовать как:
logging::formatter formatter = expr::stream
<< Xfrm(reversed)[expr::stream << std::setw(7) << std::setfill('0') << line_id]
<< " | "
<< expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
<< "[" << logging::trivial::severity << "]"
<< " - "
<< Xfrm(to_uppercase)[ expr::stream << expr::smessage ];
Или поместите манипуляторы в ваш заголовочный файл:
static inline constexpr Xfrm UC{to_uppercase};
static inline constexpr Xfrm REV{reversed};
Так что вы можете использовать его заранее:
logging::formatter formatter = expr::stream
<< REV[ expr::stream << std::setw(7) << std::setfill('0') << line_id ]
<< " | "
<< expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
<< "[" << logging::trivial::severity << "]"
<< " - "
<< UC[ expr::stream << expr::smessage ];
Вывод
3000000 | 2020-04-28, 16:36:46.184958 [warning] - THIS IS A WARNING MESSAGE
4000000 | 2020-04-28, 16:36:46.185034 [error] - THIS IS AN ERROR MESSAGE
5000000 | 2020-04-28, 16:36:46.185045 [fatal] - THIS IS A FATAL ERROR MESSAGE
Демонстрация в реальном времени
Live On Wandbox
Файл 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 <boost/make_shared.hpp>
#include <boost/phoenix.hpp>
#include <boost/phoenix/function.hpp>
#include <boost/shared_ptr.hpp>
#include <fstream>
#include <ostream>
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(line_id, "LineID", unsigned int)
BOOST_LOG_ATTRIBUTE_KEYWORD(timestamp, "TimeStamp", boost::posix_time::ptime)
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity",
logging::trivial::severity_level)
namespace /*extend locally*/ {
template <typename F> struct Xfrm {
constexpr Xfrm(F f) : _f(f) {}
F _f;
template <typename E> auto operator[](E fmt) const {
return expr::wrap_formatter(
[f = _f, fmt](logging::record_view const& rec,
logging::formatting_ostream& strm) {
logging::formatting_ostream tmp;
std::string text;
tmp.attach(text);
fmt(rec, tmp);
strm << f(text);
});
}
};
std::string reversed(std::string v) {
std::reverse(v.begin(), v.end());
return v;
}
std::string to_uppercase(std::string v) {
for (auto& ch : v) {
ch = std::toupper(ch);
}
return v;
}
inline constexpr Xfrm UC{ to_uppercase };
inline constexpr Xfrm REV{ reversed };
} // 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("LineID", attrs::counter<unsigned int>(
1)); // lines are sequentially numbered
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
<< REV[expr::stream << std::setw(7) << std::setfill('0') << line_id]
<< " | "
<< expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
<< "[" << logging::trivial::severity << "]"
<< " - " << UC[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;
}
БОНУС: более эффективный
Во избежание с двойной буферизацией, мы можем избежать использования оригинального форматера. Это лучше всего работает для известных атрибутов:
struct OddEvenId {
void operator()(logging::record_view const& rec, logging::formatting_ostream& strm) const {
auto q = rec.attribute_values()[logging::aux::default_attribute_names::line_id()];
auto vr = q.extract<uint32_t>();
if (!vr.empty()) {
strm << std::setw(4) << (vr.get<uint32_t>()%2? "Odd":"Even");
}
}
};
struct QuotedMessage {
void operator()(logging::record_view const& rec, logging::formatting_ostream& strm) const {
auto q = rec.attribute_values()[logging::aux::default_attribute_names::message()];
auto vr = q.extract<std::string>();
if (!vr.empty())
strm << std::quoted(vr.get<std::string>());
}
};
static inline auto oddeven_id = expr::wrap_formatter(OddEvenId{});
static inline auto qmessage = expr::wrap_formatter(QuotedMessage{});
Теперь мы можем просто сказать qmessage
вместо expr::smessage
, чтобы получить значение цитируемого сообщения:
Live On Wandbox
logging::formatter formatter = expr::stream
<< oddeven_id
<< " | "
<< expr::format_date_time(timestamp, "%Y-%m-%d, %H:%M:%S.%f") << " "
<< "[" << logging::trivial::severity << "]"
<< " - " << qmessage;
Отпечатки
Odd | 2020-04-28, 17:21:12.619565 [warning] - "this is a warning message"
Even | 2020-04-28, 17:21:12.619666 [error] - "this is an error message"
Odd | 2020-04-28, 17:21:12.619684 [fatal] - "this is a fatal error message"