gpt4 book ai didi

c++ - 使用全局变量和仿函数实现日志记录

转载 作者:IT老高 更新时间:2023-10-28 22:41:27 53 4
gpt4 key购买 nike

我想实现具有以下特征的 C++ 日志记录:

  • 它必须对所有源代码都可用,而不需要每个函数都有一个额外的参数(我认为它需要一个全局参数)
  • 日志调用可以指定严重级别(INFO、DEBUG、WARN 等),并且可以在运行时设置日志记录工具以忽略低于某个严重级别的调用
  • 可以在运行时将日志接收器设置到控制台或文件。

不需要的东西是:

  • 在运行时支持多个日志接收器(即,全部发送到控制台或文件)
  • 支持多线程日志记录
  • 通过能力cout记录器调用中的 -style 表达式(例如 "foo=" << foo )。我只会通过 std::string .

我找到了 this answer ,这似乎满足了我的需求,但这有点超出我的想象。我认为我的困惑集中在仿函数上。 (我阅读了维基百科的文章,但显然没有深入了解。)

以下是我了解的部分:

  • 使用宏(例如 LOG_DEBUG)方便地指定严重级别并调用记录器。
  • 使用 #ifdef NDEBUG以防止日志记录调用被编译(尽管我需要能够在运行时设置日志记录)。
  • 使用宏调用记录器的基本原理,以便它可以自动且不可见地添加信息,如 __FILE____LINE__在调用记录器的位置。
  • LOG 宏包含一个以 static_cast<std::ostringstream&> 开头的表达式.我认为这纯粹是为了评估 cout -style 格式化字符串,我不打算支持。

这就是我苦苦挣扎的地方:

Logger& Debug() {
static Logger logger(Level::Debug, Console);
return logger;
}

阅读 operator() ,看起来像 class Logger用于创建“仿函数”。每个 Logger 仿函数都被实例化 (?) 并带有一个级别和一个 LogSink。 (你“实例化”了一个仿函数吗?)LogSink 被描述为“一个使用预格式化消息的后端”,但我不知道它会是什么样子或者它是如何“写入”的。静态 Logger 对象在什么时候实例化?是什么导致它被实例化?

这些宏定义...

#define LOG(Logger_, Message_)                   \
Logger_( \
static_cast<std::ostringstream&>( \
std::ostringstream().flush() << Message_ \
).str(), \
__FUNCTION__, \
__FILE__, \
__LINE__ \
);

#define LOG_DEBUG(Message_) LOG(Debug(), Message_)

...还有这行代码...

LOG_DEBUG(my_message);

... 被预处理为:

Debug()(my_message, "my_function", "my_file", 42);

执行时会发生什么?

格式化字符串是如何以及在哪里实际写入“日志接收器”的?

(注意:有人建议我查看 log4cpp - 我发现它比我需要的要大得多,也更难理解,更不用说我会带来第三个政治问题 -党图书馆进入我们的环境)


更新:

为了了解上述解决方案的工作原理,我尝试编写了一个最低限度完整的工作程序。我故意删除了以下内容:

  • 涉及 std::ostringstream 的“魔法”
  • #ifdef NDEBUG
  • Logger Level 类枚举
  • LogSink ctor 参数(现在我只写到 std::cout)

这是完整的源文件:

#include <iostream>
#include <string>

class Logger {
public:
Logger(int l);
void operator()(std::string const& message,
char const* function,
char const* file,
int line);
private:
int _level;
};

Logger::Logger(int l) :
_level(l)
{ }

#define LOG(Logger_, Message_) \
Logger_( \
Message_, \
__FUNCTION__, \
__FILE__, \
__LINE__ \
)

#define LOG_DEBUG(Message_) \
LOG( \
Debug(), \
Message_ \
)

Logger& Debug() {
static Logger logger(1);
return logger;
}

// Use of Logger class begins here

int main(int argc, char** argv) {
LOG_DEBUG("Hello, world!");
return 0;
}

编译时:

$ c++ main.cpp
Undefined symbols for architecture x86_64:
"Logger::operator()(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, char const*, char const*, int)", referenced from:
_main in main-c81cf6.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

我看到没有定义一个函数来接受这四个参数并将它们写入 std::cout ,但是我需要定义的函数的名称是什么?

(现在我同意我应该使用 Boost::Log,但仿函数显然是一个我不完全理解的主题。)

最佳答案

Debug 函数返回一个 Logger 对象(在第一次调用此函数时创建)。

这个 Logger 对象似乎为它定义了 operator()()(根据宏定义判断),这确实使它成为 一个仿函数 。顺便说一句,仿函数并没有什么特别之处——它可以定义为为它定义了 operator()() 的任何类型。但是,您的分析似乎并不正确。相反,

LOG_DEBUG(my_message);

将扩展为

LOG(Debug(), Message_)

然后进入

Debug()(Message_, __FUNCTION__, __FILE__, __LINE__);

这里Debug()会返回一个定义了operator()()的对象,这个对象会用于调用。

一些问答

Why doesn't the Logger& Debug() signature specify four arguments?

因为它不需要。 Debug() 只返回(静态)使用特定参数(日志级别和输出设备)创建的 Logger 对象。

At what point is the static Logger object instantiated? What causes it to be instantiated?

当第一次调用 Debug() 函数时,它会初始化它的静态对象。这是静态函数变量的基础。

最后,但并非最不重要。我个人认为编写自己的记录器不值得。除非你真的需要一些特别的东西,否则这很乏味而且非常无聊。虽然我对 Boost.Log 和 log4cpp 都不是很着迷,但我(实际上)肯定会使用其中之一,而不是滚动我自己的记录器。即使是次优的日志记录也比在自己的解决方案上花费数周时间要好。

关于c++ - 使用全局变量和仿函数实现日志记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36044897/

53 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com