同步异步日志系统:前置知识

一、日志项目的介绍

1.1 为什么要有日志系统

1、⽣产环境的产品为了保证其稳定性及安全性是不允许开发⼈员附加调试器去排查问题,可以借助日志系统来打印⼀些⽇志帮助开发⼈员解决问题  

     为什么不直接printf打印在屏幕上呢??因为现实中没有谁能够一整天都盯着机房看系统运行信息,而且刷新可能很快根本看不过来

2、上线客户端的产品出现bug⽆法复现并解决,可以借助⽇志系统打印日志并上传到服务端帮助开发人员进行分析  

     为什么不允许使用调试器呢??因为使用gdb需要对程序的数据进行分析,有些时候是不被允许的,一些工程内的数据都是有隐私,有安全要求的!!

3、对于⼀些高频操作(如定时器、心跳包)在少量调试次数下可能无法触发我们想要的行为,通过断点的暂停⽅式,我们不得不重复操作几十次、上百次甚至更多,导致排查问题效率是非常低下,可以借助打印日志的方式查问题

 4、在分布式、多线程/多进程代码中,出现bug比较难以定位,可以借助日志系统打印log帮助定位bug

    日志可以快读定位bug出现在哪一个模块从而帮助程序员进行更好的分析 

5、帮助首次接触项目代码的新开发人员理解代码的运行流程  

1.2 日志系统的作用简述

日志:程序运行过程中所记录的程序运行状态(时间、错误原因、行号……)

作用:记录了程序运行状态信息,以便于程序员能够随时根据状态信息对系统的运行状态进行分析

一般来说日志都是作为一个小组件给其他业务打辅助用的,所以我们为了确保他能够更高效地开发,除了他能够正常使用之外,必要时候也要去阐述他的具体性能!

1.3 需要实现的功能

1、⽀持多级别日志消息  

     区分各种信息的程度,比如调试、警告、致命……。同时要让程序在发布的时候不要输出调试的信息,而是只输出那些让我们程序出错的信息(设置输出限制,比如未发布的时候设置为调试级别,发布时设为错误级别即低于错误的都不输出)

2、⽀持同步日志和异步日志

        同步就是将业务数据写入到数据库的操作由我的业务线程自己完成,而异步是我将数据放到内存里面,而写入的操作由一些专门负责工作的线程负责(因为如果都由我负责,那么万一写入有问题就会导致业务也做不了了)

3、支持可靠写入日志到控制台、文件以及滚动文件中  

      日志信息可以写到控制台、写到文件、滚动(切换)文件 也可以三个同时进行 想怎么输出由自己决定 

      滚动文件就是我们如果一直往一个文件里写入那么可能就会很大,所以当一个文件写到一定程度(可以按照文件大小,也可以按照日期来切换,然后设置一个定时任务每天清理3天以前的日志,只保留3天以内的日志文件)的时候切换下一个文件来进行写入

4、支持多线程程序并发写日志 (保证线程安全)

5、⽀持扩展不同的⽇志落地⽬标地  

    同时也支持扩展,比方说支持把日志信息写到数据库里,或者写到一个日志分析服务器里

1.4 开发环境&核心技术&环境搭建

开发环境(用到哪些软件):

• CentOS7

• vscode/vim

• g++/gdb

• Makefile

核心技术(会用到哪些前置知识):

• 类层次设计(继承和多态的应⽤)

• C++11(多线程、auto、智能指针、右值引⽤等)

• 双缓冲区

• ⽣产消费模型

• 多线程

• 设计模式(单例、工厂、代理、建造者等)

 环境搭建(会用到哪个第三方库)

 本项⽬不依赖其他任何第三⽅库,只需要安装好CentOS/Ubuntu+vscode/vim环境即可开发。

1.5 日志系统的技术实现 

⽇志系统的技术实现主要包括三种类型:

1、利⽤printf、std::cout等输出函数将⽇志信息打印到控制台

2、对于⼤型商业化项⽬,为了⽅便排查问题,我们⼀般会将⽇志输出到⽂件或者是数据库系统⽅便查询和分析⽇志,主要分为同步⽇志和异步⽇志⽅式

1.5.1 同步写日志

       同步⽇志是指当输出⽇志时,必须等待⽇志输出语句执⾏完毕后,才能执⾏后⾯的业务逻辑语句,日志输出语句与程序的业务逻辑语句将在同⼀个线程运行。每次调⽤⼀次打印⽇志API就对应⼀次系统调⽤write写⽇志⽂件。   

在高并发场景下,随着日志数量不断增加,同步日志系统容易产生系统瓶颈:   

