同步异步日志系统:前置知识
一、日志项目的介绍
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;
}
相关文章:

同步异步日志系统:前置知识
一、日志项目的介绍 1.1 为什么要有日志系统 1、⽣产环境的产品为了保证其稳定性及安全性是不允许开发⼈员附加调试器去排查问题,可以借助日志系统来打印⼀些⽇志帮助开发⼈员解决问题 为什么不直接printf打印在屏幕上呢??因为现实中没有…...
微服务设计原则——功能设计
文章目录 1.ID生成2.数值精度3.DB操作4.性能测试5.版本兼容5.1 向旧兼容5.2 向新兼容 6.异步时序问题7.并发问题7.1 并发时序7.2 并发数据竞争 参考文献 1.ID生成 在分布式系统中,生成全局唯一ID是非常重要的需求,因为需要确保不同节点、服务或实例在并…...

低代码软件搭建自学的第一天——熟悉PyQt
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 学习计划第 1 步:环境搭建1.1 安装 Python1.2 安装 PyQt安装命令:验证安装: 第 2 步:PyQt 基础知识2.1 创建第一个窗…...

基于Python3编写的Golang程序多平台交叉编译自动化脚本
import argparse import os import shutil import sys from shutil import copy2from loguru import loggerclass GoBuild:"""一个用于构建跨平台执行文件的类。初始化函数,设置构建的主文件、生成的执行文件名称以及目标平台。:param f: 需要构建的…...

远程桌面连接
电脑A:使用机 电脑B:被控制的另一个 方法1: 在电脑B上操作 ①winr输入cmd进入命令行窗口,输入ipconfig查询本机地址 ②我的电脑/此电脑 右键点击“属性” ③选择屏幕右边“远程桌面” ④打开“启用远程桌面” ⑤打开设置&am…...
网络地址转换NAT
NAT(Network Address Translation) 方法于1994年提出。需要在专用网连接到因特网的路由器上安装NAT软件。装有NAT软件的路由器叫做NAT路由器,它至少有一个有效的外部全球地址IPG。 所有使用本地地址的主机在和外界通信时都要在NAT路由器上将其本地地址转换成外部全球…...

什么是CRM管理软件?CRM的基本概念、功能、选择标准、应用场景
什么是CRM管理软件? 嘿,大家好!今天咱们聊聊一个在现代企业管理中非常重要的工具——CRM管理软件。CRM是Customer Relationship Management(客户关系管理)的缩写,简单来说,它就是一个帮助企业和…...

Python编程常用的19个经典案例
Python 的简洁和强大使其成为许多开发者的首选语言。本文将介绍36个常用的Python经典代码案例。这些示例覆盖了基础语法、常见任务、以及一些高级功能。 1. 列表推导式 fizz_buzz_list ["FizzBuzz" if i % 15 0 else "Fizz" if i % 3 0 else "Buzz…...

【Unity基础】AudioSource 常用方法总结
在 Unity 中,AudioSource 组件用于控制音频的播放和管理。以下是常用的 AudioSource 控制方法及其说明。 1. 播放和暂停音频 Play():开始播放音频,如果是从暂停的地方继续播放,可以直接调用。Pause():暂停当前播放的…...
CSS系列(25)-- 滚动优化详解
前端技术探索系列:CSS 滚动优化详解 📜 致读者:探索流畅滚动的艺术 👋 前端开发者们, 今天我们将深入探讨 CSS 滚动优化,学习如何创建流畅、高性能的滚动体验。 平滑滚动 🚀 基础设置 /* …...

CST天线设计的六大核心特点:为天线分析提供完整解决方案!
CST Studio Suite 为天线设计提供了从最初的概念评估到最终的合规性测试所需的所有功能,确保天线设计在各种环境下实现稳定通信。这一套工具覆盖了所有重要的设计阶段,帮助设计师顺利完成从概念到成品的全过程。 下面我们来看一看CST电磁仿真中天线设计…...

Ubuntu下C语言操作kafka示例
目录 安装kafka: 安装librdkafka consumer Producer 测试运行 安装kafka: Ubuntu下Kafka安装及使用_ubuntu安装kafka-CSDN博客 安装librdkafka github地址:GitHub - confluentinc/librdkafka: The Apache Kafka C/C library $ apt in…...

怎么将pdf中的某一个提取出来?介绍几种提取PDF中页面的方法
怎么将pdf中的某一个提取出来?传统上,我们可能通过手动截取屏幕或使用PDF阅读器的复制功能来提取信息,但这种方法往往不够精确,且无法保留原文档的排版和格式。此外,很多时候我们需要提取的内容可能涉及多个页面、多个…...

HTTP接口报错详解与解决 200,500,403,408,404
前言: 仅做学习记录,侵删 背景 当后端编写接口时,经常需要对接口使用ApiFox或者PostMan进行测试,此时就会出现各种各样的报错,一般都会包括报错编码:200,400,401等。这个状态码一般是服务器所返回的包含…...
监控IP频繁登录服务器脚本
该脚本的作用是监控IP登录失败次数,如果某个IP的登录失败次数超过设定的最大次数,则阻止该IP的进一步登录尝试。通过iptables防火墙阻止连接,当一个IP尝试登录次数超过5次时,iptables会阻止来自该IP的所有连接 #!/bin/bashfuncti…...
分布式链路追踪-03-Jaeger、Zipkin、skywalking 中的 span 是如何设计的?
开源项目 auto-log 自动日志输出 Jaeger、Zipkin 中的 spanId 是如何生成的? 在 Jaeger 和 Zipkin 这两个分布式跟踪系统中,Span ID 是通过不同的方法生成的。 下面分别介绍它们的生成方式: Jaeger 中的 Span ID 生成: 在 Ja…...
【达梦数据库】获取对象DDL
目录 背景获取表的DDL其他 背景 在排查问题时总会遇到获取对象DDL的问题,因此做以下总结。 获取表的DDL 设置disql工具中显示LONG类型数据的最大长度,避免截断: SET LONG 9999获取DDL SELECT DBMS_METADATA.GET_DDL(TABLE,表名,模式名) …...
InnoDB和MyISAM引擎优缺点和区别
nnoDB和MyISAM是MySQL数据库中常用的两种存储引擎。它们各自具有不同的特性和优势,适用于不同的应用场景。 一、InnoDB引擎: 1、它有如下特性: 1)、支持事务(ACID) 2)、支持外键约束(FOREIGN KEY const…...

文件上传知识点汇总
归纳总结一下文件上传(其实是懒得写wp) 基于Dream ZHO师傅的CTF show 文件上传篇(web151-170,看这一篇就够啦)-CSDN博客 和dota_st 师傅的ctfshow-Web1000题系列修炼(一) | dota_st 做一篇自己的总结 目录 一、什么…...
计算机网络技术基础:5.数据通信系统
一、数据通信的基本概念 1.信息 信息是对客观事物的运动状态和存在形式的反映,可以是客观事实的形态、大小、结构、性能等描述,也可以是客观事物与外部之间的联系。信息的载体可以是数字、文字、语音、图形和图像等。计算机及其外围设备产生和交换的信息…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...

什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...