【问题标题】:__func__ and logging__func__ 和日志记录
【发布时间】:2012-12-16 16:01:40
【问题描述】:

我正在用 C++ 实现一个日志处理程序,它工作得很好,但是我发现有一件事是有用的,那就是记录器从哪里获得输出。

我想这并不是什么大问题,但我偶然发现了__func__ 标识符,它基本上会保留当前函数的函数名。

所以我的日志类中有一个名为Write 的静态函数,它需要一个日志级别和一个变化列表。所以我会这样称呼它:

Log::Write(LOG_DEBUG, "this is an integer: %d", 10);

它会打印出来:

2013-01-02 => 10:12:01.366 [DEBUG]: this is an integer: 10

但是我认为在消息中也包含调用者可能会很有用,以产生如下内容:

2013... => 10:12:... (functionName) [DEBUG]: blah

所以我可以做的(当然)是将__func__ 作为参数添加到Log::Write,但这意味着任何时候我调用Log::Write 我还需要发送__func__,这将始终是一样的,我觉得应该可以在没有明确说明的情况下这样做。

所以我想要的是将提供的功能:

Log::Write(LOG_DEBUG, __func__, "message");

无需每次都明确输入 func

我不知道这是否真的可能,我最好的选择是有一些方法可以取消引用函数 Write 内部的调用者,我似乎不太可能只是“推断”这样的参数。但至少值得一问,也许我可以看到有哪些选择。

谢谢。

【问题讨论】:

  • 这是“传统上”用宏完成的。
  • C++ 中没有__func__ 标识符;它是对 C99 的补充,而 C++ 没有采用。我怀疑大多数 C++ 编译器会将其作为扩展提供(因为它们也是 C 编译器),但微软没有,而且我的经验表明 __FUNCTION__ 尽管不是标准的一部分,但实际上更便携。跨度>
  • 我相信 __func__ 标识符是 C++11 添加的,基本上它可用于任何函数并包含当前函数的名称。但是我可能错了,但这是我的理解。

标签: c++ logging callstack func


【解决方案1】:

这通常使用宏以及文件名__FILE__ 和行号__LINE__ 来完成。

void Log::Write(Level l,
                char const* function,
                char const* file,
                int line,
                char const* format,
                ...);

被包装成一个宏:

#define LOG(Level_, Format_, ...) \
    Log::Write(Level_, __func__, __FILE__, __LINE__, Format_, __VA_ARGS__);

请注意,您可能希望通过检查是否在该级别登录来“节省”一些计算:

// suppose availability of "bool Log::Enabled(Level l)"
#define LOG(Level_, Format_, ...)   \
    while (Log::Enabled(Level_)) {  \
      Log::Write(Level_, __func__, __FILE__, __LINE__, Format_, __VA_ARGS__);  \
      break;  \
    }

使用while 代替if 是为了避免悬空的else 问题。

注意:您可能希望研究使用流进行日志记录。 printf 样式的问题在于它不可组合。使用流,您可以为任何X 重载std::ostream& operator<<(std::ostream&, X const&),然后只编写一次一个方法将其内容转储到日志中。

【讨论】:

  • 在这种情况下,if (!Log::isEnabled(level)) ; else ... 更容易处理悬空 else 问题。但是存在更好的解决方案,它完全避免了可变参数。 (而__VAR_ARGS__ 是 C++11 功能,如果您担心可移植性,可能不可用。)
  • 很多很棒的信息,谢谢!我实际上正在使用 ostream,所以我也可以写 Log::get(level) << "some text" 我对 C++ 有点生疏,并且我想我对 printf-type-syntax 很满意。 (过去几年一直在使用 Objc,但尝试使用 C++ 来加快速度)。但这是一些非常好的信息,我真的很高兴,谢谢! (所以要清楚......我的Write 函数实际上只是ostream 的包装器,获取vargs 并使用get(level) 生成日志记录。
  • @JamesKanze:为了摆脱可变参数,我倾向于切换到ostream 日志记录。它只是更方便(用于记录)。
  • @qrikko:printf-style 和 ostream-style 都有它们的用途。 printf-style 更适合国际化(因此非常适合向用户显示文本),而ostream-style 更易于组合;值得庆幸的是,日志记录需要可组合性而不是国际化。
  • @MatthieuM。这真的很好,感谢您花时间指出这一点。正如我所说,我确实从 ostream 方式开始,但只是有点“损坏”或者从 Objc 中调用它,你通常只使用类似 vararg 的语法,所以我将 ostream 日志记录包装在一个函数中(@ 987654343@) 解析可变参数并将它们发送到 ostream 所以只是一个坏习惯,我将开始使用 ostream 实现,甚至删除 Log::Write 函数,因为它对我来说实际上只是“语法糖”。
【解决方案2】:
#define WRITE_LOG(X,...) Log::Write(LOG_DEBUG, __func__, (X),__VA_ARGS__);

用这个宏代替函数写日志,它会自动添加func参数。正如 Mat 提到的,这是典型的方式,在这种情况下我想不出任何方法来避免宏。

【讨论】:

  • 我认为这样就可以了,它非常干净,并且绝对可以解决“问题”,谢谢。我没有想到这一点,可能应该在谷歌上多呆一会儿,或者现在我看到它时要多考虑一下。
  • @qrikko 是的,它应该可以工作。您可以为每个日志级别制作不同的宏,或者修改此宏以采用 2 个参数,例如您的日志函数。
  • 请注意,这需要 C++11。有更好的解决方案,根本不使用可变参数:基本上,Log::Write 返回一个 LogStream 对象,它有一个模板化的 operator<< 转发到活动流(或不,取决于日志记录级别是否为活跃)。
  • @JamesKanze 我的目标是 C++11 并在其中使用了很多功能,所以这对我来说不是问题,但这是一个有效的观点,应该注意!我也确实在后台使用了 ostreams,并且对 varargs(旧的(可能是坏的)习惯)感觉更舒服,所以Log::Write 实际上只是包裹了另一个名为get(short level) 的函数,它返回一个ostream 参考,就是那个@ 987654328@ 实际上完成了所有的日志记录。
猜你喜欢
  • 2011-06-17
  • 2011-01-28
  • 2011-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-30
  • 2023-03-11
相关资源
最近更新 更多