• ⼀⽅⾯,⼤量的⽇志打印陷⼊等量的write系统调⽤,有⼀定系统开销.

• 另⼀⽅⾯,使得打印⽇志的进程附带了⼤量同步的磁盘IO,影响程序性能

1.5.2 异步写日志

     异步⽇志是指在进⾏⽇志输出时,日志输出语句与业务逻辑语句并不是在同⼀个线程中运行,而是有专门的线程用于进行日志输出操作。业务线程只需要将⽇志放到⼀个内存缓冲区中不⽤等待即可继续执⾏后续业务逻辑(作为⽇志的⽣产者),⽽⽇志的落地操作交给单独的⽇志线程去完成(作为⽇志的消费者),这是⼀个典型的⽣产-消费模型。

这样做的好处是即使日志没有真的地完成输出也不会影响程序的主业务,可以提⾼程序的性能: 

• 主线程调⽤日志打印接⼝成为非阻塞操作

• 同步的磁盘IO从主线程中剥离出来交给单独的线程完成 

二、不定参函数

      在初学C语⾔的时候,我们都⽤过printf函数进⾏打印。其中printf函数就是⼀个不定参函数,在函数内部可以根据格式化字符串中格式化字符分别获取不同的参数进⾏数据的格式化。⽽这种不定参函数在实际的使⽤中也⾮常多⻅!!!

2.1 不定参宏函数

         我们起初的风格可以这样写,但是这样写的话,我们每次写一些固定的变量的时候都需重复书写(比如 __FILE__和__LINE__)

#include<iostream>
using namespace std;
int main()
{
 printf("[%s:%d] %s-%d\n", __FILE__, __LINE__, "hello",666);
 return 0;
}

     所以我们希望 __FILE__和__LINE__这俩无论任何一条日志都要打印的信息,给他设置到宏函数里面!

1、通过把宏参数列表中最后的参数写成省略号(...),使其可以接受数量可变的宏参数。

2、后边用不定参数宏__VA_ARGS__可以自动扩展 

按理来说一定要带参数否则__VA_ARGS__无法扩展,最后替换时会留一个‘,’导致错误,但是我们可以用##来避免这个问题

3、## __VA_ARGS__是为了确保当没有传入任何参数的时候,把最后面的‘,’给去掉

##告诉编译器。如果我没有传任何参数给__VA_ARGS__,那么就把前面的‘,’去掉 

#include<iostream>
#define LOG(fmt, ...) printf("[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)

int main()
{
 //printf("[%s:%d] %s-%d\n", __FILE__, __LINE__, "hello",666);
 LOG("%s-%d", "hello", 666);
 return 0;
}

运行结果: 

     使用了宏定义来实现日志功能,虽然这种方式可以达到目的,但宏定义在C++中不够类型安全,并且调试时不如函数调用方便。 

2.2 C风格不定参函数

头文件strarg.h中定义了一组对象、方法使得我们可以使用不定参数。

  • va_list ap:用于储存省略部分数据的对象类型
  • va_start(format, ap):使得ap指向format后的不定参数列表,即不定参数列表中的第一个参数
  • int tmp = va_arg(ap, int):将当前ap指向的值返回,并使ap指针按照type类型向后移动,va_arg中第二个参数类型名要与返回值类型相同(决定了向后移动几个字节)
  • va_end(ap):完成清理工作,释放动态分配申请的用于存储参数的内存
#include <iostream>
#include <cstdarg>
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);//从可变参数中取出⼀个整形参数,然后向后移动int个字节
 std::cout << num << std::endl;
 }
 va_end(al);//清空可变参数列表--其实是将al置空 
}
int main()
{
 printNum(3, 11,22,33);
 printNum(5, 44,55,66,77,88);
 return 0;
}

 

注意:虽然是不定参,但是也要遵守规定,比如第一个数字就是专门用来确定后面有多少个参数的!如果乱写的话可能导致未定义的行为,因为va_arg会尝试读取超出传入参数数量的内存。

 如果我们的va_argc传的类型不匹配呢??那这必然导致我们读到的数据是错的!!!

     所以这也是为什么printf有格式化字符串,就是为了告诉编译器接下来要从后面读几个字节的数据,应该当做什么类型去做处理!

    vasprintf 是一个C库函数,它允许通过可变参数列表创建格式化字符串,并将其存储在动态分配的内存中。这个函数的行为类似于printf,但它不会将结果输出到标准输出,而是将格式化后的字符串存储在一个字符指针变量中。 

 char**strp:一级指针的地址,会在动态分配的内存中给我们的格式化字符串分配足够的空间

const char*fmt:带格式化的字符串 

