当前位置: 首页 > news >正文

做淘宝联盟网站要多少钱?wordpress ios客户端

做淘宝联盟网站要多少钱?,wordpress ios客户端,wordpress query,运城门户网站建设1.项目介绍 项⽬介绍 本项⽬主要实现⼀个⽇志系统#xff0c; 其主要⽀持以下功能: • ⽀持多级别⽇志消息 • ⽀持同步⽇志和异步⽇志 • ⽀持可靠写⼊⽇志到控制台、⽂件以及滚动⽂件中 • ⽀持多线程程序并发写⽇志 • ⽀持扩展不同的⽇志落地⽬标地 2.开发环境 • Cent…1.项目介绍 项⽬介绍 本项⽬主要实现⼀个⽇志系统 其主要⽀持以下功能: • ⽀持多级别⽇志消息 • ⽀持同步⽇志和异步⽇志 • ⽀持可靠写⼊⽇志到控制台、⽂件以及滚动⽂件中 • ⽀持多线程程序并发写⽇志 • ⽀持扩展不同的⽇志落地⽬标地 2.开发环境 • CentOS 7 • vscode/vim • g/gdb • Makefile 3.核心技术 • 类层次设计(继承和多态的应⽤) • C11(多线程、auto、智能指针、右值引⽤等) • 双缓冲区 • ⽣产消费模型 • 多线程 • 设计模式(单例、⼯⼚、代理、模板等) 4.环境搭建 本项⽬不依赖其他任何第三⽅库 只需要安装好CentOS/Ubuntu vscode/vim环境即可开发。 5.日志系统介绍 5.1为什么需要日志系统 • ⽣产环境的产品为了保证其稳定性及安全性是不允许开发⼈员附加调试器去排查问题 可以借助⽇志系统来打印⼀些⽇志帮助开发⼈员解决问题 • 上线客⼾端的产品出现bug⽆法复现并解决 可以借助⽇志系统打印⽇志并上传到服务端帮助开发⼈员进⾏分析 • 对于⼀些⾼频操作如定时器、⼼跳包在少量调试次数下可能⽆法触发我们想要的⾏为通过断 点的暂停⽅式我们不得不重复操作⼏⼗次、上百次甚⾄更多导致排查问题效率是⾮常低下 可以借助打印⽇志的⽅式查问题 • 在分布式、多线程/多进程代码中 出现bug⽐较难以定位 可以借助⽇志系统打印log帮助定位bug • 帮助⾸次接触项⽬代码的新开发⼈员理解代码的运⾏流程 5.2日志系统技术实现 ⽇志系统的技术实现主要包括三种类型: • 利⽤printf、std::cout等输出函数将⽇志信息打印到控制台 • 对于⼤型商业化项⽬ 为了⽅便排查问题我们⼀般会将⽇志输出到⽂件或者是数据库系统⽅便查 询和分析⽇志 主要分为同步⽇志和异步⽇志⽅式 ◦ 同步写⽇志 ◦ 异步写⽇志 5.2.1同步写日志 同步⽇志是指当输出⽇志时必须等待⽇志输出语句执⾏完毕后才能执⾏后⾯的业务逻辑语句⽇志输出语句与程序的业务逻辑语句将在同⼀个线程运⾏。每次调⽤⼀次打印⽇志API就对应⼀次系统调⽤write写⽇志⽂件。 在⾼并发场景下随着⽇志数量不断增加同步⽇志系统容易产⽣系统瓶颈 • ⼀⽅⾯⼤量的⽇志打印陷⼊等量的write系统调⽤有⼀定系统开销. • 另⼀⽅⾯使得打印⽇志的进程附带了⼤量同步的磁盘IO影响程序性能 5.2.2异步写日志 异步⽇志是指在进⾏⽇志输出时⽇志输出语句与业务逻辑语句并不是在同⼀个线程中运⾏⽽是有专⻔的线程⽤于进⾏⽇志输出操作。业务线程只需要将⽇志放到⼀个内存缓冲区中不⽤等待即可继续执⾏后续业务逻辑作为⽇志的⽣产者⽽⽇志的落地操作交给单独的⽇志线程去完成作为⽇志的消费者, 这是⼀个典型的⽣产-消费模型。 这样做的好处是即使⽇志没有真的地完成输出也不会影响程序的主业务可以提⾼程序的性能 • 主线程调⽤⽇志打印接⼝成为⾮阻塞操作 • 同步的磁盘IO从主线程中剥离出来交给单独的线程完成 6.相关技术支持补充 6.1不定参函数 6.1不定参宏函数 #include iostream #include cstdarg #define LOG(fmt, ...) printf([%s:%d] fmt \n, __FILE__, __LINE__,##__VA_ARGS__) int main() {LOG(%s-%s, hello, dhy);return 0; }6.2c风格不定参函数 void printNum(int n, ...) {va_list al;va_start(al, n);//让al指向n参数之后的第⼀个可变参数for (int i 0; i n; i) {int num va_arg(al, int);//从可变参数中取出⼀个整形参数std::cout num std::endl;}va_end(al);//清空可变参数列表--其实是将al置空 } int main() {//LOG(%s-%s, hello, dhy);printNum(3, 11,22,33);printNum(5, 44,55,66,77,88);return 0; }vasprintf会根据format字符串和可变参数列表ap的内容动态的分配足够的内存来存储格式化后的字符串并将地址存储在strp指针中如果成功就会返回格式化后的字符串的长度 void myprintf(const char *fmt, ...) {//int vasprintf(char **strp, const char *fmt, va_list ap);char *res;va_list al;va_start(al, fmt);int len vasprintf(res, fmt, al);va_end(al);std::cout res std::endl;free(res); } int main() {myprintf(%s-%d, dhy, 22);return 0; }6.3C⻛格不定参函数 #include iostream #include cstdarg #include memory #include functional void xprintf() {std::cout std::endl; } templatetypename T, typename ...Args void xprintf(const T value, Args ...args) //万能引用 {std::cout value ;if ((sizeof ...(args)) 0) {xprintf(std::forwardArgs(args)...);//完美转发}else {xprintf();} } int main() { //相当于每次都传入第一个字符串给value,然后在递归直到打印结束xprintf(dhy);xprintf(dhy, 好困);xprintf(dhy, 每天都, 好困);return 0; }6.2 设计模式 设计模式是前辈们对代码开发经验的总结是解决特定问题的⼀系列套路。它不是语法规定⽽是⼀套⽤来提⾼代码可复⽤性、可维护性、可读性、稳健性以及安全性的解决⽅案。 六⼤原则 • 单⼀职责原则Single Responsibility Principle ◦ 类的职责应该单⼀⼀个⽅法只做⼀件事。职责划分清晰了每次改动到最⼩单位的⽅法或类。 ◦ 使⽤建议两个完全不⼀样的功能不应该放⼀个类中⼀个类中应该是⼀组相关性很⾼的函数、数据的封装 ◦ ⽤例⽹络聊天⽹络通信 聊天应该分割成为⽹络通信类 聊天类 • 开闭原则Open Closed Principle ◦ 对扩展开放对修改封闭 ◦ 使⽤建议对软件实体的改动最好⽤扩展⽽⾮修改的⽅式。 ◦ ⽤例超时卖货商品价格—不是修改商品的原来价格⽽是新增促销价格。 • ⾥⽒替换原则Liskov Substitution Principle ◦ 通俗点讲就是只要⽗类能出现的地⽅⼦类就可以出现⽽且替换为⼦类也不会产⽣任何错误或异常。 ◦ 在继承类时务必重写⽗类中所有的⽅法尤其需要注意⽗类的protected⽅法⼦类尽量不要暴露⾃⼰的public⽅法供外界调⽤。 ◦ 使⽤建议⼦类必须完全实现⽗类的⽅法孩⼦类可以有⾃⼰的个性。覆盖或实现⽗类的⽅法时输⼊参数可以被放⼤输出可以缩⼩ ◦ ⽤例跑步运动员类-会跑步⼦类⻓跑运动员-会跑步且擅⻓⻓跑 ⼦类短跑运动员-会跑步且擅⻓短跑 • 依赖倒置原则Dependence Inversion Principle。 ◦ ⾼层模块不应该依赖低层模块两者都应该依赖其抽象. 不可分割的原⼦逻辑就是低层模式原⼦逻辑组装成的就是⾼层模块。 ◦ 模块间依赖通过抽象接⼝发⽣具体类之间不直接依赖 ◦ 使⽤建议每个类都尽量有抽象类任何类都不应该从具体类派⽣。尽量不要重写基类的⽅法。结合⾥⽒替换原则使⽤。 ◦ ⽤例奔驰⻋司机类–只能开奔驰 司机类 – 给什么⻋就开什么⻋ 开⻋的⼈司机–依赖于抽象 • 迪⽶特法则Law of Demeter⼜叫“最少知道法则” ◦ 尽量减少对象之间的交互从⽽减⼩类之间的耦合。⼀个对象应该对其他对象有最少的了解。 对类的低耦合提出了明确的要求 ▪ 只和直接的朋友交流 朋友之间也是有距离的。⾃⼰的就是⾃⼰的如果⼀个⽅法放在本类 中既不增加类间关系也对本类不产⽣负⾯影响那就放置在本类中。 ◦ ⽤例⽼师让班⻓点名–⽼师给班⻓⼀个名单班⻓完成点名勾选返回结果⽽不是班⻓点名⽼师勾选 • 接⼝隔离原则Interface Segregation Principle ◦ 客⼾端不应该依赖它不需要的接⼝类间的依赖关系应该建⽴在最⼩的接⼝上 ◦ 使⽤建议接⼝设计尽量精简单⼀但是不要对外暴露没有实际意义的接⼝。 ◦ ⽤例修改密码不应该提供修改⽤⼾信息接⼝⽽就是单⼀的最⼩修改密码接⼝更不要暴 露数据库操作 从整体上来理解六⼤设计原则可以简要的概括为⼀句话⽤抽象构建框架⽤实现扩展细节具体 到每⼀条设计原则则对应⼀条注意事项 • 单⼀职责原则告诉我们实现类要职责单⼀ • ⾥⽒替换原则告诉我们不要破坏继承体系 • 依赖倒置原则告诉我们要⾯向接⼝编程 • 接⼝隔离原则告诉我们在设计接⼝的时候要精简单⼀ • 迪⽶特法则告诉我们要降低耦合 • 开闭原则是总纲告诉我们要对扩展开放对修改关闭。 6.2.1单例模式 ⼀个类只能创建⼀个对象即单例模式该设计模式可以保证系统中该类只有⼀个实例并提供⼀个访问它的全局访问点该实例被所有程序模块共享。⽐如在某个服务器程序中该服务器的配置信息 存放在⼀个⽂件中这些配置数据由⼀个单例对象统⼀读取然后服务进程中的其他对象再通过这个单例对象获取这些配置信息这种⽅式简化了在复杂环境下的配置管理。 单例模式有两种实现模式饿汉模式和懒汉模式 6.2.1.1饿汉模式 饿汉模式: 程序启动时就会创建⼀个唯⼀的实例对象。 因为单例对象已经确定 所以⽐较适⽤于多线程环境中 多线程获取单例对象不需要加锁 可以有效的避免资源竞争 提⾼性能。 //饿汉模式 templatetypename 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;6.2.1.2 懒汉模式 懒汉模式:第⼀次使⽤要使⽤单例对象的时候创建实例对象。如果单例对象构造特别耗时或者耗费济源(加载插件、加载⽹络资源等) 可以选择懒汉模式 在第⼀次使⽤的时候才创建对象。 ◦ 这⾥介绍的是《Effective C》⼀书作者 Scott Meyers 提出的⼀种更加优雅简便的单例模式 Meyers’ Singleton in C。 ◦ C11 Static local variables 特性以确保C11起静态变量将能够在满⾜ thread-safe 的前提下唯⼀地被构造和析构 // 懒汉模式 template typename T class Singleton {private:Singleton(){}~Singleton(){}public:Singleton(const Singleton) delete;Singleton operator(const Singleton) delete;static T getInstance(){static Singleton _eton;return _eton;} };6.2.2 工厂模式 ⼯⼚模式 ⼯⼚模式是⼀种创建型设计模式 它提供了⼀种创建对象的最佳⽅式。在⼯⼚模式中我们创建对象时不会对上层暴露创建逻辑⽽是通过使⽤⼀个共同结构来指向新创建的对象以此实现创建-使⽤的分离。 ⼯⼚模式可以分为 6.2.2.1简单工厂模式 • 简单⼯⼚模式: 简单⼯⼚模式实现由⼀个⼯⼚对象通过类型决定创建出来指定产品类的实例。假设有个⼯⼚能⽣产出⽔果当客⼾需要产品的时候明确告知⼯⼚⽣产哪类⽔果⼯⼚需要接收⽤⼾提供的类别信息当新增产品的时候⼯⼚内部去添加新产品的⽣产⽅式。 //简单⼯⼚模式通过参数控制可以⽣产任何产品 // 优点简单粗暴直观易懂。使⽤⼀个⼯⼚⽣产同⼀等级结构下的任意产品 // 缺点 // 1. 所有东西⽣产在⼀起产品太多会导致代码量庞⼤ // 2. 开闭原则遵循(开放拓展关闭修改)的不是太好要新增产品就必须修改⼯⼚⽅法。 #include iostream #include string #include memory class Fruit { public: Fruit(){} virtual void show() 0; virtual ~Fruit() {} // 添加虚析构函数以支持多态删除 }; class Apple : public Fruit { public: Apple() {} virtual void show() override { // 使用 override 关键字可选但有助于发现错误 std::cout 我是一个苹果 std::endl; } }; class Banana : public Fruit { public: Banana() {} virtual void show() override { // 使用 override 关键字可选但有助于发现错误 std::cout 我是一个香蕉 std::endl; } }; class FruitFactory { public: static std::shared_ptrFruit create(const std::string name) { if (name 苹果) { return std::make_sharedApple(); } else if (name 香蕉) { // 修正了这里的字符 return std::make_sharedBanana(); } return std::shared_ptrFruit(); } }; int main() { std::shared_ptrFruit fruit FruitFactory::create(苹果); fruit-show(); fruit FruitFactory::create(香蕉); // 修正了这里的字符 fruit-show(); return 0; }这个模式的结构和管理产品对象的⽅式⼗分简单 但是它的扩展性⾮常差当我们需要新增产品的时候就需要去修改⼯⼚类新增⼀个类型的产品创建逻辑违背了开闭原则。 6.2.2.2工厂方法模式 ⼯⼚⽅法模式: 在简单⼯⼚模式下新增多个⼯⼚多个产品每个产品对应⼀个⼯⼚。假设现在有A、B 两种产品则开两个⼯⼚⼯⼚ A 负责⽣产产品 A⼯⼚ B 负责⽣产产品 B⽤⼾只知道产品的⼯⼚名⽽不知道具体的产品信息⼯⼚不需要再接收客⼾的产品类别⽽只负责⽣产产品。 #include iostream #include string #include memory // 定义 Fruit 基类 class Fruit { public: Fruit() {} virtual void show() 0; virtual ~Fruit() {} // 虚析构函数以支持多态删除 }; // 定义 Apple 类 class Apple : public Fruit { public: Apple() {} virtual void show() override { std::cout 我是一个苹果 std::endl; } private: std::string _color; }; // 定义 Banana 类 class Banana : public Fruit { public: Banana() {} virtual void show() override { std::cout 我是一个香蕉 std::endl; } }; // 定义 FruitFactory 抽象基类 class FruitFactory { public: virtual std::shared_ptrFruit create() 0; virtual ~FruitFactory() {} // 虚析构函数 }; // 定义 AppleFactory 类 class AppleFactory : public FruitFactory { public: std::shared_ptrFruit create() override { return std::make_sharedApple(); } }; // 定义 BananaFactory 类 class BananaFactory : public FruitFactory { public: std::shared_ptrFruit create() override { return std::make_sharedBanana(); } }; int main() { std::shared_ptrFruit fruit; // 声明 fruit 变量 std::shared_ptrFruitFactory factory; // 声明 factory 变量 factory std::make_sharedAppleFactory(); // 创建 AppleFactory 对象 fruit factory-create(); // 通过 AppleFactory 创建 Apple 对象 fruit-show(); // 显示 我是一个苹果 factory std::make_sharedBananaFactory(); // 创建 BananaFactory 对象 fruit factory-create(); // 通过 BananaFactory 创建 Banana 对象 fruit-show(); // 显示 我是一个香蕉 return 0; }⼯⼚⽅法模式每次增加⼀个产品时都需要增加⼀个具体产品类和⼯⼚类这会使得系统中类的个数成倍增加在⼀定程度上增加了系统的耦合度。 6.2.2.3抽象工厂模式 抽象⼯⼚模式: ⼯⼚⽅法模式通过引⼊⼯⼚等级结构解决了简单⼯⼚模式中⼯⼚类职责太重的问题但由于⼯⼚⽅法模式中的每个⼯⼚只⽣产⼀类产品可能会导致系统中存在⼤量的⼯⼚类势必会增加系统的开销。此时我们可以考虑将⼀些相关的产品组成⼀个产品族位于不同产品等级结构中功能相关联的产品组成的家族由同⼀个⼯⼚来统⼀⽣产这就是抽象⼯⼚模式的基本思想。 #include iostream #include string #include memory // Fruit 基类及其派生类 class Fruit { public: Fruit() {} virtual void show() 0; virtual ~Fruit() {} }; class Apple : public Fruit { public: Apple() {} void show() override { std::cout 我是一个苹果 std::endl; } private: std::string _color; // 这里_color未使用但保留作为示例 }; class Banana : public Fruit { public: Banana() {} void show() override { std::cout 我是一个香蕉 std::endl; } }; // Animal 基类及其派生类 class Animal { public: virtual void voice() 0; virtual ~Animal() {} }; class Sheep : public Animal { // 替换Lamp为Sheep public: void voice() override { std::cout 咩咩咩\n; } }; class Dog : public Animal { public: void voice() override { std::cout 汪汪汪\n; } }; // Factory 抽象基类及其派生类 class Factory { public: virtual std::shared_ptrFruit getFruit(const std::string name) 0; virtual std::shared_ptrAnimal getAnimal(const std::string name) 0; virtual ~Factory() {} }; class FruitFactory : public Factory { public: std::shared_ptrFruit getFruit(const std::string name) override { if (name 苹果) { return std::make_sharedApple(); } else if (name 香蕉) { // 修正字符串 return std::make_sharedBanana(); } return std::shared_ptrFruit(); } std::shared_ptrAnimal getAnimal(const std::string name) override { return std::shared_ptrAnimal(); // 默认不返回Animal对象 } }; class AnimalFactory : public Factory { public: std::shared_ptrAnimal getAnimal(const std::string name) override { if (name 小羊) { // 修正字符串 return std::make_sharedSheep(); } else if (name 小狗) { return std::make_sharedDog(); } return std::shared_ptrAnimal(); } std::shared_ptrFruit getFruit(const std::string name) override { return std::shared_ptrFruit(); // 默认不返回Fruit对象 } }; class FactoryProducer { public: static std::shared_ptrFactory getFactory(const std::string name) { if (name 动物) { return std::make_sharedAnimalFactory(); } else { return std::make_sharedFruitFactory(); } } }; int main() { std::shared_ptrFactory fruit_factory FactoryProducer::getFactory(水果); // 修正为水果 std::shared_ptrFruit fruit fruit_factory-getFruit(苹果); fruit-show(); fruit fruit_factory-getFruit(香蕉); // 修正为香蕉 fruit-show(); // 示例使用AnimalFactory std::shared_ptrFactory animal_factory FactoryProducer::getFactory(动物); std::shared_ptrAnimal animal animal_factory-getAnimal(小羊); animal-voice(); animal animal_factory-getAnimal(小狗); animal-voice(); return 0; }抽象⼯⼚模式适⽤于⽣产多个⼯⼚系列产品衍⽣的设计模式增加新的产品等级结构复杂需要对原有系统进⾏较⼤的修改甚⾄需要修改抽象层代码违背了“开闭原则”。 6.2.3建造者模式 建造者模式是⼀种创建型设计模式 使⽤多个简单的对象⼀步⼀步构建成⼀个复杂的对象能够将⼀个复杂的对象的构建与它的表⽰分离提供⼀种创建对象的最佳⽅式。主要⽤于解决对象的构建过于复杂的问题。 建造者模式主要基于四个核⼼类实现 • 抽象产品类 • 具体产品类⼀个具体的产品对象类 • 抽象Builder类创建⼀个产品对象所需的各个部件的抽象接⼝ • 具体产品的Builder类实现抽象接⼝构建各个部件 • 指挥者Director类统⼀组建过程提供给调⽤者使⽤通过指挥者来构造产品 #include iostream #include memory /* 抽象电脑类 */ class Computer { public: using ptr std::shared_ptrComputer; 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_ptrMacBook; MacBook() {} void setOs() override { _os Mac OS X12; } }; /* 抽象建造者类 */ class Builder { public: using ptr std::shared_ptrBuilder; 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; virtual ~Builder() {} }; /* 具体建造者类 */ class MacBookBuilder : public Builder { public: using ptr std::shared_ptrMacBookBuilder; MacBookBuilder() : _computer(std::make_sharedMacBook()) {} void buildBoard(const std::string board) override { _computer-setBoard(board); } void buildDisplay(const std::string display) override { _computer-setDisplay(display); } void buildOs() override { _computer-setOs(); } Computer::ptr build() override { return _computer; } private: Computer::ptr _computer; }; /* 指挥者类 */ class Director { public: Director(Builder::ptr 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() { auto builder std::make_sharedMacBookBuilder(); std::unique_ptrDirector pd(new Director(builder)); pd-construct(英特尔主板, VOC显示器); Computer::ptr computer builder-build(); std::cout computer-toString(); return 0; }6.2.4代理模式 代理模式指代理控制对其他对象的访问 也就是代理对象控制对原对象的引⽤。在某些情况下⼀个对象不适合或者不能直接被引⽤访问⽽代理对象可以在客⼾端和⽬标对象之间起到中介的作⽤。 代理模式的结构包括⼀个是真正的你要访问的对象(⽬标类)、⼀个是代理对象。⽬标对象与代理对象实现同⼀个接⼝先访问代理类再通过代理类访问⽬标对象。代理模式分为静态代理、动态代理 • 静态代理指的是在编译时就已经确定好了代理类和被代理类的关系。也就是说在编译时就已经确定了代理类要代理的是哪个被代理类。 • 动态代理指的是在运⾏时才动态⽣成代理类并将其与被代理类绑定。这意味着在运⾏时才能确定代理类要代理的是哪个被代理类。 以租房为例房东将房⼦租出去但是要租房⼦出去需要发布招租启⽰ 带⼈看房负责维修这些⼯作中有些操作并⾮房东能完成因此房东为了图省事将房⼦委托给中介进⾏租赁。 代理模式实现 、 /*房东要把⼀个房⼦通过中介租出去理解代理模式*/ #include iostream #include string class RentHouse {public:virtual void rentHouse() 0; }; /*房东类将房⼦租出去*/ class Landlord : public RentHouse {public:void rentHouse() {std::cout 将房⼦租出去\n; } }; /*中介代理类对租房⼦进⾏功能加强实现租房以外的其他功能*/ class Intermediary : public RentHouse {public:void rentHouse() {std::cout 发布招租启⽰\n;std::cout 带⼈看房\n;_landlord.rentHouse();std::cout 负责租后维修\n;}private:Landlord _landlord; }; int main() {Intermediary intermediary;intermediary.rentHouse();return 0; }7.⽇志系统框架设计 本项⽬实现的是⼀个多⽇志器⽇志系统主要实现的功能是让程序员能够轻松的将程序运⾏⽇志信息落地到指定的位置且⽀持同步与异步两种⽅式的⽇志落地⽅式。 项⽬的框架设计将项⽬分为以下⼏个模块来实现。 7.1模块划分 ⽇志等级模块对输出⽇志的等级进⾏划分以便于控制⽇志的输出并提供等级枚举转字符串功能。 ◦ OFF关闭 ◦ DEBUG调试调试时的关键信息输出。 ◦ INFO提⽰普通的提⽰型⽇志信息。 ◦ WARN警告不影响运⾏但是需要注意⼀下的⽇志。 ◦ ERROR错误程序运⾏出现错误的⽇志 ◦ FATAL致命⼀般是代码异常导致程序⽆法继续推进运⾏的⽇志 ⽇志消息模块中间存储⽇志输出所需的各项要素信息 ◦ 时间描述本条⽇志的输出时间。 ◦ 线程ID描述本条⽇志是哪个线程输出的。 ◦ ⽇志等级描述本条⽇志的等级。 ◦ ⽇志数据本条⽇志的有效载荷数据。 ◦ ⽇志⽂件名描述本条⽇志在哪个源码⽂件中输出的。 ◦ ⽇志⾏号描述本条⽇志在源码⽂件的哪⼀⾏输出的。 ⽇志消息格式化模块设置⽇志输出格式并提供对⽇志消息进⾏格式化功能。 ◦ 系统的默认⽇志输出格式[%d{%H:%M:%S}][%t][%p][%c][%f:%l]%m%n ◦ %d{%H:%M:%S}表⽰⽇期时间花括号中的内容表⽰⽇期时间的格式。 ◦ %T表⽰制表符缩进。 ◦ %t表⽰线程ID ◦ %p表⽰⽇志级别 ◦ %c表⽰⽇志器名称不同的开发组可以创建⾃⼰的⽇志器进⾏⽇志输出⼩组之间互不影 响。 ◦ %f表⽰⽇志输出时的源代码⽂件名。 ◦ %l表⽰⽇志输出时的源代码⾏号。 ◦ %m表⽰给与的⽇志有效载荷数据 ◦ %n表⽰换⾏ ◦ 设计思想设计不同的⼦类不同的⼦类从⽇志消息中取出不同的数据进⾏处理。 ⽇志消息落地模块决定了⽇志的落地⽅向可以是标准输出也可以是⽇志⽂件也可以滚动⽂件输出… ◦ 标准输出表⽰将⽇志进⾏标准输出的打印。 ◦ ⽇志⽂件输出表⽰将⽇志写⼊指定的⽂件末尾。 ◦ 滚动⽂件输出当前以⽂件⼤⼩进⾏控制当⼀个⽇志⽂件⼤⼩达到指定⼤⼩则切换下⼀个⽂件进⾏输出 ◦ 后期也可以扩展远程⽇志输出创建客⼾端将⽇志消息发送给远程的⽇志分析服务器。 ◦ 设计思想设计不同的⼦类不同的⼦类控制不同的⽇志落地⽅向。 ⽇志器模块 ◦ 此模块是对以上⼏个模块的整合模块⽤⼾通过⽇志器进⾏⽇志的输出有效降低⽤⼾的使⽤难度。 ◦ 包含有⽇志消息落地模块对象⽇志消息格式化模块对象⽇志输出等级 ⽇志器管理模块 ◦ 为了降低项⽬开发的⽇志耦合不同的项⽬组可以有⾃⼰的⽇志器来控制输出格式以及落地⽅向因此本项⽬是⼀个多⽇志器的⽇志系统。 ◦ 管理模块就是对创建的所有⽇志器进⾏统⼀管理。并提供⼀个默认⽇志器提供标准输出的⽇志输出。 异步线程模块 ◦ 实现对⽇志的异步输出功能⽤⼾只需要将输出⽇志任务放⼊任务池异步线程负责⽇志的落地输出功能以此提供更加⾼效的⾮阻塞⽇志输出。 7.2模块关系图 8.代码设计 8.1 实⽤类设计 提前完成⼀些零碎的功能接⼝以便于项⽬中会⽤到。 • 获取系统时间 • 判断⽂件是否存在 • 获取⽂件的所在⽬录路径 • 创建⽬录 #ifndef __M_UTIL_H__ #define __M_UTIL_H__ /* 通⽤功能类与业务⽆关的功能实现 1. 获取系统时间 2. 获取⽂件⼤⼩ 3. 创建⽬录 4. 获取⽂件所在⽬录 */ #include iostream #include fstream #include sstream #include string #include ctime #include cassert #include sys/stat.h namespace dhylog {namespace util{class date{public:static size_t now(){return size_t(time(nullptr));}};class File{public:static bool exists(const std::string name){struct stat st;return stat(name.c_str(),st)0;}static std::string path(const std::string name){if(name.empty()) return .;size_t posname.find_last_of(/\\);if(posstd::string::npos){return .;}return name.substr(0,pos1);}static void create_directory(const std::string path) {if(path.empty()) return;if(exists(path)) return;size_t pos0,idx0;while(idxpath.size()){pospath.find_first_of(/\\,idx);if(posstd::string::npos){mkdir(path.c_str(), 0777);return;}std::string parent_dirpath.substr(0,pos1);if(exists(parent_dir)) {idxpos1; continue;}mkdir(parent_dir.c_str(),0777);idxpos1;}} };}; }; #endif8.2⽇志等级类设计 ⽇志等级总共分为7个等级分别为 • OFF 关闭所有⽇志输出 • DRBUG 进⾏debug时候打印⽇志的等级 • INFO 打印⼀些⽤⼾提⽰信息 • WARN 打印警告信息 • ERROR 打印错误信息 • FATAL 打印致命信息- 导致程序崩溃的信息 #ifndef __M_LEVEL_H__ #define __M_LEVEL_H__ namespace dhylog {class loglevel{public:enum class value{UNKNOW0,DEBUG,INFO,WARN,ERROR,FATAL,OFF,};static const char *toString(loglevel::value level){switch(level){case loglevel::value::DEBUG: return DEBUG;case loglevel::value::INFO: return INFO;case loglevel::value::WARN: return WARN;case loglevel::value::ERROR: return ERROR;case loglevel::value::FATAL: return FATAL;case loglevel::value::OFF: return OFF;}return UNKNOW;}}; } #endif8.3 ⽇志消息类设计 ⽇志消息类主要是封装⼀条完整的⽇志消息所需的内容其中包括⽇志等级、对应的logger name、打印⽇志源⽂件的位置信息包括⽂件名和⾏号、线程ID、时间戳信息、具体的⽇志信息等内容。 #ifndef __M_MESSAGE_H__ #define __M_MESSAGE_H__#includeiostream #includelevel.hpp #includeutil.hpp #includethread namespace dhylog {struct LogMsg{time_t _ctime;//日志产生的时间戳loglevel::value _level;//日志等级size_t _line;//行号std::thread::id _tid;//线程idstd::string _file;//源码文件名std::string _logger;//日志器名称std::string _payload;//有效消息数据LogMsg(loglevel::value level,size_t line,std::string file,std::string logger,std::string msg):_ctime(util::date::now()),_line(line),_level(level),_tid(std::this_thread::get_id()),_file(file),_logger(logger),_payload(msg) {} }; } #endif8.4 ⽇志输出格式化类设计 ⽇志格式化Formatter类主要负责格式化⽇志消息。其主要包含以下内容 • pattern成员保存⽇志输出的格式字符串。 ◦ %d ⽇期 ◦ %T 缩进 ◦ %t 线程id ◦ %p ⽇志级别 ◦ %c ⽇志器名称 ◦ %f ⽂件名 ◦ %l ⾏号 ◦ %m ⽇志消息 ◦ %n 换⾏ #ifndef __M_FMT_H__ #define __M_FMT_H__#include util.hpp #include message.hpp #include level.hpp #include memory #include vector #include tuple #include ctimenamespace dhylog {class FormatItem{public:using ptrstd::shared_ptrFormatItem;virtual void format(std::ostream out, const LogMsg msg) 0;};class MsgFormatItem :public FormatItem{public:void format(std::ostream out, const LogMsg msg)override{outmsg._payload;}};class LevelFormatItem :public FormatItem{public:void format(std::ostream out, const LogMsg msg)override{outloglevel::toString(msg._level);}};class TimeFormatItem :public FormatItem{public:TimeFormatItem(const std::string fmt%H:%M:%S):_time_fmt(fmt){}void format(std::ostream out, const LogMsg msg)override{struct tm t;localtime_r(msg._ctime,t);char tmp[128];strftime(tmp,127,_time_fmt.c_str(),t);outtmp;}private:std::string _time_fmt;};class FileFormatItem :public FormatItem{public:void format(std::ostream out, const LogMsg msg)override{outmsg._file;}};class LineFormatItem :public FormatItem{public:void format(std::ostream out, const LogMsg msg)override{outmsg._line;}};class ThreadFormatItem :public FormatItem{public:void format(std::ostream out, const LogMsg msg)override{outmsg._tid;}};class LoggerFormatItem :public FormatItem{public:void format(std::ostream out, const LogMsg msg)override{outmsg._logger;}};class TabFormatItem :public FormatItem{public:void format(std::ostream out, const LogMsg msg)override{out\t;}};class NlineFormatItem :public FormatItem{public:void format(std::ostream out, const LogMsg msg)override{out\n;}};class OtherFormatItem :public FormatItem{public:OtherFormatItem(const std::string str ):_str(str){}void format(std::ostream out, const LogMsg msg)override{out_str;}private:std::string _str;};class Formatter{public:using ptrstd::shared_ptrFormatter;/*%d 日期%T 缩进%t 线程id%p 日志级别%c 日志器名称%f 文件名%l 行号%m 日志消息%n 换行*///时间{年-月-日 时:分:秒}缩进 线程ID 缩进 [日志级别] 缩进 [日志名称] 缩进 文件名:行号 缩进 消息换行 [%d{%H:%M:%S}][%t][%p][%c][%f:%l]%m%nFormatter(const std::string pattern [%d{%H:%M:%S}][%t][%p][%c][%f:%l]%m%n):_pattern(pattern){assert(parsePattern());}const std::string pattern() { return _pattern; }std::string format(const LogMsg msg){std::stringstream ss;for(auto it :_items){it-format(ss,msg);}return ss.str();}std::ostream format(std::ostream out,const LogMsgmsg){for(auto it :_items){it-format(out,msg);}return out;}FormatItem::ptr createItem(const std::string key, const std::string val) {if(keyd) return std::make_sharedTimeFormatItem(val);if(keyT) return std::make_sharedTabFormatItem();if(keyc) return std::make_sharedLoggerFormatItem();if(keyf) return std::make_sharedFileFormatItem();if(keyl) return std::make_sharedLineFormatItem();if(keyp) return std::make_sharedLevelFormatItem();if(keyt) return std::make_sharedThreadFormatItem();if(keym) return std::make_sharedMsgFormatItem();if(keyn) return std::make_sharedNlineFormatItem();if(key.empty()) return std::make_sharedOtherFormatItem(val);std::cout没有对应的格式化字符%keystd::endl;abort();}private:bool parsePattern() {//[%d{%H:%M:%S}][%t][%p][%c][%f:%l] %m%n//sg{}fsg%d{%H:%M:%S}%Tsdf%t%T[%p]%T[%c]%T%f:%l%T%m%n std::vectorstd::pairstd::string,std::string fmt_order;size_t pos0;std::string key,value;while(pos_pattern.size()){//寻找%if(_pattern[pos]!%) {value.push_back(_pattern[pos]);continue;}//当前位置为%,判断后面是否为%if(pos1_pattern.size()_pattern[pos1]%){value.push_back(%);pos2;continue;};//当前位置为%,且pos1不为%,剔除前两下项为%d的情况if(!value.empty()){fmt_order.push_back(std::make_pair(,value));value.clear();}//当前位置为%后面为格式化字符接下来处理格式化字符pos1;//当前位置为格式化字符判断越界if(pos_pattern.size()){std::cout%之后没有对相应的格式化字符!\n;return false;}key_pattern[pos];pos1;//格式化字符完整当前位置为格式化字符后一位判断是否为{if(pos_pattern.size()_pattern[pos]{){pos;while(pos_pattern.size()_pattern[pos]!}){value.push_back(_pattern[pos]);}if(pos_pattern.size()){std::cout{不匹配;return false;}pos1;} fmt_order.push_back(std::make_pair(key,value));key.clear();value.clear();}for(auto it:fmt_order){_items.push_back(createItem(it.first,it.second));}return true;}std::string _pattern;std::vectorFormatItem::ptr _items;}; }#endif8.5 ⽇志落地(LogSink)类设计简单⼯⼚模式 ⽇志落地类主要负责落地⽇志消息到⽬的地。 它主要包括以下内容 • Formatter⽇志格式化器主要是负责格式化⽇志消息 • mutex互斥锁保证多线程⽇志落地过程中的线程安全避免出现交叉输出的情况。 这个类⽀持可扩展其成员函数log设置为纯虚函数当我们需要增加⼀个log输出⽬标 可以增加⼀ 个类继承⾃该类并重写log⽅法实现具体的落地⽇志逻辑。 ⽬前实现了三个不同⽅向上的⽇志落地 • 标准输出StdoutSink • 固定⽂件FileSink • 滚动⽂件RollSink ◦ 滚动⽇志⽂件输出的必要性 ▪ 由于机器磁盘空间有限 我们不可能⼀直⽆限地向⼀个⽂件中增加数据 ▪ 如果⼀个⽇志⽂件体积太⼤⼀⽅⾯是不好打开另⼀⽅⾯是即时打开了由于包含数据巨⼤也不利于查找我们需要的信息 ▪ 所以实际开发中会对单个⽇志⽂件的⼤⼩也会做⼀些控制即当⼤⼩超过某个⼤⼩时如1GB我们就重新创建⼀个新的⽇志⽂件来滚动写⽇志。 对于那些过期的⽇志 ⼤部分企业内部都有专⻔的运维⼈员去定时清理过期的⽇志或者设置系统定时任务定时清理过期⽇志。 ◦ ⽇志⽂件的滚动思想 ⽇志⽂件滚动的条件有两个:⽂件⼤⼩ 和 时间。我们可以选择 ▪ ⽇志⽂件在⼤于 1GB 的时候会更换新的⽂件 ▪ 每天定点滚动⼀个⽇志⽂件 本项⽬基于⽂件⼤⼩的判断滚动⽣成新的⽂件 #ifndef __M_SINK_H__ #define __M_SINK_H__ #include util.hpp #include message.hpp #include formatter.hpp #include memory #include mutexnamespace dhylog {class LogSink{public:using ptr std::shared_ptrLogSink;LogSink() {}virtual void log(const char *data, size_t len) 0;};class StdoutSink : public LogSink {public:using ptr std::shared_ptrStdoutSink;void log(const char *data, size_t len)override{std::cout.write(data,len);std::coutstd::endl;}};class FileSink : public LogSink {public:using ptr std::shared_ptrFileSink;FileSink(const std::string filename):_Filename(filename) {util::File::create_directory(util::File::path(filename));_ofs.open(filename, std::ios::binary | std::ios::app);assert(_ofs.is_open());}void log(const char *data, size_t len)override{_ofs.write(data,len);if(_ofs.good()false){std::cout日志文件输出失败std::endl;}}private:std::string _Filename;std::ofstream _ofs;};class RollSink : public LogSink{public:using ptr std::shared_ptrRollSink;RollSink(std::string basename,size_t max_fsize):_basename(basename),_max_fsize(max_fsize),_cur_fsize(0){util::File::create_directory(util::File::path(basename)); }void log(const char *data, size_t len)override{initLogFile();_ofs.write(data,len);_ofs\n;if (_ofs.good() false) {std::cout 日志输出文件失败\n;}_cur_fsize len;}private:void initLogFile() {if(_ofs.is_open()false||_cur_fsize_max_fsize){_ofs.close();std::string name createFilename();_ofs.open(name, std::ios::binary | std::ios::app);assert(_ofs.is_open());_cur_fsize 0;return;}return;}std::string createFilename(){time_t tutil::date::now();struct tm lt;localtime_r(t, lt);std::stringstream ss;ss _basename;ss lt.tm_year 1900;ss -;ss lt.tm_mon 1;ss -;ss lt.tm_mday;ss -;ss lt.tm_hour;ss -;ss lt.tm_min;ss -;ss lt.tm_sec;ss .log;return ss.str();}std::string _basename;std::ofstream _ofs;size_t _max_fsize;size_t _cur_fsize;};class SinkFactory {public:templatetypename SinkType, typename ...Argsstatic LogSink::ptr create(Args ...args) {return std::make_sharedSinkType(std::forwardArgs(args)...);}}; } #endif8.6 ⽇志器类(Logger)设计建造者模式 ⽇志器主要是⽤来和前端交互 当我们需要使⽤⽇志系统打印log的时候 只需要创建Logger对象调⽤该对象debug、info、warn、error、fatal等⽅法输出⾃⼰想打印的⽇志即可⽀持解析可变参数列表和输出格式 即可以做到像使⽤printf函数⼀样打印⽇志。 当前⽇志系统⽀持同步⽇志 异步⽇志两种模式两个不同的⽇志器唯⼀不同的地⽅在于他们在⽇志的落地⽅式上有所不同 同步⽇志器直接对⽇志消息进⾏输出。 异步⽇志器将⽇志消息放⼊缓冲区由异步线程进⾏输出。 因此⽇志器类在设计的时候先设计出⼀个Logger基类在Logger基类的基础上继承出SyncLogger同步⽇志器和AsyncLogger异步⽇志器。 且因为⽇志器模块是对前边多个模块的整合想要创建⼀个⽇志器需要设置⽇志器名称设置⽇志输出等级设置⽇志器类型设置⽇志输出格式设置落地⽅向且落地⽅向有可能存在多个整个⽇志器的创建过程较为复杂为了保持良好的代码⻛格编写出优雅的代码因此⽇志器的创建这⾥采⽤了建造者模式来进⾏创建。 #ifndef _M_LOGGER_H_ #define _M_LOGGER_H_ //#define _GNU_SOURCE #includeutil.hpp #includelevel.hpp #includeformatter.hpp #includesink.hpp #includelooper.hpp #includeatomic #includecstdarg #includeunordered_mapnamespace dhylog {class Logger{public:using ptrstd::shared_ptrLogger;Logger(const std::string logger_name,loglevel::value level,Formatter::ptr formatter,std::vectorLogSink::ptr sinks):_logger_name(logger_name),_limit_level(level),_formatter(formatter),_sinks(sinks.begin(),sinks.end()){}const std::string name(){return _logger_name;}void debug(const std::string file,size_t line,const std::string fmt,...){if(loglevel::value::DEBUG_limit_level) {return;}va_list ap;va_start(ap,fmt);char *res;int retvasprintf(res,fmt.c_str(),ap);if(ret-1){std::coutvasprintf failed!!\n;return;}va_end(ap);serialize(loglevel::value::DEBUG,file,line,res); free(res);}void info(const std::string file,size_t line,const std::string fmt,...){if(loglevel::value::INFO_limit_level) {return;}va_list ap;va_start(ap,fmt);char *res;int retvasprintf(res,fmt.c_str(),ap);if(ret-1){std::coutvasprintf failed!!\n;return;}va_end(ap);serialize(loglevel::value::INFO,file,line,res); free(res);}void warn(const std::string file,size_t line,const std::string fmt,...){if(loglevel::value::WARN_limit_level) {return;}va_list ap;va_start(ap,fmt);char *res;int retvasprintf(res,fmt.c_str(),ap);if(ret-1){std::coutvasprintf failed!!\n;return;}va_end(ap);serialize(loglevel::value::WARN,file,line,res); free(res);}void error(const std::string file,size_t line,const std::string fmt,...){if(loglevel::value::ERROR_limit_level) {return;}va_list ap;va_start(ap,fmt);char *res;int retvasprintf(res,fmt.c_str(),ap);if(ret-1){std::coutvasprintf failed!!\n;return;}va_end(ap);serialize(loglevel::value::ERROR,file,line,res); free(res);}void fatal(const std::string file,size_t line,const std::string fmt,...){if(loglevel::value::FATAL_limit_level) {return;}va_list ap;va_start(ap,fmt);char *res;int retvasprintf(res,fmt.c_str(),ap);if(ret-1){std::coutvasprintf failed!!\n;return;}va_end(ap);serialize(loglevel::value::FATAL,file,line,res); free(res);}protected:void serialize(loglevel::value level,const std::string file,size_t line,char*str){LogMsg msg(level,line,file,_logger_name,str);std::stringstream ss;_formatter-format(ss,msg);log(ss.str().c_str(),ss.str().size()); }virtual void log(const char*data,size_t len)0;protected:std::mutex _mutex;std::string _logger_name;std::atomicloglevel::value _limit_level;Formatter::ptr _formatter;std::vectorLogSink::ptr _sinks;};//同步日志器class SyncLogger : public Logger {public:SyncLogger(const std::string logger_name,loglevel::value level,Formatter::ptr formatter,std::vectorLogSink::ptr sinks):Logger(logger_name,level,formatter,sinks) {}protected:void log(const char*data,size_t len){std::unique_lockstd::mutex lock(_mutex);if(_sinks.empty()) return;for(auto sink :_sinks){sink-log(data,len);} }};//异步日志器class AsyncLogger : public Logger {public://using ptr std::shared_ptrAsyncLogger;AsyncLogger(const std::string logger_name,loglevel::value level,Formatter::ptr formatter,std::vectorLogSink::ptr sinks,AsyncType looper_type):Logger(logger_name,level,formatter,sinks),_looper(std::make_sharedAsyncLooper(std::bind(AsyncLogger::reallog,this,std::placeholders::_1),looper_type)){}protected:void log(const char*data,size_t len)//将数据写入缓冲区{_looper-push(data,len);}//设计一个实际落地函数将缓冲区中的数据落地void reallog(Buffer buf){if(_sinks.empty()) return;for(auto sink:_sinks){sink-log(buf.begin(),buf.readAbleSize());}}private:AsyncLooper::ptr _looper;};enum class LoggerType {LOGGER_SYNC 0,LOGGER_ASYNC};class LoggerBuilder{public:LoggerBuilder():_logger_type(LoggerType::LOGGER_SYNC),_limit_level(loglevel::value::DEBUG),_looper_Type(AsyncType::ASYNC_SAFE){}void buildLoggerType(LoggerType type){_logger_typetype;}void buildEnableUnSafeAsync(){_looper_TypeAsyncType::ASYNC_UNSAFE;}void buildLoggerName(const std::string name){_logger_namename;}void buildLoggerLevel(loglevel::value level){_limit_levellevel;}void buildFormatter(const std::string pattern){_formatterstd::make_sharedFormatter(pattern);}templatetypename SinkType,typename ...Argsvoid buildSink(Args ...args){LogSink::ptr psinkSinkFactory::createSinkType(std::forwardArgs(args)...);_sinks.push_back(psink);}virtual Logger::ptr build()0;protected:AsyncType _looper_Type;LoggerType _logger_type;std::string _logger_name;loglevel::value _limit_level;Formatter::ptr _formatter;std::vectorLogSink::ptr _sinks;};class LocalLoggerBuilder :public LoggerBuilder{public:Logger::ptr build() override{assert(_logger_name.empty()false);if(_formatter.get()nullptr){_formatterstd::make_sharedFormatter();}if(_sinks.empty()){buildSinkStdoutSink();}if(_logger_typeLoggerType::LOGGER_ASYNC){//std::cout(_looper_TypeAsyncType::ASYNC_UNSAFE);return std::make_sharedAsyncLogger(_logger_name,_limit_level,_formatter,_sinks,_looper_Type);}return std::make_sharedSyncLogger(_logger_name,_limit_level,_formatter,_sinks);}};class LoggerManager{public:static LoggerManager getInstance(){static LoggerManager eton;return eton;}void addLogger(Logger::ptr Logger){if(hasLogger(Logger-name())) return ;std::unique_lockstd::mutex lock(_mutex);_loggers.insert(make_pair(Logger-name(),Logger)); }bool hasLogger(std::string name){std::unique_lockstd::mutex lock(_mutex);auto it_loggers.find(name);if(it_loggers.end()) return false;else return true;}Logger::ptr getLogger(const std::string name){std::unique_lockstd::mutex lock(_mutex);auto it_loggers.find(name);if(it_loggers.end()) return Logger::ptr();else return it-second;}Logger::ptr rootLogger(){return _root_logger;}private:LoggerManager(){std::unique_ptrdhylog::LoggerBuilder builder(new dhylog::LocalLoggerBuilder());builder-buildLoggerName(root);_root_loggerbuilder-build();_loggers.insert(make_pair(root,_root_logger));}private:std::mutex _mutex;Logger::ptr _root_logger;//默认日志器std::unordered_mapstd::string,Logger::ptr _loggers;};class GlobalLoggerBuilder :public LoggerBuilder{public:Logger::ptr build() override{assert(_logger_name.empty()false);if(_formatter.get()nullptr){_formatterstd::make_sharedFormatter();}if(_sinks.empty()){buildSinkStdoutSink();}Logger::ptr logger;if(_logger_typeLoggerType::LOGGER_ASYNC){ loggerstd::make_sharedAsyncLogger(_logger_name,_limit_level,_formatter,_sinks,_looper_Type);}else{loggerstd::make_sharedSyncLogger(_logger_name,_limit_level,_formatter,_sinks);}LoggerManager::getInstance().addLogger(logger);return logger;}}; } #endif 8.7双缓冲区异步任务处理器AsyncLooper设计 设计思想异步处理线程 数据池 使⽤者将需要完成的任务添加到任务池中由异步线程来完成任务的实际执⾏操作。 任务池的设计思想双缓冲区阻塞数据池 优势避免了空间的频繁申请释放且尽可能的减少了⽣产者与消费者之间锁冲突的概率提⾼了任务处理效率。 在任务池的设计中有很多备选⽅案⽐如循环队列等等但是不管是哪⼀种都会涉及到锁冲突的情况因为在⽣产者与消费者模型中任何两个⻆⾊之间都具有互斥关系因此每⼀次的任务添加与取出都有可能涉及锁的冲突⽽双缓冲区不同双缓冲区是处理器将⼀个缓冲区中的任务全部处理完毕后然后交换两个缓冲区重新对新的缓冲区中的任务进⾏处理虽然同时多线程写⼊也会冲突但是冲突并不会像每次只处理⼀条的时候频繁减少了⽣产者与消费者之间的锁冲突且不涉及到空间的频繁申请释放所带来的消耗。 #ifndef M_BUF_H #define M_BUF_H #includeutil.hpp #includevector namespace dhylog{#define DEFAULT_BUFFER_SIZE (100*1024*1024)#define THRESHOLD_BUFFER_SIZE (80*1024*1024)#define INCREMENNT_BUFFER_SIZE (10*1024*1024)class Buffer{public:Buffer():_buffer(DEFAULT_BUFFER_SIZE),_writer_idx(0),_reader_idx(0){}//向缓冲区写入数据void push(const char*data,size_t len){//缓冲区剩余空间不够的情况1.扩容//1.固定大小则直接返回//if(lenwriteAbleSize()) return;//2.动态控件用于极限性能测试--扩容ensureEnoughSize(len);//1.将数据拷贝到缓冲区std::copy(data,datalen,_buffer[_writer_idx]);//2.将当前写入位置向后偏移moveWriter(len); }size_t writeAbleSize(){return (_buffer.size()-_writer_idx);}//返回可读数据得起始地址const char*begin(){return _buffer[_reader_idx];}//返回可读数据的长度size_t readAbleSize(){return (_writer_idx-_reader_idx);}void moveReader(size_t len){assert(lenreadAbleSize());_reader_idxlen;}//重置读写位置初始化缓冲区void reset(){_writer_idx0;_reader_idx0;}//对Buffer实现交换操作void swap(Buffer buffer){_buffer.swap(buffer._buffer);std::swap(_reader_idx,buffer._reader_idx);std::swap(_writer_idx,buffer._writer_idx);}//判断缓冲区是否为空bool empty(){{return (_reader_idx_writer_idx);}}private://对空间进行扩容void ensureEnoughSize(size_t len){if(lenwriteAbleSize()) return ;size_t new_size0;if(_buffer.size()THRESHOLD_BUFFER_SIZE){new_size_buffer.size()*2;}else {new_size_buffer.size()INCREMENNT_BUFFER_SIZE;}_buffer.resize(new_size);}//对读写指针进行向后偏移操作void moveWriter(size_t len){assert((len_writer_idx)_buffer.size());_writer_idxlen;}private:std::vectorchar _buffer;size_t _reader_idx;//当前可读数据的指针--本质是下标size_t _writer_idx;//当前可写的指针}; } #endif8.8 异步⽇志器(AsyncLogger)设计 异步⽇志器类继承⾃⽇志器类 并在同步⽇志器类上拓展了异步消息处理器。当我们需要异步输出⽇志的时候 需要创建异步⽇志器和消息处理器 调⽤异步⽇志器的log、error、info、fatal等函数输出不同级别⽇志。 • log函数为重写Logger类的函数 主要实现将⽇志数据加⼊异步队列缓冲区中 • realLog函数主要由异步线程进⾏调⽤(是为异步消息处理器设置的回调函数)完成⽇志的实际落地⼯作。 //异步日志器class AsyncLogger : public Logger {public://using ptr std::shared_ptrAsyncLogger;AsyncLogger(const std::string logger_name,loglevel::value level,Formatter::ptr formatter,std::vectorLogSink::ptr sinks,AsyncType looper_type):Logger(logger_name,level,formatter,sinks),_looper(std::make_sharedAsyncLooper(std::bind(AsyncLogger::reallog,this,std::placeholders::_1),looper_type)){}protected:void log(const char*data,size_t len)//将数据写入缓冲区{_looper-push(data,len);}//设计一个实际落地函数将缓冲区中的数据落地void reallog(Buffer buf){if(_sinks.empty()) return;for(auto sink:_sinks){sink-log(buf.begin(),buf.readAbleSize());}}private:AsyncLooper::ptr _looper;};8.9 单例⽇志器管理类设计单例模式 ⽇志的输出我们希望能够在任意位置都可以进⾏但是当我们创建了⼀个⽇志器之后就会受到⽇志器所在作⽤域的访问属性限制。 因此为了突破访问区域的限制我们创建⼀个⽇志器管理类且这个类是⼀个单例类这样的话我们就可以在任意位置来通过管理器单例获取到指定的⽇志器来进⾏⽇志输出了。 基于单例⽇志器管理器的设计思想我们对于⽇志器建造者类进⾏继承继承出⼀个全局⽇志器建造者类实现⼀个⽇志器在创建完毕后直接将其添加到单例的⽇志器管理器中以便于能够在任何位置通过⽇志器名称能够获取到指定的⽇志器进⾏⽇志输出。 class LoggerManager{public:static LoggerManager getInstance(){static LoggerManager eton;return eton;}void addLogger(Logger::ptr Logger){if(hasLogger(Logger-name())) return ;std::unique_lockstd::mutex lock(_mutex);_loggers.insert(make_pair(Logger-name(),Logger)); }bool hasLogger(std::string name){std::unique_lockstd::mutex lock(_mutex);auto it_loggers.find(name);if(it_loggers.end()) return false;else return true;}Logger::ptr getLogger(const std::string name){std::unique_lockstd::mutex lock(_mutex);auto it_loggers.find(name);if(it_loggers.end()) return Logger::ptr();else return it-second;}Logger::ptr rootLogger(){return _root_logger;}private:LoggerManager(){std::unique_ptrdhylog::LoggerBuilder builder(new dhylog::LocalLoggerBuilder());builder-buildLoggerName(root);_root_loggerbuilder-build();_loggers.insert(make_pair(root,_root_logger));}private:std::mutex _mutex;Logger::ptr _root_logger;//默认日志器std::unordered_mapstd::string,Logger::ptr _loggers;};8.10 ⽇志宏全局接⼝设计代理模式 提供全局的⽇志器获取接⼝。 使⽤代理模式通过全局函数或宏函数来代理Logger类的log、debug、info、warn、error、fatal等接⼝以便于控制源码⽂件名称和⾏号的输出控制简化⽤⼾操作。 当仅需标准输出⽇志的时候可以通过主⽇志器来打印⽇志。 且操作时只需要通过宏函数直接进⾏输出 即可。 #ifndef _MY_LOG_ #define _MY_LOG_ #includelogger.hppnamespace dhylog {//1.提供获取指定日志器的全局接口避免用户自己操作单例对象Logger::ptr getLogger(const std::string name){return dhylog::LoggerManager::getInstance().getLogger(name);}Logger::ptr rootLogger(){return dhylog::LoggerManager::getInstance().rootLogger();}//2.使用宏函数对日志器的接口进行代理代理模式#define debug(fmt, ...) debug(__FILE__, __LINE__, fmt, ##__VA_ARGS__)#define info(fmt, ...) info(__FILE__, __LINE__, fmt, ##__VA_ARGS__)#define warn(fmt, ...) warn(__FILE__, __LINE__, fmt, ##__VA_ARGS__)#define error(fmt, ...) error(__FILE__, __LINE__, fmt, ##__VA_ARGS__)#define fatal(fmt, ...) fatal(__FILE__, __LINE__, fmt, ##__VA_ARGS__)//3.提供宏函数直接通过默认日志器进行日志标准输出打印不用获取日志器了#define DEBUG(fmt,...) dhylog::rootLogger()-debug(fmt, ##__VA_ARGS__)#define INFO(fmt,...) dhylog::rootLogger()-info(fmt, ##__VA_ARGS__)#define WARN(fmt,...) dhylog::rootLogger()-warn(fmt, ##__VA_ARGS__)#define ERROR(fmt,...) dhylog::rootLogger()-error(fmt, ##__VA_ARGS__)#define FATAL(fmt,...) dhylog::rootLogger()-fatal(fmt, ##__VA_ARGS__) } #endif9. 功能⽤例 #include ../logs/dhylog.hpp #includeunistd.h void test_log(const std::string name) {INFO(%s, 测试开始);dhylog::Logger::ptr loggerdhylog::LoggerManager::getInstance().getLogger(name);logger-debug( %s, 测试日志);logger-info( %s, 测试日志);logger-error(%s, 测试日志);logger-warn( %s, 测试日志);logger-fatal(%s, 测试日志);INFO( %s, 测试结束); } int main() {std::unique_ptrdhylog::LoggerBuilder builder(new dhylog::GlobalLoggerBuilder());builder-buildLoggerName(async_logger);builder-buildLoggerLevel(dhylog::loglevel::value::WARN);builder-buildFormatter([%c][%f:%l]%m%n);builder-buildLoggerType(dhylog::LoggerType::LOGGER_ASYNC);//builder-buildEnableUnSafeAsync();builder-buildSinkdhylog::FileSink(./logfile/async.log);builder-buildSinkdhylog::StdoutSink();builder-buildSinkdhylog::RollSink(./logfile/roll-async-by-size,1024*1024);builder-build();test_log(async_logger);return 0; } 10.性能测试 下⾯对⽇志系统做⼀个性能测试测试⼀下平均每秒能打印多少条⽇志消息到⽂件。 主要的测试⽅法是每秒能打印⽇志数 打印⽇志条数 / 总的打印⽇志消耗时间 主要测试要素同步/异步 单线程/多线程 • 100w条指定⻓度的⽇志输出所耗时间 • 每秒可以输出多少条⽇志 • 每秒可以输出多少MB⽇志 测试环境 #include ../logs/dhylog.hpp #includevector #includethread #includechronovoid bench(const std::string logger_name,size_t thr_count,size_t msg_count,size_t msg_len) {dhylog::Logger::ptr loggerdhylog::getLogger(logger_name);if(logger.get()nullptr){return;}std::cout测试日志msg_count条总大小:(msg_count*msg_len)/1024kb\n;std::string msg(msg_len-1,A);std::vectorstd::thread threads;std::vectordouble cost_arry(thr_count);size_t msg_per_thrmsg_count/thr_count;for(int i0;ithr_count;i){threads.emplace_back([,i](){auto startstd::chrono::high_resolution_clock::now();for(int j0;jmsg_per_thr;j){logger-fatal(%s,msg.c_str());}auto endstd::chrono::high_resolution_clock::now();std::chrono::durationdouble costend-start;cost_arry[i]cost.count();std::cout线程i:\t输出数量msg_per_thr,耗时cost.count()sstd::endl;});}for(int i0;ithr_count;i){threads[i].join();}double max_costcost_arry[0];for(int i0;ithr_count;i) max_costmax_costcost_arry[i]? cost_arry[i]:max_cost;size_t msg_per_secmsg_count/max_cost;size_t size_per_sec(msg_count*msg_len)/(max_cost*1024);std::cout\t总耗时:max_costs\n;std::cout\t每秒输出日志数量:msg_per_sec条\n;std::cout\t每秒输出日志大小:size_per_secKB\n; } void sync_bench() {std::unique_ptrdhylog::LoggerBuilder builder(new dhylog::GlobalLoggerBuilder());builder-buildLoggerName(sync_logger);builder-buildFormatter(%m%n);builder-buildLoggerType(dhylog::LoggerType::LOGGER_SYNC);builder-buildSinkdhylog::FileSink(./logfile/sync.log);builder-build();bench(sync_logger,1,1000000,100);} void async_bench() {std::unique_ptrdhylog::LoggerBuilder builder(new dhylog::GlobalLoggerBuilder());builder-buildLoggerName(async_logger);builder-buildFormatter(%m%n);builder-buildLoggerType(dhylog::LoggerType::LOGGER_ASYNC);builder-buildEnableUnSafeAsync();builder-buildSinkdhylog::FileSink(./logfile/async.log);builder-build();bench(async_logger,3,1000000,100);} int main() {sync_bench();//async_bench();return 0;}11.参考文献 参考资料 https://www.imangodoc.com/174918.html https://blog.csdn.net/w1014074794/article/details/125074038 https://zhuanlan.zhihu.com/p/472569975 https://zhuanlan.zhihu.com/p/460476053 https://gitee.com/davidditao/DDlog https://www.cnblogs.com/ailumiyana/p/9519614.html https://gitee.com/lqk1949/plog/ https://www.cnblogs.com/horacle/p/15494358.html https://blog.csdn.net/qq_29220369/article/details/127314390
http://www.dnsts.com.cn/news/52211.html

