用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 <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);
}
{
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/
用C++实现一个Log系统的更多相关文章
- 自己封装一个Log模块
Unity自己有log系统,为什么要自己封装一个 1.不好用,只能在pc上记录log文件,移动平台是没有的 2.在开发时期的log,不想在正式版里面出现.没有一个统一的开关来控制是不是要显示log,要 ...
- 使用monit搭建一个监控系统
上周用monit搭建或者说定制了一个监控系统,来监控服务器发生事情.当然了主要是监控异常,因为我们的产品属于服务器类型,很多进程都daemon,要不停的运行.我们搭建监控目的不过是出现问题能够及时的知 ...
- 超强教程:如何搭建一个 iOS 系统的视频直播 App?
现今,直播市场热火朝天,不少人喜欢在手机端安装各类直播 App,便于随时随地观看直播或者自己当主播.作为开发者来说,搭建一个稳定性强.延迟率低.可用性强的直播平台,需要考虑到部署视频源.搭建聊天室.优 ...
- Java核心知识点学习----线程中如何创建锁和使用锁 Lock,设计一个缓存系统
理论知识很枯燥,但这些都是基本功,学完可能会忘,但等用的时候,会发觉之前的学习是非常有意义的,学习线程就是这样子的. 1.如何创建锁? Lock lock = new ReentrantLock(); ...
- [km] 如何判断一个直播系统是否使用的是RTMP
如何判断一个直播系统是否使用的是RTMP from: http://peiqiang.net/2016/03/21/how-to-judge-whether-rtmp-is-used-by-a-liv ...
- log4j:ERROR setFile(null,true) call failed.java.io.FileNotFoundException: ..\logs\2010-1-19.log (系统找不到指定的路径。)
log4j:ERROR setFile(null,true) call failed.java.io.FileNotFoundException: ..\logs\2010-1-19.log (系统找 ...
- 如何设计一个RPC系统
版权声明:本文由韩伟原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/162 来源:腾云阁 https://www.qclou ...
- petshop4.0 具体解释之中的一个(系统架构设计)
前言:PetShop是一个范例,微软用它来展示.Net企业系统开发的能力.业界有很多.Net与J2EE之争,很多数据是从微软的PetShop和Sun的PetStore而来.这样的争论不可避免带有浓厚的 ...
- 架构漫谈:自己开发一个Log框架
前言 在日常开发中我们常常都会用到写日志的功能,现在网上的写Log的框架有很多,但是对于我个人而言,过于庞大:我们往往只为了使用框架中的某一个功能就不得不引用整个框架. 所以,我们今天就来自己动手开发 ...
随机推荐
- cache支持三种pre-fetch方式:normal/pre-fetch1/pre-fetch2-way1/pre-fetch-way2
1.normal fetch ----fetch 1 cache line once 2. pre-fetch mode one ---- fetch 3 cache line once 3.pre ...
- Objective-C 正则表达式使用(1)
学习了一下OC的正则表达式备忘一下 使用正则表达式的步骤: 创建一个一个正则表达式对象:定义规则. 利用正则表达式对象测试,相应的字符串. NSString *userName = @"12 ...
- Python中的序列化以及pickle和json模块介绍
Python中的序列化指的是在程序运行期间,变量都是在内存中保存着的,如果我们想保留一些运行中的变量值,就可以使用序列化操作把变量内容从内存保存到磁盘中,在Python中这个操作叫pickling,等 ...
- 算法学习记录-图——应用之拓扑排序(Topological Sort)
这一篇写有向无环图及其它的应用: 清楚概念: 有向无环图(DAG):一个无环的有向图.通俗的讲就是从一个点沿着有向边出发,无论怎么遍历都不会回到出发点上. 有向无环图是描述一项工程或者系统的进行过程的 ...
- skkyk:题解 洛谷P3865 【【模板】ST表】
我不会ST表 智推推到这个题 发现标签中居然有线段树..? 于是贸然来了一发线段树 众所周知,线段树的查询是log(n)的 题目中"请注意最大数据时限只有0.8s,数据强度不低,请务必保证你 ...
- div+css居中
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- C/C++的类型安全
类型安全很大程度上可以等价于内存安全,类型安全的代码不会试图访问自己没被授权的内存区域.“类型安全”常被用来形容编程语言,其根据在于该门编程语言是否提供保障类型安全的机制:有的时候也用“类型安全”形容 ...
- 【LeetCode】Same Tree(相同的树)
这道题是LeetCode里的第100道题. 这是题目: 给定两个二叉树,编写一个函数来检验它们是否相同. 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的. 示例 1: 输入: 1 1 ...
- 【转】Python3学习笔记(urllib模块的使用)
原文地址:https://www.cnblogs.com/Lands-ljk/p/5447127.html 1.基本方法 urllib.request.urlopen(url, data=None, ...
- Oracle 数据库有五个必需的后台进程,DBWR,LGWR,CKPT,SMON,PMON
SMON 是系统监视器(System Monitor)的缩写.如果Oracle实例失败,则在SGA中的任何没有写到磁盘中的数据都会丢失.有许多情况可能引起Oracle实例失败,例如,操作系统的崩溃就会 ...