用C 实现一个Log系统

发表于2017-09-08
评论0 1.5k浏览

提要

最近在写一些C 的图形代码,在调试和测试过程中都会需要在终端打印一些信息出来。之前的做法是直接用

  1. std::cout<<"Some Word"<<std::endl;  < span=""></std::endl;  <>

这样做其实非常的麻烦,每次都要打很多的字母还有特殊符号,除去我要打印的内容,还需要按下28下键盘,简直不能忍!

参考Unity里面的打log的方式

  1. Debug.Log("Some Word");  

或者Qt中的处理方式copy

  1. 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的流程如下图


测试代码

  1. void DebugTest()  
  2. {  
  3.     Vector2 v = Vector2(1, 1);  
  4.     Vector2 v2 = Vector2(2, 1);  
  5.     Vector3 v3 = Vector3(0, 2, 1);  
  6.     Vector3 v4 = Vector3(0, 2, 1);  
  7.     Vector3 v5 = Vector3(23, 112, 22);  
  8.     Vector3 v6 = Vector3(23, 112, 22);  
  9.     std::vector vec;  
  10.     vec.push_back(v3);  
  11.     vec.push_back(v4);  
  12.     vec.push_back(v5);  
  13.     vec.push_back(v6);  
  14.     vec.push_back(v6);  
  15.     vec.push_back(v6);  
  16.     vec.push_back(v6);  
  17.     vec.push_back(v6);  
  18.     std::string testStr = "vector Test";  
  19.     qDebug() << "Hello Debug";  
  20.     qDebug() <<""<< v << v2<< v3;  
  21.     qDebug() << v3;  
  22.     qWarning() << vec;  
  23. }  

运行结果





代码清单

MessageLogContext.h
  1. #pragma once  
  2. #include   
  3.   
  4. class MessageLogContext  
  5. {  
  6. public:  
  7.     MessageLogContext() : line(0), file(0), function(0) {}  
  8.     MessageLogContext(const char *fileName, const char *functionName, int lineNumber)  
  9.         : file(fileName), function(functionName), line(lineNumber) {}  
  10.   
  11.     int line;  
  12.     const char *file;  
  13.     const char *function;  
  14.     void copy(const MessageLogContext &logContext)  
  15.     {  
  16.         this->file = logContext.file;  
  17.         this->line = logContext.line;  
  18.         this->function = logContext.function;  
  19.     }  
  20.   
  21. private:  
  22.     friend class MessageLogger;  
  23.     friend class Debug;  
  24. };  


Log.h
  1. #pragma once  
  2. #define qDebug MessageLogger(__FILE__, __FUNCTION__, __LINE__).debug  
  3. #define qInfo MessageLogger(__FILE__, __FUNCTION__, __LINE__).info  
  4. #define qWarning MessageLogger(__FILE__, __FUNCTION__, __LINE__).warning  
  5. #define qCritical MessageLogger(__FILE__, __FUNCTION__, __LINE__).critical  
  6. #define qFatal MessageLogger(__FILE__, __FUNCTION__, __LINE__).fatal  
  7. #include "Debug.h"  
  8. #include "MessageLogContext.h"  
  9.   
  10. class MessageLogger  
  11. {  
  12. public:  
  13.     MessageLogger() : context(){}  
  14.     MessageLogger(const char *fileName, const char *functionName, int lineNumber)  
  15.         : context(fileName, functionName, lineNumber) {}  
  16.   
  17.     Debug info() const;  
  18.     Debug warning() const;  
  19.     Debug critical() const;  
  20.     Debug debug() const;  
  21.   
  22. protected:  
  23. private:  
  24.     MessageLogContext context;  
  25. };  




Log.cpp
  1. #include "Log.h"  
  2.   
  3.   
  4. Debug MessageLogger::debug() const  
  5. {  
  6.     std::string debug = "debug";  
  7.     Debug dbg = Debug(&debug);  
  8.     MessageLogContext &ctxt = dbg.stream->context;  
  9.     ctxt.copy(context);  
  10.     dbg.stream->logType = Info;  
  11.     return dbg;  
  12. }  
  13.   
  14. Debug MessageLogger::info() const  
  15. {  
  16.     Debug dbg = Debug();  
  17.     MessageLogContext &ctxt = dbg.stream->context;  
  18.     ctxt.copy(context);  
  19.     dbg.stream->logType = Info;  
  20.     return dbg;  
  21. }  
  22.   
  23. Debug MessageLogger::warning() const  
  24. {  
  25.     Debug dbg = Debug();  
  26.     MessageLogContext &ctxt = dbg.stream->context;  
  27.     ctxt.copy(context);  
  28.     dbg.stream->logType = Warning;  
  29.     return dbg;  
  30. }  
  31.   
  32. Debug MessageLogger::critical() const  
  33. {  
  34.     Debug dbg = Debug();  
  35.     MessageLogContext &ctxt = dbg.stream->context;  
  36.     ctxt.copy(context);  
  37.     dbg.stream->logType = Error;  
  38.     return dbg;  
  39. }  



Debug.h copy
#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
[cpp] view plain copy
#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);  
}  

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引