va_list ap:从ap里面一个个取参数进行解析,然后将组织好的字符串放到我们预先申请的空间里

注意因为这个空间是由OS帮我们申请的,所以最后我们一定要记得free!! 

成功的话会返回对应的字节数,失败的话会返回-1 

#define _GNU_SOURCE 
#include<iostream>
#include<cstdarg>
#include<stdlib.h>
void myprintf(const char* fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    char*res; //不需要我们自己申请
    int ret= vasprintf(&res,fmt,ap);
    if(ret!=-1)
    {
        printf(res);
        free(res);
    }
    va_end(ap);
}
int main()
{
    myprintf("hello %s", "world");
    return 0;
}

 

2.3 C++风格不定参函数

 cpp使用的是模版参数包

 sizeof …(args)是固定写法

因为递归到最后并没有空函数的xprintf,因此我们还得定义一个没有函数的xprintf

#include <iostream>
#include <cstdarg>
#include <memory>
#include <functional>
void xprintf() {
 std::cout << std::endl;
}
template<typename T, typename ...Args>
void xprintf(const T &value, Args &&...args) {
 std::cout << value << " ";
 if ((sizeof ...(args)) > 0) {
 xprintf(std::forward<Args>(args)...);
 }else {
 xprintf();
 }
}
int main()
{
 xprintf("hello");
 xprintf("hello", 666);
 xprintf("hello", "world", 666);
 return 0;
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/939851.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

搭建私有链

文章目录 1. 准备工作2. 创建创世区块配置文件2.1 创建数据目录2.2 创建创世区块配置文件1. “config”部分2. “alloc”部分3. “coinbase”4. “difficulty”5. “extraData”6. “gasLimit”7. “nonce”8. “mixhash”9. “parentHash”10. “timestamp” 3. 初始化&#x…

国标GB28181平台EasyGBS在安防视频监控中的信号传输(电源/视频/音频)特性及差异

在现代安防视频监控系统中&#xff0c;国标GB28181协议作为公共安全视频监控联网系统的国家标准&#xff0c;该协议不仅规范了视频监控系统的信息传输、交换和控制技术要求&#xff0c;还为不同厂商设备之间的互联互通提供了统一的框架。EasyGBS平台基于GB28181协议&#xff0c…

详细解读TISAX认证的意义

详细解读TISAX认证的意义&#xff0c;犹如揭开信息安全领域的一颗璀璨明珠&#xff0c;它不仅代表了企业在信息安全管理方面的卓越成就&#xff0c;更是通往全球汽车供应链信任桥梁的关键一环。TISAX&#xff0c;即“Trusted Information Security Assessment Exchange”&#…

Pytorch | 从零构建AlexNet对CIFAR10进行分类

Pytorch | 从零构建AlexNet对CIFAR10进行分类 CIFAR10数据集AlexNet网络结构技术创新点性能表现影响和意义 AlexNet结构代码详解结构代码代码详解特征提取层 self.features分类部分self.classifier前向传播forward 训练过程和测试结果代码汇总alexnet.pytrain.pytest.py CIFAR1…

初学stm32 --- 系统时钟配置

众所周知&#xff0c;时钟系统是 CPU 的脉搏&#xff0c;就像人的心跳一样。所以时钟系统的重要性就不言而喻了。 STM32 的时钟系统比较复杂&#xff0c;不像简单的 51 单片机一个系统时钟就可以解决一切。于是有人要问&#xff0c;采用一个系统时钟不是很简单吗&#xff1f;为…

进程间通信方式---System V IPC信号量

进程间通信方式—System V IPC信号量 文章目录 进程间通信方式---System V IPC信号量信号量1.信号量原语2.semget 系统调用参数返回值 3.semop 系统调用参数返回值 4.semctl 系统调用5.特殊键值 IPC_PRIVATE6.信号量实现进程间通信1. 数据结构定义2. 信号量操作相关部分3. 生产…

深入理解Kafka:核心设计与实践原理读书笔记

目录 初识Kafka基本概念安装与配置ZooKeeper安装与配置Kafka的安装与配置 生产与消费服务端参数配置 生产者客户端开发消息对象 ProducerRecord必要的参数配置发送消息序列化分区器生产者拦截器 原理分析整体架构元数据的更新 重要的生产者参数acksmax.request.sizeretries和re…

electron 顶部的元素点不中,点击事件不生效

electron 顶部的元素点不中&#xff0c;点击事件不生效

Excel设置生日自动智能提醒,公式可直接套用!

大家好&#xff0c;我是小鱼。 今天跟大家分享一个WPS表格中根据出生日期&#xff0c;设置生日提醒&#xff0c;并且根据距离生日天数自动标记数据颜色。简单又实用&#xff0c;一个公式轻松搞定&#xff01; 接下来我们先学习一下需要使用到的函数&#xff0c;然后再根据实例让…

全域数据集成平台ETL

全域数据集成平台ETL Restcloud 工作原理 RestCloud数据集成平台采用SpringCloud微服务架构技术开发&#xff0c;底层基于纯Java语言采用前后端分离架构&#xff0c;前端采用React技术进行开发。 RestCloud数据集成平台是基于数据流工作流引擎的架构进行研发的&#xff0c;底…

Spring(一)---IOC(控制权反转)

目录 引入 1.什么叫IOC(Inversion of Control)控制权反转&#xff1f; 2.什么叫AOP(Aspect-Oriented Programming)面向切面编程(涉及Java代理)&#xff1f; 3.简单谈一下Java怎么实现ICO? Spring框架的介绍 1. Spring框架的概述 2. Spring框架的优点 Spring IOC容器介绍…

【GESP】C++二级考试大纲知识点梳理, (4)流程图

GESP C二级官方考试大纲中&#xff0c;共有9条考点&#xff0c;本文针对C&#xff08;4&#xff09;号知识点进行总结梳理。 &#xff08;4&#xff09;了解流程图的概念及基本表示符号&#xff0c;掌握绘制流程图的方法&#xff0c;能正确使用流程图描述程序设计的三种基本结构…

scala中正则表达式的使用

正则表达式&#xff1a; 基本概念 在 Scala 中&#xff0c;正则表达式是用于处理文本模式匹配的强大工具。它通过java.util.regex.Pattern和java.util.regex.Matcher这两个 Java 类来实现&#xff08;因为 Scala 运行在 Java 虚拟机上&#xff0c;可以无缝使用 Java 类库&…

使用VSCode Debugger 调试 React项目

一般我们调试代码时&#xff0c;用的最多的应该就是console.log方式了&#xff0c;还有的是使用Chrome DevTools 通过在对应的 sourcemap代码位置打断点进行调试&#xff0c;除了上面两种方式外还有一种更好用的调试方式&#xff1a; VSCode Debugger。 VSCode Debugger可以直…

微信小程序实现上传图片自定义水印功能、放大缩小旋转删除、自定义字号颜色位置、图片导出下载、图像预览裁剪、Canvas绘制 开箱即用

目录 功能实现画布绘制上传图片并渲染图片操作事件添加文字水印canvas解析微信小程序中 canvas 的应用场景canvas 与 2D 上下文、webgl 上下文的关系图像的加载与绘制总结说明功能实现 画布绘制 在wxml添加canvas标签并在在当前页面的 data 对象中,创建一个 Canvas 上下文(c…

用.Net Core框架创建一个Web API接口服务器

我们选择一个Web Api类型的项目创建一个解决方案为解决方案取一个名称我们这里选择的是。Net 8.0框架 注意&#xff0c;需要勾选的项。 我们找到appsetting.json配置文件 appsettings.json配置文件内容如下 {"Logging": {"LogLevel": {"Default&quo…

[创业之路-199]:《华为战略管理法-DSTE实战体系》- 3 - 价值转移理论与利润区理论

目录 一、价值转移理论 1.1. 什么是价值&#xff1f; 1.2. 什么价值创造 &#xff08;1&#xff09;、定义 &#xff08;2&#xff09;、影响价值创造的因素 &#xff08;3&#xff09;、价值创造的三个过程 &#xff08;4&#xff09;、价值创造的实践 &#xff08;5&…

【阅读记录-章节6】Build a Large Language Model (From Scratch)

文章目录 6. Fine-tuning for classification6.1 Different categories of fine-tuning6.2 Preparing the dataset第一步&#xff1a;下载并解压数据集第二步&#xff1a;检查类别标签分布第三步&#xff1a;创建平衡数据集第四步&#xff1a;数据集拆分 6.3 Creating data loa…

[搜广推]王树森推荐系统——矩阵补充最近邻查找

矩阵补充&#xff08;工业界不常用&#xff09; 模型结构 embedding可以把 用户ID 或者 物品ID 映射成向量输入用户ID 和 物品ID&#xff0c;输出向量的内积&#xff08;一个实数&#xff09;&#xff0c;内积越大说明用户对这个物品越感兴趣模型中的两个embedding层不共享参…

【优选算法篇】揭秘快速排序:分治算法如何突破性能瓶颈

文章目录 须知 &#x1f4ac; 欢迎讨论&#xff1a;如果你在学习过程中有任何问题或想法&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流学习。你的支持是我继续创作的动力&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;觉得这篇文章对你有帮助吗&#xff1…