我有一个广泛使用boost log 2.0的应用程序。现在我想为该应用程序设置一些默认标志,如std::setprecision(std::numeric_limits<double>::digits10 + 1)
,std::scientific
和std::left
。但是我该怎么做?一种方法是在我的main函数的最开始创建一个记录器并创建一个虚拟日志消息。这将永久设置所需的标志。但有没有更好的方法来做到这一点?
修改以回复:&#34; OP应显示实际代码。&#34;
我有一个全局的Logging单例,名为L:
class L{
public:
enum severity_level
{
dddebug,
ddebug,
debug,
control,
iiinfo,
iinfo,
info,
result,
warning,
error,
critical
};
typedef boost::log::sources::severity_channel_logger<
severity_level, // the type of the severity level
std::string // the type of the channel name
> logger_t;
typedef boost::log::sinks::synchronous_sink< boost::log::sinks::text_ostream_backend > text_sink;
boost::shared_ptr< text_sink > sink_;
static L& get();
static boost::shared_ptr<text_sink> sink();
static double t0();
static double tElapsed();
private:
L();
double t0_p;
static std::string tElapsedFormat();
L(const L&) = delete;
void operator=(const L&) = delete;
};
提供日志记录接收器,严重性级别,并利用MPI方法跨MPI节点进行同步计时。类成员的实现如下:
#include "log.h"
#include <iomanip>
#include <limits>
#include <fstream>
#include <boost/log/attributes/function.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared_object.hpp>
#include <boost/log/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace expr = boost::log::expressions;
namespace sinks = boost::log::sinks;
namespace attrs = boost::log::attributes;
namespace keywords = boost::log::keywords;
#include "mpiwrap.h"
#include <mpi.h>
BOOST_LOG_ATTRIBUTE_KEYWORD(t, "Time", std::string)
BOOST_LOG_ATTRIBUTE_KEYWORD(rank, "Rank", int)
BOOST_LOG_ATTRIBUTE_KEYWORD(channel, "Channel", std::string)
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", L::severity_level)
L::L():
sink_(boost::make_shared< text_sink >()),
t0_p(MPI_Wtime())
{
sink_->locked_backend()->add_stream(
boost::make_shared< std::ofstream >("log." + std::to_string(MpiWrap::getRank())));
sink_->set_formatter
(
expr::stream
<< "< "
<< t << " "
<< "[p:" << rank << "] "
<< "[c:" << channel << "] "
<< "[s:" << severity << "] "
<< expr::smessage
);
logging::core::get()->add_sink(sink_);
logging::core::get()->set_filter(
(channel == "ChannelName1" && severity >= dddebug)
|| (channel == "ChannelName2" && severity >= info)
|| (channel == "ChannelName3" && severity >= result)
);
// Add attributes
logging::core::get()->add_global_attribute("Time", attrs::make_function(&tElapsedFormat));
logging::core::get()->add_global_attribute("Rank", attrs::constant<int>(MpiWrap::getRank()));
}
L& L::get(){
static L instance;
return instance;
}
boost::shared_ptr<L::text_sink> L::sink(){
return get().sink_;
}
double L::t0(){
return get().t0_p;
}
double L::tElapsed(){
return MPI_Wtime() - t0();
}
std::string L::tElapsedFormat(){
std::stringstream ss;
const int prec = std::numeric_limits<double>::digits10;
ss << std::setw(prec + 2 + 6) << std::left << std::setprecision(prec) << tElapsed();
return ss.str();
}
std::ostream& operator<< (std::ostream& strm, L::severity_level level)
{
static const char* strings[] =
{
"DBG3",
"DBG2",
"DBG1",
"CTRL",
"INF3",
"INF2",
"INF1",
"RSLT",
"WARN",
"ERRR",
"CRIT"
};
if (static_cast< std::size_t >(level) < sizeof(strings) / sizeof(*strings))
strm << strings[level];
else
strm << static_cast< int >(level);
return strm;
}
现在用于:我的类通常有一个静态logger_t
(boost::log::sources::severity_channel_logger<severity_level, std::string>
的成员定义)成员
class A {
public:
logger_t logger;
//other stuff here
void function_which_does_logging();
};
L::logger_t A::logger(boost::log::keywords::channel = "ClassA");
void A::function_which_does_logging(){
//do non logging related stuff
BOOST_LOG_SEV(logger, L::result) << "the error is: " << 0.1234567890;
//do non logging related stuff
}
我目前解决问题的方法是在我的程序开头添加一个日志语句
int main(){
L::logger_t logger(boost::log::keywords::channel = "init");
BOOST_LOG_SEV(logger, L::critical) << "setting up logger" << std::scientific << std::setprecision(std::numeric_limits<double>::digits10 + 1);
//do stuff
}
答案 0 :(得分:5)
@rhashimoto对当前解决方案如何通过多线程/并发日志记录操作进行分解提出了一个很好的观点。我觉得最好的解决方案是定义自己的日志宏来替换包含流修饰符的BOOST_LOG_SEV
,如下所示:
#define LOG_SCIENTIFIC(logger, sev) (BOOST_LOG_SEV(logger, sev) << std::scientific)
这可以仅用作BOOST_LOG_SEV
的替代品,将数字格式化为科学。但是,通过代码并使用新的自定义宏替换每个日志记录操作可能会很痛苦。您也可以重新定义BOOST_LOG_SEV
以表达自己想要的效果,而不是定义自己的宏。 boost/log/sources/severity_feature.hpp
定义BOOST_LOG_SEV
如下:
//! An equivalent to BOOST_LOG_STREAM_SEV(logger, lvl)
#define BOOST_LOG_SEV(logger, lvl) BOOST_LOG_STREAM_SEV(logger, lvl)
由于BOOST_LOG_STREAM_SEV
仍然是公共提升API的一部分,因此您应该能够安全地重新定义BOOST_LOG_SEV
,如下所示:
#define BOOST_LOG_SEV(logger, lvl) (BOOST_LOG_STREAM_SEV(logger, lvl) << std::scientific)
只要在包含boost日志标题后定义了它,它就应该按照您的意愿执行。但是,我建议使用带有自定义名称的宏,以便其他人清楚您的代码正在做什么。
答案 1 :(得分:4)
我不相信您当前的方法适用于所有情况,特别是如果您的代码是有线程的。当你说一个记录的格式标志用于修复多个记录器时,我很紧张,所以我查看了代码(record_ostream.hpp和record_ostream.cpp)。
Boost Log使用Object pool design pattern为带有辅助结构stream_provider
的记录器提供流格式化程序。 stream_provider
的实现使用线程本地存储(当支持线程时)为每个线程使用单独的ostream
实例池。在池中,根据需要创建ostream
个实例 - 如果一次只需要一个格式化程序,则只会创建一个格式化程序。因此,如果您从单个线程和进行日志记录,如果您从未在记录其他内容时记录某些内容,那么我认为您当前的解决方法将适用于当前的Boost Log实现。
线程失败的方法应该非常明显。以下是一个简单的示例,说明如何在单个线程中失败:
static double f(double x) {
BOOST_LOG(my_logger::get()) << "called f with " << x;
return x;
}
int main() {
BOOST_LOG(my_logger::get()) << std::scientific << "format flag";
BOOST_LOG(my_logger::get()) << "top level " << f(0.01);
return 0;
}
产生:
[2014-04-27 14:16:39.832008] [0x000007fff7a1c631] [info] format flag
[2014-04-27 14:16:39.832616] [0x000007fff7a1c631] [info] called f with 0.01
[2014-04-27 14:16:39.832630] [0x000007fff7a1c631] [info] top level 1.000000e-02
请注意,顶级日志条目(在第三行)格式正确,而功能日志条目(在第二行)不是。这是因为调用函数时配置的流正在使用,因此创建并使用了单独的流。
我认为@ WilliamKunkel建议定义自己的日志记录宏是我见过的最佳方法。但是,如果你真的想使用Boost宏,那么这个hack在Boost 1.55上对我有用:
#include <iomanip>
#define BOOST_LOG_DYN_LINK
#include <boost/log/sources/global_logger_storage.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
typedef boost::log::sources::logger logger_t;
BOOST_LOG_INLINE_GLOBAL_LOGGER_DEFAULT(my_logger, logger_t)
namespace boost {
namespace log {
namespace aux {
template<>
BOOST_FORCEINLINE record_pump<logger_t> make_record_pump(logger_t& lg, record& rec)
{
auto rp = record_pump<logger_t>(lg, rec);
rp.stream() << std::scientific;
return rp;
}
}
}
}
int main() {
BOOST_LOG(my_logger::get()) << "Logging number " << 0.01;
return 0;
}
基本思想是专门化将流提供给宏的模板函数。当它专门用于您正在使用的实际记录器类时,传递std::scientific
标志的专用实现将优先于通用实现。
这是一个hack,因为它取决于Boost Log的实现细节,并且不保证在发行版之间有效。在我看来,定义自己的宏似乎是一种更好的方法。
我原本希望可以使用boost::log::basic_formatting_stream
完成某些操作,因为它的标题是:
虽然
basic_formatting_ostream
不是std::basic_ostream
派生的,但用户无需添加operator<<
的特殊重载,因为流将默认重用std::basic_ostream
的运算符。 但是,如果某种类型需要,可以为operator<<
定义basic_formatting_ostream
的特殊重载 输出到日志时的特殊格式。
不幸的是,它看起来只适用于类类型而不是基本类型,因为operator<<
的成员函数实现适用于所有基本类型。