如何组合生成唯一标识符的两个提升日志记录宏?

How do I combine two boost logging macros that generate unique identifiers?

我正在使用 boost_logging(版本 2),我想避免在我的代码中乱扔这些东西:

BOOST_LOG_NAMED_SCOPE("SomeModuleName")
BOOST_LOG_FUNCTION()

我现在的想法是,我更愿意将它们组合成这样的宏:

#define LOG_NAMED_SCOPE_FUNCTION(name)\
  BOOST_LOG_NAMED_SCOPE(name)\
  BOOST_LOG_FUNCTION()

但是当我尝试这样做时出现错误。见下文。

如果有一个解决方案允许我在模块级别执行类似 BOOST_LOG_NAMED_SCOPE("SomeModuleName") 的操作(也会出错),我会非常高兴。更好的解决方案是执行某种 RAII/AOP 以允许我在函数的开头和结尾附加 "enter" 和 "exit" 跟踪消息,因为这是我的最终目标.

我猜是因为生成的唯一标识符是在宏定义时扩展的,而不是在宏调用时扩展的。我还查看了 DEFERRED 和 EXPAND 助手,但我不确定它们是否会在这里帮助我。

错误信息如下:

Project/SomeModuleName.cpp:9:5: error: redefinition of '_boost_log_named_scope_sentry_9'
    LOG_NAMED_SCOPE_FUNCTION("SomeModuleName")
    ^
Project/Logging.hpp:24:5: note: expanded from macro 'LOG_NAMED_SCOPE_FUNCTION'
    BOOST_LOG_FUNCTION()
    ^
/usr/local/include/boost/log/attributes/named_scope.hpp:458:36: note: expanded from macro 'BOOST_LOG_FUNCTION'
    BOOST_LOG_NAMED_SCOPE_INTERNAL(BOOST_LOG_UNIQUE_IDENTIFIER_NAME(_boost_log_named_scope_sentry_), BOOST_CURRENT_FUNCTION, __FILE__, __LINE__, ::boost::log::attributes::named_scope_entry::function)
                                   ^
/usr/local/include/boost/log/utility/unique_identifier_name.hpp:48:5: note: expanded from macro 'BOOST_LOG_UNIQUE_IDENTIFIER_NAME'
    BOOST_LOG_UNIQUE_IDENTIFIER_NAME_INTERNAL(prefix, __LINE__)
    ^
