提要
最近在寫一些C++的圖形代碼,在調試和測試過程中都會需要在終端打印一些信息出來,用C++實現一個Log系統。之前的做法是直接用
std::cout<<some pre=""></p><p> 這樣做其實非常的麻煩,每次都要打很多的字母還有特殊符號,除去我要打印的內容,還需要按下28下鍵盤,簡直不能忍!</p><p> 參考Unity里面的打log的方式</p>或者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::vector<vector3>vec;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;}</vector3>運行結果
代碼清單MessageLogContext.h
#pragma once#include<string>class 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;};</string>
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.hclass 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.hDebug 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<iostream>#include<iomanip>#include<fstream>#include<string>#include<cstdlib>#include<stdint.h>#include<sstream>#include Math/Vector2.h #include Math/Vector3.h #include<vector>//#include Log.h#include MessageLogContext.henum 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<typename t="">inline Debug &operator<<(const std::vector<t>&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;};</t></typename></vector></sstream></stdint.h></cstdlib></string></fstream></iomanip></iostream>
Debug.cpp#include Debug.hDebug::~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);}