1.项目介绍
本项目就是一个日志器系统,目的就是方便程序员在代码开发中进行日志信息的获取,调试代码定位错误排查问题
这个项目主要支持以下几个功能:
多级别日志消息(可以控制日志输出等级)
同步于异步日志
多种落地方向 (如标准输出,指定文件,以大小自动切换的文件) 可扩展
支持多线程写日志
用到的核心技术:
分模块叙述:
项目源代码
2.开发环境
• CentOS 7
• vscode
• g++/gdb
• Makefile
3.核心技术
4.日志器模块
1.日志等级模块:定义日志的等级 等级有大小区分 可以做到限制等级输出 开发者根据日志等级可以针对性的解决和定位问题
本日志器共分为五个等级
DEBUG, 调试消息
INFO, 消息
WARNING, 警告
ERROR, 发生错误
FATAL, 发生致命错误
OFF 关闭所以日志输出
2.日志消息模块:一条日志所需要的的各种信息总和 类
类中含有 日志等级 时间 线程id 文件名 行号 日志器名称 消息主体
3.日志器消息格式模块:对日志器的日志消息输出格式,得到字符串类型的整体日志消息 我们也缺省提供了一个默认的格式
格式化流程: 我们对每种消息都有对应的格式控制(还是需要使用者看一下使用方法)
%+字符对应一种类型信息 我们对每个格式都有对应的函数 以将日志消息中的对应信息转换为字符串(这里大量使用了继承和多态) 使用时需要传入对应格式的字符串, 我们会对其进行切割 按顺序将对应的格式对应处理方法放到一个vector管理起来 在输出时只需要遍历vector调用方法就可以得到对应的字符串
各种字符代表的消息
%d ⽇期 %T 缩进 %t 线程id %p ⽇志级别 %c ⽇志器名称
%f ⽂件名 %l ⾏号 %m ⽇志消息 %n 换⾏
4.日志器落地模块:将日志消息落地到对应的文件 本模块默认支持三种落地模式 可扩展
采用到了简单工厂模式
将日志器消息格式模块得到的字符串数据写到文件中
默认支持三种方式
标准输出 指定文件 根据文件大小滚动切换文件
5.异步线程模块:异步日志器需要这个模块
异步模式 主线程将得到的字符串交给异步线程 由异步线程调用落地函数
这里的消息传递采用双缓冲区的生产消费模型 实现了读写分离
一个写入缓冲区 由主线程写入 一个落地缓冲区 异步线程将数据取出并写入文件
双缓冲区的应用:
双缓冲区的好处:
提高了并发读写的效率 在多线程情况下 读写操作存在竞争的 读写分离减少了竞争 仅在两个缓冲区交数据的情况下存在线程互斥
提高了数据安全性 避免了读写操作交叉 从而保证了安全性
提高系统稳定性 减少读写阻塞 提高系统稳定性
相较于环形队列
环形队列同时读取 需要相对较平衡的生产和消费 才能高效 节约了空间 内存反复使用
双缓冲区适用于双方速率不一致 消耗空间
当写缓冲区所以数据都写入完毕就交换讲个缓冲区 这样只有两个缓冲区交换时才需要加锁
6.日志器模块:对以上模块的整合
采用建造者模式
日志器分为同步,异步(安全,非安全) 这些日志器有分为 本地,全局
得到日志消息 建造日志消息对象 得到日志消息的字符串 再调用落地模块
异步模式需要调用异步线程模块
实现流程:由工作线程将日志信息交给异步线程 由异步线程实现落地
全局模式下的日志器都要放进日志器管理模块
7.日志器管理模块:管理全局的日志器
采用了单例模式
将全局模式的日志器都记录下来 再上层调用的时候根据日志器名调用对应日志器
8.缓冲区模块
与异步线程的信息交换
维护一片缓冲区 拥有数据写入和取出方法 安全模式下不扩容 极限测试模式下可以进行扩容
9.任志宏&全局接口模块
为了方便用户使用 还需要使用宏封装一个默认的日志器 根据等级直接落地日志
采用代理模式简化用户使用函数的成本 用户只需要传入日志消息本体就行
设计模式
4.1设计模式是什么
设计模式是一种解决方案,他不是语法规定,是一些提高代码开发效率,代码复用性,健壮性,可读性等等的一种方法。
4.2六大原则
单一职责原则:类的职责单一,尽量让一个类中实现的功能的是相似的。
开闭原则:对扩展开放,对修改封闭,需要修改代码时增加接口。
里氏替换原则:子类必须对继承于父类的所有接口进行重写,是的子类可以代替父类。
依赖倒置原则:⾼层模块不应该依赖低层模块,两者都应该依赖其抽象. 不可分割的原⼦逻辑就是低层模式,原⼦逻辑组装成的就是⾼层模块。
迪米特法则(最少知道法则):尽量减少对象之间的交互,从⽽减⼩类之间的耦合。⼀个对象应该对其他对象有最少的了解。
接口隔离原则:客⼾端不应该依赖它不需要的接⼝,类间的依赖关系应该建⽴在最⼩的接⼝上。
单例模式
即在整个程序运行期间,这个类值构建一个类对象。
该设计模式保证只有一个实例,并提供全局均可用于获取这个实例的唯一接口。
例如 开机启动配置文件的读取,本项目中的日志器管理器类就是一个单例模式。
单例模式有两种类型: 懒汉模式,饿汉模式
1.饿汉模式:项目启动直接创建对象,需要使用时直接获取
// 饿汉模式
template<typename T>
class Singleton {
private:
static Singleton _eton;
private:
Singleton(){}
~Singleton(){}
public:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static T& getInstance()
{
return _eton;
}
};
Singleton Singleton::_eton;
饿汉模式在程序运行时就要创建对象,可能会拖慢程序启动速度,在未使用的单例对象的情况下就构建了对象可能会浪费空间,但胜在其最开始就创建好了对象,所以获取对象不需要加锁 可以并发获取
2.懒汉模式:只有在第一次需要使用实例的时候才创建对象
// 懒汉模式
template <typename T>
class Singleton {
private:
Singleton(){}
~Singleton(){}
public:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static T& getInstance()
{
static Singleton _eton;//c++11以后保证了静态变量构造与析构的线程安全性 所以不用加锁了
return _eton;
}
};
工厂模式
简单工厂模式
//简单⼯⼚模式:通过参数控制可以⽣产任何产品
// 优点:简单粗暴,直观易懂。使⽤⼀个⼯⼚⽣产同⼀等级结构下的任意产品
// 缺点:
// 1. 所有东西⽣产在⼀起,产品太多会导致代码量庞⼤
// 2. 开闭原则遵循(开放拓展,关闭修改)的不是太好,要新增产品就必须修改⼯⼚⽅法。
#include
#include
#include
class Fruit {
public:
Fruit(){}
virtual void show() = 0;
};
class Apple : public Fruit {
public:
Apple() {}
virtual void show() {
std::cout << "我是⼀个苹果" << std::endl;
}
};
class Banana : public Fruit {
public:
Banana() {}
virtual void show() {
std::cout << "我是⼀个⾹蕉" << std::endl;
}
};
class FruitFactory {
public:
static std::shared_ptr<Fruit> create(const std::string &name) {
if (name == "苹果") {
return std::make_shared<Apple>();
}else if(name == "⾹蕉") {
return std::make_shared<Banana>();
}
return std::shared_ptr<Fruit>();
}
};
int main()
{
std::shared_ptr<Fruit> fruit = FruitFactory::create("苹果");
fruit->show();
fruit = FruitFactory::create("⾹蕉");
fruit->show();
return 0;
}
工厂方法模式
在简单工厂基础上,保证开闭原则,为每个方法建立一个工厂,增加方法的时候只需要增加工厂就行,但是又显得代码冗余。
class FruitFactory {
public:
virtual std::shared_ptr<Fruit> create() = 0;
};
class AppleFactory : public FruitFactory {
public:
virtual std::shared_ptr<Fruit> create() {
return std::make_shared<Apple>();
}
};
class BananaFactory : public FruitFactory {
public:
virtual std::shared_ptr<Fruit> create() {
return std::make_shared<Banana>();
}
}
抽象工厂模式 对于对象划分种类
class Factory {
public:
virtual std::shared_ptr<Fruit> getFruit(const std::string &name) = 0;
virtual std::shared_ptr<Animal> getAnimal(const std::string &name) = 0;
};
class FruitFactory : public Factory {
public:
virtual std::shared_ptr<Animal> getAnimal(const std::string &name) {
return std::shared_ptr<Animal>();
}
virtual std::shared_ptr<Fruit> getFruit(const std::string &name) {
if (name == "苹果") {
return std::make_shared<Apple>();
}else if(name == "⾹蕉") {
return std::make_shared<Banana>();
}
return std::shared_ptr<Fruit>();
}
};
class AnimalFactory : public Factory {
public:
virtual std::shared_ptr<Fruit> getFruit(const std::string &name) {
return std::shared_ptr<Fruit>();
}
virtual std::shared_ptr<Animal> getAnimal(const std::string &name) {
if (name == "⼩⽺") {
return std::make_shared<Lamp>();
}else if(name == "⼩狗") {
return std::make_shared<Dog>();
}
return std::shared_ptr<Animal>();
}
};
class FactoryProducer {
public:
static std::shared_ptr<Factory> getFactory(const std::string &name) {
if (name == "动物") {
return std::make_shared<AnimalFactory>();
}else {
return std::make_shared<FruitFactory>();
}
}
};
int main()
{
std::shared_ptr<Factory> fruit_factory = FactoryProducer::getFactory("⽔
果");
std::shared_ptr<Fruit> fruit = fruit_factory->getFruit("苹果");
fruit->show();
fruit = fruit_factory->getFruit("⾹蕉");
fruit->show();
std::shared_ptr<Factory> animal_factory = FactoryProducer::getFactory("动
物");
std::shared_ptr<Animal> animal = animal_factory->getAnimal("⼩⽺");
animal->voice();
animal = animal_factory->getAnimal("⼩狗");
animal->voice();
return 0;
}
增加方法种类时依旧违反了开闭原则
建造者模式
建造者以一个个简单的对象经过一定的顺序构建一个复杂的类对象
建造者主要包含 对象类 建造者类 指挥者类
#include
#include
/*抽象电脑类*/
class Computer {
public:
using ptr = std::shared_ptr<Computer>;
Computer() {}
void setBoard(const std::string &board) {_board = board;}
void setDisplay(const std::string &display) {_display = display;}
virtual void setOs() = 0;
std::string toString() {
std::string computer = "Computer:{\n";
computer += "\tboard=" + _board + ",\n";
computer += "\tdisplay=" + _display + ",\n";
computer += "\tOs=" + _os + ",\n";
computer += "}\n";
return computer;
}
protected:
std::string _board;
std::string _display;
std::string _os;
};
/*具体产品类*/
class MacBook : public Computer {
public:
using ptr = std::shared_ptr<MacBook>;
MacBook() {}
virtual void setOs() {
_os = "Max Os X12";
}
};
/*抽象建造者类:包含创建⼀个产品对象的各个部件的抽象接⼝*/
class Builder {
public:
using ptr = std::shared_ptr<Builder>;
virtual void buildBoard(const std::string &board) = 0;
virtual void buildDisplay(const std::string &display) = 0;
virtual void buildOs() = 0;
virtual Computer::ptr build() = 0;
};
/*具体产品的具体建造者类:实现抽象接⼝,构建和组装各个部件*/
class MacBookBuilder : public Builder {
public:
using ptr = std::shared_ptr<MacBookBuilder>;
MacBookBuilder(): _computer(new MacBook()) {}
virtual void buildBoard(const std::string &board) {
_computer->setBoard(board);
}
virtual void buildDisplay(const std::string &display) {
_computer->setDisplay(display);
}
virtual void buildOs() {
_computer->setOs();
}
virtual Computer::ptr build() {
return _computer;
}
private:
Computer::ptr _computer;
};
/*指挥者类,提供给调⽤者使⽤,通过指挥者来构造复杂产品*/
class Director {
public:
Director(Builder* builder):_builder(builder){}
void construct(const std::string &board, const std::string &display) {
_builder->buildBoard(board);
_builder->buildDisplay(display);
_builder->buildOs();
}
private:
Builder::ptr _builder;
};
int main()
{
Builder *buidler = new MackBookBuilder();
std::unique_ptr<Director> pd(new Director(buidler));
pd->construct("英特尔主板", "VOC显⽰器");
Computer::ptr computer = buidler->build();
std::cout << computer->toString();
return 0;
}