我可以为此使用 LineID 属性吗?我希望我可以使用 sink::set_formatter 来代替使用
__LINE__
和
__FILE__
在每个日志语句中。
我为此苦苦挣扎,直到找到这个片段
#define LFC1_LOG_TRACE(logger) \
BOOST_LOG_SEV(logger, trivial::trace) << "(" << __FILE__ << ", " << __LINE__ << ") "
奇迹般有效
LineID 属性是一个序列号,每条日志消息都会递增。所以你不能使用它。
您可以使用属性来记录行号等。这允许您使用格式字符串进行灵活的格式化,而使用 Chris 的答案,您的格式是固定的。
在日志记录初始化函数中注册全局属性:
using namespace boost::log;
core::get()->add_global_attribute("Line", attributes::mutable_constant<int>(5));
core::get()->add_global_attribute("File", attributes::mutable_constant<std::string>(""));
core::get()->add_global_attribute("Function", attributes::mutable_constant<std::string>(""));
在日志记录宏中设置这些属性:
#define logInfo(methodname, message) do { \
LOG_LOCATION; \
BOOST_LOG_SEV(_log, boost::log::trivial::severity_level::info) << message; \
} while (false)
#define LOG_LOCATION \
boost::log::attribute_cast<boost::log::attributes::mutable_constant<int>>(boost::log::core::get()->get_global_attributes()["Line"]).set(__LINE__); \
boost::log::attribute_cast<boost::log::attributes::mutable_constant<std::string>>(boost::log::core::get()->get_global_attributes()["File"]).set(__FILE__); \
boost::log::attribute_cast<boost::log::attributes::mutable_constant<std::string>>(boost::log::core::get()->get_global_attributes()["Function"]).set(__func__);
不是很漂亮,但它很有效,对我来说还有很长的路要走。遗憾的是,boost 没有开箱即用地提供此功能。
do {... } while(false) 是为了使宏在语义上是中性的。
Chris展示的解决方案是可行的,但是如果要自定义格式或选择在每个接收器中显示哪些信息,则需要使用可变常量属性:
logging::core::get()->add_global_attribute("File", attrs::mutable_constant<std::string>(""));
logging::core::get()->add_global_attribute("Line", attrs::mutable_constant<int>(0));
然后,您创建一个包含这些新属性的自定义宏:
// New macro that includes severity, filename and line number
#define CUSTOM_LOG(logger, sev) \
BOOST_LOG_STREAM_WITH_PARAMS( \
(logger), \
(set_get_attrib("File", path_to_filename(__FILE__))) \
(set_get_attrib("Line", __LINE__)) \
(::boost::log::keywords::severity = (boost::log::trivial::sev)) \
)
// Set attribute and return the new value
template<typename ValueType>
ValueType set_get_attrib(const char* name, ValueType value) {
auto attr = logging::attribute_cast<attrs::mutable_constant<ValueType>>(logging::core::get()->get_global_attributes()[name]);
attr.set(value);
return attr.get();
}
// Convert file path to only the filename
std::string path_to_filename(std::string path) {
return path.substr(path.find_last_of("/\\")+1);
}
下一个完整的源代码创建两个接收器。第一个使用 File 和 Line 属性,第二个不使用。
#include <boost/log/trivial.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/attributes/mutable_constant.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/log/attributes/mutable_constant.hpp>
namespace logging = boost::log;
namespace attrs = boost::log::attributes;
namespace expr = boost::log::expressions;
namespace src = boost::log::sources;
namespace keywords = boost::log::keywords;
// New macro that includes severity, filename and line number
#define CUSTOM_LOG(logger, sev) \
BOOST_LOG_STREAM_WITH_PARAMS( \
(logger), \
(set_get_attrib("File", path_to_filename(__FILE__))) \
(set_get_attrib("Line", __LINE__)) \
(::boost::log::keywords::severity = (boost::log::trivial::sev)) \
)
// Set attribute and return the new value
template<typename ValueType>
ValueType set_get_attrib(const char* name, ValueType value) {
auto attr = logging::attribute_cast<attrs::mutable_constant<ValueType>>(logging::core::get()->get_global_attributes()[name]);
attr.set(value);
return attr.get();
}
// Convert file path to only the filename
std::string path_to_filename(std::string path) {
return path.substr(path.find_last_of("/\\")+1);
}
void init() {
// New attributes that hold filename and line number
logging::core::get()->add_global_attribute("File", attrs::mutable_constant<std::string>(""));
logging::core::get()->add_global_attribute("Line", attrs::mutable_constant<int>(0));
// A file log with time, severity, filename, line and message
logging::add_file_log (
keywords::file_name = "sample.log",
keywords::format = (
expr::stream
<< expr::format_date_time<boost::posix_time::ptime>("TimeStamp", "%Y-%m-%d_%H:%M:%S.%f")
<< ": <" << boost::log::trivial::severity << "> "
<< '[' << expr::attr<std::string>("File")
<< ':' << expr::attr<int>("Line") << "] "
<< expr::smessage
)
);
// A console log with only time and message
logging::add_console_log (
std::clog,
keywords::format = (
expr::stream
<< expr::format_date_time< boost::posix_time::ptime >("TimeStamp", "%Y-%m-%d %H:%M:%S")
<< " | " << expr::smessage
)
);
logging::add_common_attributes();
}
int main(int argc, char* argv[]) {
init();
src::severity_logger<logging::trivial::severity_level> lg;
CUSTOM_LOG(lg, debug) << "A regular message";
return 0;
}
该语句CUSTOM_LOG(lg, debug) << "A regular message";
生成两个输出,用这种格式写一个日志文件......
2015-10-15_15:25:12.743153: <debug> [main.cpp:61] A regular message
...并输出到控制台:
2015-10-15 16:58:35 | A regular message
另一种可能性是在创建每个日志记录后为其添加行和文件属性。这是可能的,因为在较新的版本中。稍后添加的属性不参与过滤。
假设使用变量 logger 标识的 severity_logger:
boost::log::record rec = logger.open_record(boost::log::keywords::severity = <some severity value>);
if (rec)
{
rec.attribute_values().insert(boost::log::attribute_name("Line"),
boost::log::attributes::constant<unsigned int>(__LINE__).get_value());
... other stuff appended to record ...
}
当然,上面的内容会被包装成方便的宏。
稍后您可以使用接收器的自定义格式化程序显示此属性:
sink->set_formatter( ...other stuff... << expr::attr<unsigned int>("Line") << ...other stuff... );
与之前的答案不同,这种方法需要更多自定义代码,并且不能使用现成的 boost 日志记录宏。
为了后代的缘故——我为非常简单的日志记录需求制作了这组宏,这对我很有帮助——用于简单的日志记录需求。但是它们说明了如何在一般情况下执行此操作,并且该概念很容易与 Boost 配合使用。它们应该是一个文件的本地文件(在多个进程中运行,有时在多个进程中的多个线程中运行)。它们是为了相对简单而不是速度而设计的。他们可以安全地放入 if 语句等,以免窃取 else。在想要记录的函数开始时,调用
GLogFunc("function name");
然后可以这样做来记录一整行:
GLogL("this is a log entry with a string: " << some_string);
他们就是这样——
#define GLogFunc(x) std::stringstream logstr; \
std::string logfunc; \
logfunc = x
#define GLog(x) do { logstr << x; } while(0)
#define GLogComplete do { \
_log << "[PID:" << _my_process << " L:" << __LINE__ << "] ((" << logfunc << ")) " << logstr.str() << endl; \
logstr.str(""); \
_log.flush(); \
} while(0)
#define GLogLine(x) do { GLog(x); GLogComplete; } while(0)
#define GLogL(x) GLogLine(x)
#define GLC GLogComplete
一个人也可以在几行上建立一个日志......
GLog("I did this.");
// later
GLog("One result was " << some_number << " and then " << something_else);
// finally
GLog("And now I'm done!");
GLogComplete;
无论流 _log 是什么(我在类构造函数中将其作为文件打开,在这种情况下保证是安全的)都会得到如下输出:
[PID:4848 L:348] ((SetTextBC)) ERROR: bad argument row:0 col:-64
并且可以有条件地关闭它们,并且在编译时通过符号否定所有性能损失,如下所示:
#ifdef LOGGING_ENABLED
... do the stuff above ...
#else
#define GLogFunc(x)
#define GLog(x)
#define GLogComplete
#define GLogLine(x)
#define GLogL(x)
#endif
这是我的解决方案。
设置代码
auto formatter =
expr::format("[ %3% %1%:%2% :: %4%]")
% expr::attr< std::string >("File")
% expr::attr< uint32_t >("Line")
% expr::attr< boost::posix_time::ptime >("TimeStamp")
% expr::smessage
;
/* stdout sink*/
boost::shared_ptr< sinks::text_ostream_backend > backend =
boost::make_shared< sinks::text_ostream_backend >();
backend->add_stream(
boost::shared_ptr< std::ostream >(&std::clog, NullDeleter()));
// Enable auto-flushing after each log record written
backend->auto_flush(true);
// Wrap it into the frontend and register in the core.
// The backend requires synchronization in the frontend.
typedef sinks::synchronous_sink< sinks::text_ostream_backend > sink2_t;
boost::shared_ptr< sink2_t > sink_text(new sink2_t(backend));
logging::add_common_attributes();
sink_text->set_formatter(formatter);
日志使用代码(短版):
rec.attribute_values().insert("File", attrs::make_attribute_value(std::string(__FILE__))); \
完整版本 :
#define LOG(s, message) { \
src::severity_logger< severity_level > slg; \
logging::record rec = slg.open_record(keywords::severity = s); \
if (rec) \
{ \
rec.attribute_values().insert("File", attrs::make_attribute_value(boost::filesystem::path(__FILE__).filename().string())); \
rec.attribute_values().insert("Line", attrs::make_attribute_value(uint32_t(__LINE__))); \
logging::record_ostream strm(rec); \
strm << message; \
strm.flush(); \
slg.push_record(boost::move(rec)); \
} \
}\
如果我定义全局属性(就像人们之前建议的那样),即
logging::core::get()->add_global_attribute("File", attrs::mutable_constant<std::string>(""));
然后我得到空文件/搅拌。