我想实现具有以下特征的C ++日志记录:
我不需要的东西是:
cout
- 样式表达式(例如"foo=" << foo
)。我只会传递std::string
。我找到了this answer,这似乎可以满足我的需求,但这有点过头了。我认为我的困惑集中在仿函数上。 (我读过维基百科的文章,但它显然没有沉入其中。)
以下是我做 >的部分:
#ifdef NDEBUG
来保持日志记录的编译(尽管我需要能够在运行时设置日志记录)。__FILE__
和__LINE__
等信息。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 - 我发现它比我需要的更大更难理解,更不用说我带来的第三个政治问题 - 党的图书馆进入我们的环境)
更新:
为了理解上述解决方案的工作原理,我尝试编写一个最小的完整的工作程序。我故意删除了以下内容:
以下是完整的源文件:
#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,但是仿函数显然是一个我不完全理解的话题。)
答案 0 :(得分:5)
函数Debug
返回一个Logger对象(在第一次调用此函数时创建)。
此Logger
对象似乎已为其定义operator()()
(根据宏定义判断),这确实使成为仿函数。顺便说一句,仿函数并不特别 - 它定义了operator()()
为其定义的任何类型。但是,您的分析看起来并不正确。相反,
LOG_DEBUG(my_message);
将扩展为
LOG(Debug(), Message_)
然后进入
Debug()(Message_, __FUNCTION__, __FILE__, __LINE__);
此处Debug()
将返回定义了operator()()
的对象,此对象将用于调用。
为什么没有Logger&amp; Debug()签名指定了四个参数?
因为它不需要。 Debug()只返回(静态)使用特定参数(日志级别和输出设备)创建的Logger对象。
在什么时候实例化静态Logger对象?是什么导致它被实例化?
第一次调用Debug()函数时,它会初始化它的静态对象。这是静态函数变量的基础。
最后,但并非最不重要。我个人觉得编写自己的记录器不值得。除非你真的需要一些特别的东西,否则它很乏味且非常无聊。虽然我对Boost.Log和log4cpp并不是很疯狂,但我(实际上)肯定会使用其中一个而不是滚动我自己的记录器。即使是次优的日志记录也比在自己的解决方案上花费数周更好。