提要
最近在寫一些C++的圖形代碼,在調(diào)試和測試過程中都會需要在終端打印一些信息出來,
用C++實現(xiàn)一個Log系統(tǒng)
。之前的做法是直接用std::cout<<some pre=""></p><p> 這樣做其實非常的麻煩,每次都要打很多的字母還有特殊符號,除去我要打印的內(nèi)容,還需要按下28下鍵盤,簡直不能忍!</p><p> 參考Unity里面的打log的方式</p>或者Qt中的處理方式
qDebug() << Some Word;這兩種都方便太多。
今天要實現(xiàn)的Log系統(tǒng)需要滿足的特性有:
1.很方便地在終端打印各種類型數(shù)據(jù)信息;
2.可以區(qū)分Log等級;
3.打印信息的同時能夠提供打印語句的文件,函數(shù)名,行號
類說明
簡單地畫了下UML,主要分為下面幾個類
簡單說一下類的作用
MessageLogContext
記錄Log的上下文,也就是Log處在的文件,函數(shù)名,行號。
MessageLogger
主要的Log類,提供了上次調(diào)用的一些接口,注意一下這個宏比較有意思
qDebug MessageLogger(__FILE__, __FUNCTION__, __LINE__).debug
這樣當(dāng)使用
qDebug()
的時候,
宏替換就直接轉(zhuǎn)換成了MessageLogger的構(gòu)造函數(shù)
MessageLogger(__FILE__, __FUNCTION__, __LINE__).debug()
等于是先構(gòu)造MessageLogger,然后調(diào)用這個對象的debug()方法。
Debug
具體處理Debug信息的類。
用了一個內(nèi)部Stream結(jié)構(gòu)體來記錄Debug信息,記得在使用前要new,析構(gòu)的時候delete掉。
重構(gòu)了很多的<<方法,就是為了能處理多種數(shù)據(jù)類型,包括自定義的類。還可以通過模板來打印stl里面的東西。
LogToConsole是將log信息打印到終端的函數(shù),在析構(gòu)函數(shù)中會被調(diào)用。如果想要實現(xiàn)更加炫酷的打印log方式(各種顏色),擴(kuò)展這個函數(shù)就好了。
整個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>運(yùn)行結(jié)果
代碼清單
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);}