用C 实现一个Log系统
发表于2017-09-08
提要
最近在写一些C 的图形代码,在调试和测试过程中都会需要在终端打印一些信息出来。之前的做法是直接用
- std::cout<<"Some Word"<<std::endl; < span=""></std::endl; <>
这样做其实非常的麻烦,每次都要打很多的字母还有特殊符号,除去我要打印的内容,还需要按下28下键盘,简直不能忍!
参考Unity里面的打log的方式
- Debug.Log("Some Word");
或者Qt中的处理方式copy
- 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
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
- 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 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);
}
