IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [原]用C++实现一个Log系统

    qp120291570发表于 2015-12-13 01:14:43
    love 0

    提要

    最近在写一些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;
    }

    运行结果





    代码清单

    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;
    };
    


    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 <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;
    };
    


    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);
    }



    参考

    Qt source code

    Qt Documentation http://doc.qt.io/qt-4.8/qdebug.html

    http://www.cplusplus.com/



沪ICP备19023445号-2号
友情链接