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 *”的参数,在函数再定义一个临时的文件,并将传递进来的字符串写入文件,如下:
这样,一个基本的日志输出到文件功能就具备了。此外,我们也可通过debug宏(比如VS的预处理器定义中就含有“_DEBUG”宏),选择在debug时将日志输出到console,在release是将日志输出到文件。
二、为日志添加时间戳
为了方便跟踪程序运行的状况,我们需要为每条日志添加时间戳。具体的方法也有两种,第一种情况对应上面的全局文件流对象,使用LOG打印宏进行包装。第二种是在上面的函数中调用系统时间,如下:
三、添加其他的日志信息
除了时间戳,我们可能还关心日志发生的位置,如文件名、行号、类名和函数名。在此可以参考博文“C/C++语言中特定的宏”,通过调用宏“__FILE__”、“__LINE__”、“__FUNCTION__”,来自动获取日志发生时的位置,而不是采用硬编码的方式。
对于类名,在QT中可以通过元编程的方式获得,如下:
参考《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。
众所周知,在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。
相关文章推荐
- 第一章:C语言数据类型
- C语言-----循环单链表
- PVS-Studio C/C++/C++11 静态代码分析工具
- 线程安全的的map-CSuperMap
- 《C++编程规范:101条规则、准则与最佳实践》学习笔记
- vs2012编译c语言-指针不能判NULL
- 迷宫最少步数的求解(利用队列)
- 《C++必知必会》学习笔记
- C++笔记(8) template partitial specialization模板偏特化
- c++实现封装socket2
- 顺序队的基本操作(C++完整代码)
- C语言常见命名规范
- C++学习中关于cout遇到的一个小问题
- c++调试重要武器GetLastError
- C语言 side effect和sequence point
- vector 中生成一大批线程并等待完成
- 【转】使用C语言实现MD5消息摘要算法
- 【C语言】 浅谈指针
- c\c++ codeblocks编译常见错误
- C\C++代码优化的27个建议