将具有堆栈缓冲区的printf样式记录器转换为stringstream

时间:2014-12-01 22:08:09

标签: c++ c logging variadic-functions

我目前有一个定义宏的日志记录解决方案:

#define MY_LOG(level, component, message, ...) { MyLog::Instance()->Log(level, component, message, __FILE__, __LINE__, ##__VA_ARGS__); }

const char * message参数使用类似printf的格式,例如" 我的名字是%s而我是%u。"

我正在使用的实际日志记录方法是在堆栈上声明 char buffer [2048] 变量,我使用vsnprintf将我的va_list参数转换为我的message参数中定义的变量我的缓冲区。但是,因为我经常处理的值大于2k,所以我的缓冲区总是被截断,所以我想在stringstream中重新处理整个事情会更灵活。

实现此解决方案时,我遇到了从列表中检索va_args参数以与<<一起使用时出现问题的问题。 operator ...我唯一可用的解决方案是解析%值的整个消息参数,并使用检索到的类型的va_arg检索它们吗?我还需要在我的%值之间推送文本,所以vsnprintf真的很方便,因为现在我需要在%上拆分字符串以将前一个文本推送到我的stringstream ...任何提示或想法来帮助我解决这个问题?

我不能使用外部库,因为我支持可能支持或不支持C ++ 11的平台和编译器,但我不能完全依赖新的语言功能,而是添加了定义来支持两种实施都是可行的。此外,由于当前代码库中已存在大量痕迹,我无法真正更改消息格式。

谢谢!

1 个答案:

答案 0 :(得分:0)

我建议不要尝试在stringstream上重新实现vsnprintf。如果你打算保持你的记录器前端不变,你应该保留vsnprintf!

首先,您应该猜测格式化字符串的最终大小并分配一些缓冲区(如果您愿意,可以使用C ++ 11 unique_ptr)。你不应该担心分配开销,因为这基本上是字符串串做的。

然后,使用此缓冲区调用vsnprintf并测试其返回值。此值(如果为正)将为您提供正确格式化输出字符串所需的最小大小,不包括终止空字符。知道了这一点,您可以选择重新分配一些具有正确大小的缓冲区,并再次使用它来调用vsnprintf。

最后用这个格式化字符串做任何事情: - 构造一个std :: string - 把它写在某个地方(到一个文件?) - 不要忘记释放内存(或使用unique_ptr)。

注意:不要直接使用std :: string作为缓冲区。写入std :: string :: c_str()返回的缓冲区是未定义的行为。