相关文章:

  • 齐河县城乡建设局网站苏州制作网页公司
  • 做网站学哪方面知识外贸行业网站建设公司
  • 工信部网站备案查询 验证码错误南通外贸建站
  • 企业网站优化怎么提高关键词排名世界足球排名前100名
  • 厦门学网站建设wordpress 更改 邮箱
  • 做网站 租服务器吗中国最新军事新闻 今天
  • 做网站经费如何给自己网站做反链
  • 查询网站的外链网站建设合作合同范文
  • 濮阳做网站推广的公司网站备案要邮寄资料吗
  • 做神马网站快速排心悦会员荣誉战场两张免做卡网站
  • 网站建设是什么科目正能量不良网站进入窗口免费阅读
  • 长春建站优化加徽信xiala5哪个网站学做凉皮
  • 资讯门户网站怎么做广告公司简介模板100字
  • 带后台管理的网站模板如何推广
  • wordpress 网站重置杭州建站价格
  • 做网站前台要学哪些网站开发运营费用
  • 可以转app的网站怎么做凡科建站登录入口官方正版
  • 付网站建设服务费什么科目室内设计短期培训学校
  • 网站建设公司广州中国建设人才招聘官网
  • 做英文兼职的网站有哪些网站建设的实施方案
  • 嘉兴建设规划网站沈阳哪家公司做网站好
  • 公司网站链接怎么弄图片模板在线设计制作
  • 怎么查看网站的点击率百度免费网站怎样建设
  • filetype doc 网站建设批量建站怎么赚钱
  • 设计作品欣赏网站做网站推广员必备的条件
  • 沈阳网站建设培训黔南服务好的高端网站设计公司
  • 企业网站建设的一般要素包括什么网络营销模式的优势
  • 网站运行环境建设方案在线制作图网站
  • 原生app开发教程专业的网站优化公司
  • 福建微网站建设价格广饶网站建设