log4cpp是个开源项目的C++日志库,该库的license是LGPL, 我们的项目里使用了它。它的接口字符串参数类型全是std::string,只支持窄字符字符串。以前的同事更改了log4pp库的部分源码,使接口字符串参数可以处理std::wstring(宽字符字符串),我们的项目里使用到的字符串恰好都是std::wstring类型的。
出于对开源价值的认同,我认为:
? log4cpp和log4j系出同门,而Java默认的字符串就是宽字符组成的,因此log4cpp应该也有机制提供
? 对于一个被广泛使用的开源项目,不可能没有考虑过这个问题
google上搜索了一把,没有任何关于log4cpp如何日志宽字符字符串的信息,那么只好自己动手了,有代码还怕啥? tea in, code out!
代码里搜索了一下std::wstring以及wchar_t,发现了整个库只有CategoryStream流的opeartor<<函数的参数是std::wstring字符串, 找了篇log4cpp的介绍文章”便利的开发工具-log4cpp快速使用指南”(http://www.ibm.com/developerworks/cn/linux/l-log4cpp/index.html), 又仔细hack了下log4cpp代码,终于尝试出来如何日志宽字符字符串的代码。
请看:
//unicode style
static void DoLog(Category& logger, Priority::Value priority, const wchar_t* stringFormat, …)
{
CategoryStream &stream = logger.getStream(priority);
wchar_t pBuf[5000] = {0};
va_list args;
va_start(args, stringFormat);
vswprintf_s(pBuf, sizeof(pBuf) / sizeof(pBuf[0]), stringFormat, args);
std::wstring msg(pBuf);
stream.operator<<<std::wstring>(msg);
}
static void DoLog(Category& logger, Priority::Value priority, const wchar_t* stringFormat, …)
{
CategoryStream &stream = logger.getStream(priority);
wchar_t pBuf[5000] = {0};
va_list args;
va_start(args, stringFormat);
vswprintf_s(pBuf, sizeof(pBuf) / sizeof(pBuf[0]), stringFormat, args);
std::wstring msg(pBuf);
stream.operator<<<std::wstring>(msg);
}
最后一行十分变态: stream.operator<<<std::wstring>(msg);
stream类的operator<<是一个模板,并且有一个“半模板实例化的”std::wstring参数类型的operator<<,
但是可惜原型是写成这样的:
template<typename T>
CategoryStream& operator<<(const std::wstring& t) {
if (getPriority() != Priority::NOTSET) {
if (!_wbuffer) {
if (!(_wbuffer = new std::wostringstream)) {
// XXX help help help
}
}
(*_wbuffer) << t;
}
return *this;
}
CategoryStream& operator<<(const std::wstring& t) {
if (getPriority() != Priority::NOTSET) {
if (!_wbuffer) {
if (!(_wbuffer = new std::wostringstream)) {
// XXX help help help
}
}
(*_wbuffer) << t;
}
return *this;
}
这里模板参数typename T是没有用到的,所以才有了上面那么变态的代码。
打开日志文件后发现,输入到日志文件里的的确是宽字符。如果日志”xuminggang”,则显示成”x u m i n g g a n g”, 稀松宽拉, 很不正常!查看16进制,每个字符都变成了在英文字母对应的窄字符后面添加了0.反思一下,我们平时看到的文件是宽字符的吗?没有!
是的,没有!
回头来看,log4cpp库的接口字符串类型设计成std::string是正确的,任何一个日志库最终日志到文件中去的都是窄字符格式(或宽字符编码后的格式,譬如utf-8), 因为你平时使用编辑器打开的常见文本文件都是这种格式。你可能会说utf-8这种unicode编码格式的文件也很常见啊!是的,通过std::wstring转换成utf-8格式的字符,最后的数据类型还是std::string的!因此如果要日志宽字符字符串,则可以先转换成窄字符字符串,然后再日志!
再退一步看了看以前同事修改的代码,跟踪调试了一下,发现虽然更改了接口字符串参数类型为宽字符类型,但是内部日志的地方还是进行了一层 转换,将宽字符转换成窄字符,只不过添加了许多复杂的功能(譬如通过读文件头的BOM来判断是否是utf-8或utf-16编码格式日志文件等等)。
对于我们的项目,我想完全可以在LGPL的约束下使用log4cpp。