使用Line Pos Info 和 Modern C++ 改进打印日志记录
使用Line Pos Info 和 Modern C++ 改进打印日志记录
使用跟踪值:不管自己是多么的精通,可能仍然使用调试的主要方法之一 printf , TRaCE, outputDebugString, 等…然后扫描输出, 同时调试。
添加有关行号和日志消息来源的文件的信息是一种非常有效的方法,可以为你节省大量时间,在这篇文章将描述一个在visual Studio中特别有用的技巧,在其他IDE/编译器中有所帮助。
还将展示现在C++和C++20如何使代码更好。
常规
在调试C++代码时,将值输出到控制台或输出到窗口并扫描日志非常方便。
std::cout << "myval: " << val << endl;
可以通过添加LINE和FILE信息轻松增强此技术,这样就可以看到那条消息的来源。扫描大量日志时,这可能非常方便。
为什么会这么重要呢?就每个人来说,当我们试图查找某些日志输出的来源时,我们已经失去了很多时间。当我们看到一条消息时,我们会复制他,搜索解决方案,然后通常在滚动后终于找到了正确的代码行。那么有没有更为简便的方法呢?
将使用“标准”C++实现此代码,然后转到现代C++,最后看看C++20将会发生什么?
log4cpp 中添加需要打印的行号,文件名,函数名 (标准C++)
使用log4cpp中的Category的debug, error, info , warn 等方法输出,需要调试的信息:
root.debug(“Message”);
但最后用宏包装上面的函数:
#define logWarn(msg)\ my_logWarn(__LINE__,__FILE__,__FUNCTION__,msg)
通过logWarn(msg)使用;
上面代码调用logWarn(msg)内部调用的函数my_llogWarn。
为什么定义一个宏?当然,方便。否则,必须手动传递行号和文件名。无法在内部获取文件和行,my_logWarn 因为它始终指向实现的源代码my_logWarn而不是调用它的代码。
什么是 __ FILE __ 和 __ LINE __ ? 在vistual Studio 中,这下可以在代码中使用的预定义宏。顾名思义,他们会扩展到源代码的文件名和给定翻译单元中的确定行。要控制 __ FILE __ 宏, 可以使用编译器选项 /FC 。该选项使文件名更长(完整路径)或更短(相对于解决方案目录),请注意,/FC使用“编译并继续“时暗示。
注意, __ FILE __ 并且 __ LINE __ 也由标准制定,因此其他编译器也应该实现它。
void my_logWarn(int line,const char *filename,const char *funcName, const char *msg) { Mylog *log = Mylog::getInstance(); string tempMsg = msg; tempMsg = tempMsg + " doucument name:" + std::string(filename) + " this is line: " + std::to_string(line) + " func name: " + string(funcName); log->Warn(tempMsg.c_str()); }
C++ 20
在C++20中有std::source_location
新的库类型声明如下:
struct source_location{ static constexpr source_location current () noexcept; constexpr uint_lest32_t line() const noexcept; constexpr uint_lest32_t column() const noexcept; constexpr const char * filename() const noexcept; constexpr const char *function_name() const noexcept; }
一个基本的使用例子:
#include <iostream> #include <string_view> #include <experimental/source_location> using namespace std; using namespace std::experimental; void log(const string_view& message, const source_location& location = source_location::current()) { std::cout << "info:" << location.file_name() << ":" << location.line() << " " << location.function_name() << " " << message << '\n'; } int main() { log("Hello world!"); // another log log("super extra!"); }
总结
简单介绍了增强的printf样式和日志记录
起初,使用的主要是C风格的标准代码,后来使用了现代C++进行更新,最后了使用了C++20中的source_location引入的新类型实现。
随着soure_location使我们可以跳过使用__ FILE __ 和 __ LINE __ 预定义宏,不过,日志宏(#define Log(…)) 是有帮助的,因为他可以隐藏于位置信息缺省参数。
base C++:
#pragma once #define logInfo(msg)\ my_logInfo(__LINE__,__FILE__,__FUNCTION__,msg) #define logWarn(msg)\ my_logWarn(__LINE__,__FILE__,__FUNCTION__,msg) #define logError(msg)\ my_logError(__LINE__,__FILE__,__FUNCTION__,msg) #define logDebug(msg)\ my_logDebug(__LINE__,__FILE__,__FUNCTION__,msg) #include <stdlib.h> #include <iostream> #include <string> using std::cout; using std::endl; using std::string; #include <log4cpp/Category.hh> #include <log4cpp/Priority.hh> #include <log4cpp/FileAppender.hh> #include <log4cpp/RollingFileAppender.hh> #include <log4cpp/OstreamAppender.hh> #include <log4cpp/PatternLayout.hh> using namespace log4cpp; class Mylog { public: void Warn(const char *msg); void Error(const char *msg); void Debug(const char * msg); void Info(const char * msg); static Mylog * getInstance() { if(_pInstance == nullptr){ _pInstance = new Mylog(); } return _pInstance; } static void destory() { if(_pInstance) { Category::shutdown(); } } private: Mylog():root(Category::getRoot()){ PatternLayout * ptnLayout1 = new PatternLayout(); ptnLayout1->setConversionPattern("%d [%p] %m%n"); PatternLayout * ptnLayout2 = new PatternLayout(); ptnLayout2->setConversionPattern("%d [%p] %m%n"); PatternLayout * ptnLayout3 = new PatternLayout(); ptnLayout3->setConversionPattern("%d [%p] %m%n"); OstreamAppender * ostreamAppender = new OstreamAppender("ostreamAppender",&cout); ostreamAppender->setLayout(ptnLayout1); FileAppender *fileAppender = new FileAppender("fileAppender","davarain.log"); fileAppender->setLayout(ptnLayout2); RollingFileAppender * rollingAppender = new RollingFileAppender("rollingAppender","rollingAppender.log",5*1024,2); rollingAppender->setLayout(ptnLayout3); root.setAppender(ostreamAppender); root.addAppender(fileAppender); root.addAppender(rollingAppender); root.setPriority(Priority::DEBUG); cout << "Mylog()" << endl; } ~Mylog() { cout << "~Mylog()" << endl; } private: Category & root; static Mylog * _pInstance; }; Mylog * Mylog::_pInstance = nullptr; void Mylog::Info(const char *msg) { root.info(msg); } void Mylog::Warn(const char *msg) { root.warn(msg); } void Mylog::Debug(const char *msg) { root.debug(msg); } void Mylog::Error(const char *msg) { root.error(msg); } void my_logInfo(int line, const char *filename, const char *funcName, const char *msg) { Mylog *log = Mylog::getInstance(); string tempMsg = msg; tempMsg = tempMsg + " doucument name:" + std::string(filename) + " this is line: " + std::to_string(line) + " func name: " + string(funcName); log->Info(tempMsg.c_str()); } void my_logError(int line,const char *filename,const char *funcName, const char *msg) { Mylog *log = Mylog::getInstance(); string tempMsg = msg; tempMsg = tempMsg + " doucument name:" + std::string(filename) + " this is line: " + std::to_string(line) + " func name: " + string(funcName); log->Error(tempMsg.c_str()); } void my_logWarn(int line,const char *filename,const char *funcName, const char *msg) { Mylog *log = Mylog::getInstance(); string tempMsg = msg; tempMsg = tempMsg + " doucument name:" + std::string(filename) \ + " this is line: " + std::to_string(line) + " func name: " + string(funcName); log->Warn(tempMsg.c_str()); void my_logDebug(int line, const char *filename, const char *funcName,const char *msg) { Mylog *log = Mylog::getInstance(); string tempMsg = msg; tempMsg = tempMsg + " doucument name:" + std::string(filename) + " this is line: " + std::to_string(line) + " func name: " + string(funcName); log->Debug(tempMsg.c_str()); }
C++20:
#pragma once /* #define logInfo(msg) \ my_logInfo(__LINE__,__FILE__,__FUNCTION__,msg) #define logWarn(msg)\ my_logWarn(__LINE__,__FILE__,__FUNCTION__,msg) #define logError(msg)\ my_logError(__LINE__,__FILE__,__FUNCTION__,msg) #define logDebug(msg)\ my_logDebug(__LINE__,__FILE__,__FUNCTION__,msg) */ #include <stdlib.h> #include <iostream> #include <string> #include <experimental/source_location> using std::cout; using std::endl; using std::string; using namespace std::experimental; #include <log4cpp/Category.hh> #include <log4cpp/Priority.hh> #include <log4cpp/FileAppender.hh> #include <log4cpp/RollingFileAppender.hh> #include <log4cpp/OstreamAppender.hh> #include <log4cpp/PatternLayout.hh> using namespace log4cpp; class Mylog { public: void Warn(const char *msg); void Error(const char *msg); void Debug(const char * msg); void Info(const char * msg); static Mylog * getInstance() { if(_pInstance == nullptr){ _pInstance = new Mylog(); } return _pInstance; } static void destory() { if(_pInstance) { Category::shutdown(); } } private: Mylog():root(Category::getRoot()){ PatternLayout * ptnLayout1 = new PatternLayout(); ptnLayout1->setConversionPattern("%d [%p] %m%n"); PatternLayout * ptnLayout2 = new PatternLayout(); ptnLayout2->setConversionPattern("%d [%p] %m%n"); PatternLayout * ptnLayout3 = new PatternLayout(); ptnLayout3->setConversionPattern("%d [%p] %m%n"); OstreamAppender * ostreamAppender = new OstreamAppender("ostreamAppender",&cout); ostreamAppender->setLayout(ptnLayout1); FileAppender *fileAppender = new FileAppender("fileAppender","davarain.log"); fileAppender->setLayout(ptnLayout2); RollingFileAppender * rollingAppender = new RollingFileAppender("rollingAppender","rollingAppender.log",5*1024,2); rollingAppender->setLayout(ptnLayout3); root.setAppender(ostreamAppender); root.addAppender(fileAppender); root.addAppender(rollingAppender); root.setPriority(Priority::DEBUG); cout << "Mylog()" << endl; } ~Mylog() { cout << "~Mylog()" << endl; } private: Category & root; static Mylog * _pInstance; }; Mylog * Mylog::_pInstance = nullptr; void Mylog::Info(const char *msg) { root.info(msg); } void Mylog::Warn(const char *msg) { root.warn(msg); } void Mylog::Debug(const char *msg) { root.debug(msg); } void Mylog::Error(const char *msg) { root.error(msg); } void logInfo(const char *msg,const source_location& location= source_location::current()) { Mylog *log = Mylog::getInstance(); string tempMsg = msg; tempMsg = tempMsg + " doucument name:" + location.file_name() + " this is line: " + std::to_string(location.line()) + " func name: " + location.function_name(); log->Info(tempMsg.c_str()); } void logError(const char *msg, const source_location& location = source_location::current()) { Mylog *log = Mylog::getInstance(); string tempMsg = msg; tempMsg = tempMsg + " doucument name:" + location.file_name() + " this is line: " + std::to_string(location.line()) + " func name: " + location.function_name(); log->Error(tempMsg.c_str()); } void logWarn(const char *msg,const source_location& location = source_location::current()) { Mylog *log = Mylog::getInstance(); string tempMsg = msg; tempMsg = tempMsg + " doucument name:" + location.file_name() + " this is line: " + std::to_string(location.line()) + " func name: " + location.function_name(); log->Warn(tempMsg.c_str()); } void logDebug(const char *msg,const source_location& location = source_location::current()) { Mylog *log = Mylog::getInstance(); string tempMsg = msg; tempMsg = tempMsg + " doucument name:" + location.file_name() + " this is line: " + std::to_string(location.line()) + " func name: " + location.function_name(); log->Debug(tempMsg.c_str()); }