您的位置:首页 > 编程语言 > C语言/C++

C++之日志打印

2015-11-24 13:48 435 查看
一、C++日志输出到文件

众所周知,在C++标准库<iostream>中提供了一个控制台(console)输出对象——std::cout,程序员可以调用该对象将需要查看的内容输出到控制台。除了std::cout外,C++标准库<fstream>还提供了一个文件输出流,程序员可以使用它定一个流对象,如“std::ofstream fout”,并关联一个文件,然后像使用控制台输出一样将将需要查看的内容输出到文件。参考《C++ Primer Plus》第17章。

我们在设计程序的时候,可以借助C++标准库的文件流对象,程序运行的日志输出到文件。具体的方法是:1)定义一个全局的std::ofstream对象,在main()函数中将它初始化,指定关联的文件。然后在其他各个文件使用extern声明该对象,并在需要的地方使用该对象;2)设计一个全局函数,使用print_log名称空间封装。这个函数接受一个“const char *”的参数,在函数再定义一个临时的文件,并将传递进来的字符串写入文件,如下:

<span style="font-size:14px;">void output(const char* log)
{
std::ofstream fout;
fout.open("/Log/log.txt", std::out | std::append);
fout << log;
fout.flush();
fout.close();
}</span>


这样,一个基本的日志输出到文件功能就具备了。此外,我们也可通过debug宏(比如VS的预处理器定义中就含有“_DEBUG”宏),选择在debug时将日志输出到console,在release是将日志输出到文件。

二、为日志添加时间戳

为了方便跟踪程序运行的状况,我们需要为每条日志添加时间戳。具体的方法也有两种,第一种情况对应上面的全局文件流对象,使用LOG打印宏进行包装。第二种是在上面的函数中调用系统时间,如下:

<span style="font-size:14px;">void output(const char* log)
{
std::ofstream fout;
fout.open("/Log/log.txt", std::out | std::append);
SYSTEMTIME sys;
GetLocalTime(&sys);
fout << "[Time]:" << sys.wYear << "-" << sys.wMonth  << "-" << sys.wDay  << " "   << sys.wHour << ":" << sys.wMinute << ":" << sys.wSecond << "." << sys.wMilliseconds << log << std::endl;
fout.flush();
fout.close();
}</span>
此处是通过调用windows系统函数,获得时间戳,也可以应用C++11新增的“chrono库”来获取与平台无关的时间戳或者调用C/C++的两个宏“__DATE”、“__TIME__”。参考《深入应用C++11》第6章。此外,在QT中,获取时间戳的代码如下:

<span style="font-size:14px;">    QString current_date_time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
int current_time = QTime::currentTime().msec();
QString current_date = QString("(%1 %2)").arg(current_date_time).arg(current_time);</span>


三、添加其他的日志信息

除了时间戳,我们可能还关心日志发生的位置,如文件名、行号、类名和函数名。在此可以参考博文“C/C++语言中特定的宏”,通过调用宏“__FILE__”、“__LINE__”、“__FUNCTION__”,来自动获取日志发生时的位置,而不是采用硬编码的方式。

对于类名,在QT中可以通过元编程的方式获得,如下:

<span style="font-size:14px;">	if (row < 0 || column < 0 || row > 1 || column > 1) {
qWarning("QGridLayout: Cannot add %s/%s to %s/%s at row %d column %d",
widget->metaObject()->className(), widget->objectName().toLocal8Bit().data(),
q->metaObject()->className(), q->objectName().toLocal8Bit().data(), row, column);</span>


参考《An Introduction to Design Patterns in C++ with Qt》的第12章。继承自QObject的对象,添加了宏“Q_OBJECT”的类,QT会为它生成一个meta对象,里面包含了它的类信息。

四、跨线程的日志同步

两个线程同时输出日志的时候,如果采用的是方法一(全局文件流对象),容易出现日志错乱的情况,特别是一条日志通过包含多个“《”符合的时候。解决的办法是添加互斥锁。当然,最好是采用第二中方法(全局日志输出函数),将日志组好,在传参给函数,调用函数输出日志。

五、改进“一去、二三里的Qt日志输出到文件”

参考博文:“Qt之日志输出文件”。该博文对Qt的日志输出介绍的比较详细,其中主要应用Qt的QMessageLogContext。不足的是:1)缺少毫秒级的时间戳;2)没用打印函数名和类名;

1,添加毫秒级时间戳,见上文代码。

2,在函数调研的地方,使用宏“__FUNCTION__”,添加函数名。

3,考虑使用全局文件,而不用每次open和close。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: