用C++实现一个Log系统
提要
近期在写一些C++的图形代码,在调试和測试过程中都会须要在终端打印一些信息出来。
之前的做法是直接用
std::cout<<"Some Word"<<std::endl;
这样做事实上非常的麻烦,每次都要打非常多的字母还有特殊符号,除去我要打印的内容。还须要按下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::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; }
执行结果
代码清单
#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; };
#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; };
#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; }
#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.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 <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;
};
#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);
}
參考
Qt source code
Qt Documentation http://doc.qt.io/qt-4.8/qdebug.html
http://www.cplusplus.com/