最近在寫一些C++的圖形代碼,在調試和測試過程中都會需要在終端打印一些信息出來。之前的做法是直接用
std::cout<
這樣做其實非常的麻煩,每次都要打很多的字母還有特殊符號,除去我要打印的內容,還需要按下28下鍵盤,簡直不能忍!
參考Unity裡面的打log的方式
Debug.Log(Some Word);
或者Qt中的處理方式
qDebug() << Some Word;
這兩種都方便太多。
今天要實現的Log系統需要滿足的特性有:
1.很方便地在終端打印各種類型數據信息;
2.可以區分Log等級;
3.打印信息的同時能夠提供打印語句的文件,函數名,行號
類說明
簡單地畫了下UML,主要分為下面幾個類
簡單說一下類的作用
MessageLogContext
記錄Log的上下文,也就是Log處在的文件,函數名,行號。
MessageLogger
主要的Log類,提供了上次調用的一些接口,注意一下這個宏比較有意思
qDebug MessageLogger(__FILE__, __FUNCTION__, __LINE__).debug
這樣當使用
qDebug()
的時候,
宏替換就直接轉換成了MessageLogger的構造函數
MessageLogger(__FILE__, __FUNCTION__, __LINE__).debug()
等於是先構造MessageLogger,然後調用這個對象的debug()方法。
Debug
具體處理Debug信息的類。
用了一個內部Stream結構體來記錄Debug信息,記得在使用前要new,析構的時候delete掉。
重構了很多的<<方法,就是為了能處理多種數據類型,包括自定義的類。還可以通過模板來打印stl裡面的東西。
LogToConsole是將log信息打印到終端的函數,在析構函數中會被調用。如果想要實現更加炫酷的打印log方式(各種顏色),擴展這個函數就好了。
整個Log的流程如下圖
測試代碼
void DebugTest() { Vector2 v = Vector2(1, 1); Vector2 v2 = Vector2(2, 1); Vector3 v3 = Vector3(0, 2, 1); Vector3 v4 = Vector3(0, 2, 1); Vector3 v5 = Vector3(23, 112, 22); Vector3 v6 = Vector3(23, 112, 22); std::vectorvec; vec.push_back(v3); vec.push_back(v4); vec.push_back(v5); vec.push_back(v6); vec.push_back(v6); vec.push_back(v6); vec.push_back(v6); vec.push_back(v6); std::string testStr = vector Test; qDebug() << Hello Debug; qDebug() <<<< v << v2<< v3; qDebug() << v3; qWarning() << vec; }
運行結果
代碼清單
MessageLogContext.h
#pragma once #includeclass MessageLogContext { public: MessageLogContext() : line(0), file(0), function(0) {} MessageLogContext(const char *fileName, const char *functionName, int lineNumber) : file(fileName), function(functionName), line(lineNumber) {} int line; const char *file; const char *function; void copy(const MessageLogContext &logContext) { this->file = logContext.file; this->line = logContext.line; this->function = logContext.function; } private: friend class MessageLogger; friend class Debug; };
Log.h#pragma once #define qDebug MessageLogger(__FILE__, __FUNCTION__, __LINE__).debug #define qInfo MessageLogger(__FILE__, __FUNCTION__, __LINE__).info #define qWarning MessageLogger(__FILE__, __FUNCTION__, __LINE__).warning #define qCritical MessageLogger(__FILE__, __FUNCTION__, __LINE__).critical #define qFatal MessageLogger(__FILE__, __FUNCTION__, __LINE__).fatal #include Debug.h #include MessageLogContext.h class MessageLogger { public: MessageLogger() : context(){} MessageLogger(const char *fileName, const char *functionName, int lineNumber) : context(fileName, functionName, lineNumber) {} Debug info() const; Debug warning() const; Debug critical() const; Debug debug() const; protected: private: MessageLogContext context; };
Log.cpp
#include Log.h Debug MessageLogger::debug() const { std::string debug = debug; Debug dbg = Debug(&debug); MessageLogContext &ctxt = dbg.stream->context; ctxt.copy(context); dbg.stream->logType = Info; return dbg; } Debug MessageLogger::info() const { Debug dbg = Debug(); MessageLogContext &ctxt = dbg.stream->context; ctxt.copy(context); dbg.stream->logType = Info; return dbg; } Debug MessageLogger::warning() const { Debug dbg = Debug(); MessageLogContext &ctxt = dbg.stream->context; ctxt.copy(context); dbg.stream->logType = Warning; return dbg; } Debug MessageLogger::critical() const { Debug dbg = Debug(); MessageLogContext &ctxt = dbg.stream->context; ctxt.copy(context); dbg.stream->logType = Error; return dbg; }
Debug.h
#pragma once #include#include #include #include #include #include #include #include Math/Vector2.h #include Math/Vector3.h #include //#include Log.h #include MessageLogContext.h enum LogType { Info, Warning, Error, Default, }; class Debug { public: struct Stream { Stream():ss(), space(true), context() {} Stream(std::string *s) :ss(*s), space(true), context(){} std::ostringstream ss; bool space; MessageLogContext context; LogType logType; } *stream; Debug() : stream(new Stream()) {} inline Debug(std::string *s) : stream(new Stream(s)) {} ~Debug(); inline Debug &operator<<(bool t) { stream->ss<<(t ? true : false); return maybeSpace(); } inline Debug &operator<<(char t) { stream->ss<< t; return maybeSpace(); } inline Debug &operator<<(signed short t) { stream->ss << t; return maybeSpace(); } inline Debug &operator<<(unsigned short t) { stream->ss << t; return maybeSpace(); } inline Debug &operator<<(std::string s) { stream->ss << s; return maybeSpace(); } inline Debug &operator<<(const char* c) { stream->ss << c; return maybeSpace(); } inline Debug &operator<<(Vector2 vec) { stream->ss << ( << vec.x <<,<< vec.y<<); return maybeSpace(); } inline Debug &operator<<(Vector3 vec) { stream->ss << ( << vec.x << , << vec.y <<, << vec.z << ); return maybeSpace(); } inline Debug &space() { stream->space = true; stream->ss << ' '; return *this; } inline Debug &nospace() { stream->space = false; return *this; } inline Debug &maybeSpace() { if (stream->space) stream->ss << ' '; return *this; } template inline Debug &operator<<(const std::vector &vec) { stream->ss << '('; for (int i = 0; i < vec.size(); ++i) { stream->ss << vec.at(i); stream->ss << , ; } stream->ss << ')'; return maybeSpace(); } void LogToConsole(LogType type, const MessageLogContext &context, std::string logBuffer); private: static Debug* _instance; };
Debug.cpp
#include Debug.h Debug::~Debug() { LogToConsole(stream->logType, stream->context, stream->ss.str()); delete stream; } void Debug::LogToConsole(LogType type, const MessageLogContext &context, std::string logBuffer) { std::string logString; switch (type) { case Error: logString.append(Error! ); break; case Info: //logString.append(); break; case Warning: logString.append(Warning! ); break; default: break; } logString.append(logBuffer); logString.append(......); logString.append(context.file); logString.append( ); logString.append(context.function); logString.append(()); std::cout << logString << line: << context.line << << std::endl; //logString.append(context.line); }