note: (skipping 2 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
/usr/local/include/boost/preprocessor/cat.hpp:22:32: note: expanded from macro 'BOOST_PP_CAT'
#    define BOOST_PP_CAT(a, b) BOOST_PP_CAT_I(a, b)
                               ^
/usr/local/include/boost/preprocessor/cat.hpp:29:34: note: expanded from macro 'BOOST_PP_CAT_I'
#    define BOOST_PP_CAT_I(a, b) a ## b
                                 ^
<scratch space>:89:1: note: expanded from here
_boost_log_named_scope_sentry_9
^
Project/SomeModuleName.cpp:9:5: note: previous definition is here
Project/Logging.hpp:23:5: note: expanded from macro 'LOG_NAMED_SCOPE_FUNCTION'
    BOOST_LOG_NAMED_SCOPE(name)\
    ^
/usr/local/include/boost/log/attributes/named_scope.hpp:449:36: note: expanded from macro 'BOOST_LOG_NAMED_SCOPE'
    BOOST_LOG_NAMED_SCOPE_INTERNAL(BOOST_LOG_UNIQUE_IDENTIFIER_NAME(_boost_log_named_scope_sentry_), name, __FILE__, __LINE__, ::boost::log::attributes::named_scope_entry::general)
                                   ^
/usr/local/include/boost/log/utility/unique_identifier_name.hpp:48:5: note: expanded from macro 'BOOST_LOG_UNIQUE_IDENTIFIER_NAME'
    BOOST_LOG_UNIQUE_IDENTIFIER_NAME_INTERNAL(prefix, __LINE__)
    ^
note: (skipping 2 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
/usr/local/include/boost/preprocessor/cat.hpp:22:32: note: expanded from macro 'BOOST_PP_CAT'
#    define BOOST_PP_CAT(a, b) BOOST_PP_CAT_I(a, b)
                               ^
/usr/local/include/boost/preprocessor/cat.hpp:29:34: note: expanded from macro 'BOOST_PP_CAT_I'
#    define BOOST_PP_CAT_I(a, b) a ## b
                                 ^
<scratch space>:85:1: note: expanded from here
_boost_log_named_scope_sentry_9
^

您遇到的问题是因为 BOOST_LOG_FUNCTION 本质上是基于 BOOST_LOG_NAMED_SCOPE - 它添加了一个范围,其名称对应于当前函数签名。这两个宏都会创建一个局部变量,该变量的名称对于源文件的给定行是唯一的(它使用 __LINE__ 来生成该名称)。根据 C/C++ 预处理器规则,所有宏都扩展为一行,因此您的 LOG_NAMED_SCOPE_FUNCTION 扩展为同一范围内的两个相同命名的局部变量,因此出现编译器错误。

解决此问题的一种方法是定义您的宏,以便它直接为两个范围定义两个不同的变量。变量应该有 named_scope::sentry 类型,这是一个作用域守卫,在构造和销毁时自动将作用域压入和弹出堆栈。

#define LOG_NAMED_SCOPE_FUNCTION(name)\
    boost::log::attributes::named_scope::sentry BOOST_LOG_UNIQUE_IDENTIFIER_NAME(scope_sentry1_)(name, __FILE__, __LINE__);\
    boost::log::attributes::named_scope::sentry BOOST_LOG_UNIQUE_IDENTIFIER_NAME(scope_sentry2_)(BOOST_CURRENT_FUNCTION, __FILE__, __LINE__, boost::log::attributes::named_scope_entry::function)

这样每个 LOG_NAMED_SCOPE_FUNCTION 都会向列表中添加两个作用域 - 命名作用域和当前函数。

但是,从您的描述来看,这实际上并不是您想要实现的。 Boost.Log 不允许直接使用日志记录的来源模块名称来标记日志记录 - 主要是因为没有可移植的方法知道这一点,而且非可移植的方法很昂贵。但是有一些方法可以模拟这种行为。这里有一些想法。

最简单的方法是使用您自己的日志记录宏,它会自动添加当前模块名称属性。

// Define the attribute keyword for the module name
BOOST_LOG_ATTRIBUTE_KEYWORD(a_module, "Module", std::string)

#define MY_LOG(lg)\
    BOOST_LOG(lg) << boost::log::add_value(a_module, CURRENT_MODULE)

如果您在项目设置中将 CURRENT_MODULE 定义为命名当前模块的字符串,MY_LOG 宏将自动将其作为属性附加到记录中。请参阅有关 attribute keywords and add_value manipulator.

的文档

另一种方法是使用 channels. If your loggers are not shared by different modules, you can set the current module name as the channel name. Or, if you already use channels, add it as a new separate attribute to every logger you create. You can write your own logger feature 实现自动化。

如果您确实共享记录器,那么您还可以查看 scoped attributes 是如何实现的,尤其是 BOOST_LOG_SCOPED_THREAD_TAG。由于您可能会在不同模块之间调用函数,因此 BOOST_LOG_SCOPED_THREAD_TAG 不会按原样为您工作(因为如果已经存在,它不会替换集合中的属性 - 在您的情况下,这意味着您只会看到首先设置属性的模块的名称),但您可以实现适合您的情况的类似内容。这个想法是如果添加 none 或替换现有的一个,则将当前模块名称添加为特定于线程的属性。这必须在范围保护中完成,该范围保护必须在可以从其他模块调用的每个函数中使用。