【发布时间】:2016-07-02 21:03:03
【问题描述】:
我想实现具有以下特征的 C++ 日志记录:
- 它必须对所有源代码都可用,而不需要每个函数都有一个额外的参数(我认为它需要一个全局参数)
- 日志记录调用可以指定严重级别(INFO、DEBUG、WARN 等),并且可以在运行时设置日志记录工具以忽略低于特定严重级别的调用
- 可以在运行时将日志接收器设置到控制台或文件。
我不需要的东西是:
- 在运行时支持多个日志接收器(即,所有日志都发送到控制台或文件)
- 支持多线程日志记录
- 能够在记录器调用中传递
cout样式的表达式(例如"foo=" << foo)。我只会传递一个std::string。
我找到了this answer,它似乎可以满足我的需求,但它有点超出我的想象。我认为我的困惑集中在函子上。 (我阅读了维基百科的文章,但显然没有深入了解。)
以下是我了解的部分:
- 使用宏(例如 LOG_DEBUG)方便地指定严重级别并调用记录器。
- 使用
#ifdef NDEBUG来阻止日志记录调用被编译(尽管我需要能够在运行时设置日志记录)。 - 使用宏调用记录器的基本原理,以便它可以在调用记录器的位置自动且不可见地添加
__FILE__和__LINE__等信息。 - LOG 宏包含以
static_cast<std::ostringstream&>开头的表达式。我认为这纯粹是为了评估cout风格的格式化字符串,我不打算支持它。
这就是我苦苦挣扎的地方:
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,但函子显然是一个我不完全理解的话题。)
【问题讨论】:
标签: c++ logging global-variables functor