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

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

一、日志项目的介绍

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、⽣产环境的产品为了保证其稳定性及安全性是不允许开发⼈员附加调试器去排查问题&#xff0c;可以借助日志系统来打印⼀些⽇志帮助开发⼈员解决问题 为什么不直接printf打印在屏幕上呢&#xff1f;&#xff1f;因为现实中没有…...

微服务设计原则——功能设计

文章目录 1.ID生成2.数值精度3.DB操作4.性能测试5.版本兼容5.1 向旧兼容5.2 向新兼容 6.异步时序问题7.并发问题7.1 并发时序7.2 并发数据竞争 参考文献 1.ID生成 在分布式系统中&#xff0c;生成全局唯一ID是非常重要的需求&#xff0c;因为需要确保不同节点、服务或实例在并…...

低代码软件搭建自学的第一天——熟悉PyQt

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 学习计划第 1 步&#xff1a;环境搭建1.1 安装 Python1.2 安装 PyQt安装命令&#xff1a;验证安装&#xff1a; 第 2 步&#xff1a;PyQt 基础知识2.1 创建第一个窗…...

基于Python3编写的Golang程序多平台交叉编译自动化脚本

import argparse import os import shutil import sys from shutil import copy2from loguru import loggerclass GoBuild:"""一个用于构建跨平台执行文件的类。初始化函数&#xff0c;设置构建的主文件、生成的执行文件名称以及目标平台。:param f: 需要构建的…...

远程桌面连接

电脑A&#xff1a;使用机 电脑B&#xff1a;被控制的另一个 方法1&#xff1a; 在电脑B上操作 ①winr输入cmd进入命令行窗口&#xff0c;输入ipconfig查询本机地址 ②我的电脑/此电脑 右键点击“属性” ③选择屏幕右边“远程桌面” ④打开“启用远程桌面” ⑤打开设置&am…...

网络地址转换NAT

NAT(Network Address Translation) 方法于1994年提出。需要在专用网连接到因特网的路由器上安装NAT软件。装有NAT软件的路由器叫做NAT路由器&#xff0c;它至少有一个有效的外部全球地址IPG。 所有使用本地地址的主机在和外界通信时都要在NAT路由器上将其本地地址转换成外部全球…...

什么是CRM管理软件?CRM的基本概念、功能、选择标准、应用场景

什么是CRM管理软件&#xff1f; 嘿&#xff0c;大家好&#xff01;今天咱们聊聊一个在现代企业管理中非常重要的工具——CRM管理软件。CRM是Customer Relationship Management&#xff08;客户关系管理&#xff09;的缩写&#xff0c;简单来说&#xff0c;它就是一个帮助企业和…...

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 中&#xff0c;AudioSource 组件用于控制音频的播放和管理。以下是常用的 AudioSource 控制方法及其说明。 1. 播放和暂停音频 Play()&#xff1a;开始播放音频&#xff0c;如果是从暂停的地方继续播放&#xff0c;可以直接调用。Pause()&#xff1a;暂停当前播放的…...

CSS系列(25)-- 滚动优化详解

前端技术探索系列&#xff1a;CSS 滚动优化详解 &#x1f4dc; 致读者&#xff1a;探索流畅滚动的艺术 &#x1f44b; 前端开发者们&#xff0c; 今天我们将深入探讨 CSS 滚动优化&#xff0c;学习如何创建流畅、高性能的滚动体验。 平滑滚动 &#x1f680; 基础设置 /* …...

CST天线设计的六大核心特点:为天线分析提供完整解决方案!

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

Ubuntu下C语言操作kafka示例

目录 安装kafka&#xff1a; 安装librdkafka consumer Producer 测试运行 安装kafka&#xff1a; Ubuntu下Kafka安装及使用_ubuntu安装kafka-CSDN博客 安装librdkafka github地址&#xff1a;GitHub - confluentinc/librdkafka: The Apache Kafka C/C library $ apt in…...

怎么将pdf中的某一个提取出来?介绍几种提取PDF中页面的方法

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

HTTP接口报错详解与解决 200,500,403,408,404

前言&#xff1a; 仅做学习记录&#xff0c;侵删 背景 当后端编写接口时&#xff0c;经常需要对接口使用ApiFox或者PostMan进行测试&#xff0c;此时就会出现各种各样的报错&#xff0c;一般都会包括报错编码&#xff1a;200,400,401等。这个状态码一般是服务器所返回的包含…...

监控IP频繁登录服务器脚本

该脚本的作用是监控IP登录失败次数&#xff0c;如果某个IP的登录失败次数超过设定的最大次数&#xff0c;则阻止该IP的进一步登录尝试。通过iptables防火墙阻止连接&#xff0c;当一个IP尝试登录次数超过5次时&#xff0c;iptables会阻止来自该IP的所有连接 #!/bin/bashfuncti…...

分布式链路追踪-03-Jaeger、Zipkin、skywalking 中的 span 是如何设计的?

开源项目 auto-log 自动日志输出 Jaeger、Zipkin 中的 spanId 是如何生成的&#xff1f; 在 Jaeger 和 Zipkin 这两个分布式跟踪系统中&#xff0c;Span ID 是通过不同的方法生成的。 下面分别介绍它们的生成方式&#xff1a; Jaeger 中的 Span ID 生成&#xff1a; 在 Ja…...

【达梦数据库】获取对象DDL

目录 背景获取表的DDL其他 背景 在排查问题时总会遇到获取对象DDL的问题&#xff0c;因此做以下总结。 获取表的DDL 设置disql工具中显示LONG类型数据的最大长度&#xff0c;避免截断&#xff1a; SET LONG 9999获取DDL SELECT DBMS_METADATA.GET_DDL(TABLE,表名,模式名) …...

InnoDB和MyISAM引擎优缺点和区别

nnoDB和MyISAM是MySQL数据库中常用的两种存储引擎。它们各自具有不同的特性和优势&#xff0c;适用于不同的应用场景。 一、InnoDB引擎&#xff1a; 1、它有如下特性&#xff1a; 1)、支持事务&#xff08;ACID&#xff09; 2)、支持外键约束&#xff08;FOREIGN KEY const…...

文件上传知识点汇总

归纳总结一下文件上传&#xff08;其实是懒得写wp&#xff09; 基于Dream ZHO师傅的CTF show 文件上传篇&#xff08;web151-170&#xff0c;看这一篇就够啦&#xff09;-CSDN博客 和dota_st 师傅的ctfshow-Web1000题系列修炼(一) | dota_st 做一篇自己的总结 目录 一、什么…...

计算机网络技术基础:5.数据通信系统

一、数据通信的基本概念 1.信息 信息是对客观事物的运动状态和存在形式的反映&#xff0c;可以是客观事实的形态、大小、结构、性能等描述&#xff0c;也可以是客观事物与外部之间的联系。信息的载体可以是数字、文字、语音、图形和图像等。计算机及其外围设备产生和交换的信息…...

光谱相机在农业的应用

一、作物生长监测1、营养状况评估 原理&#xff1a;不同的营养元素在植物体内的含量变化会导致植物叶片或其他组织的光谱反射率特性发生改变。例如&#xff0c;氮元素是植物叶绿素的重要组成部分&#xff0c;植物缺氮时&#xff0c;叶绿素含量下降&#xff0c;其在可见光波段&a…...

高考志愿填报:如何制定合理的志愿梯度?

高考志愿填报中常见的避雷行为&#xff0c;深入分析了专业选择、招生政策了解、学校选择、备选方案准备以及防诈骗等方面的关键问题&#xff0c;并提出了针对性的建议与策略。旨在为考生和家长提供实用的指导&#xff0c;助力考生科学合理地填报高考志愿&#xff0c;避免陷入各…...

Android基于Path的addRoundRect,Canvas剪切clipPath简洁的圆角矩形实现,Kotlin(1)

Android基于Path的addRoundRect&#xff0c;Canvas剪切clipPath简洁的圆角矩形实现&#xff0c;Kotlin&#xff08;1&#xff09; <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res…...

webGL硬核知识:图形渲染管渲染流程,各个阶段对应的API调用方式

一、图形渲染管线基础流程概述 WebGL 的图形渲染管线大致可分为以下几个主要阶段&#xff0c;每个阶段都有其特定的任务&#xff0c;协同工作将 3D 场景中的物体最终转换为屏幕上呈现的 2D 图像&#xff1a; 顶点处理&#xff08;Vertex Processing&#xff09;阶段&#xff1…...

区块链详解

1. 概述 1.1 什么是区块链&#xff1f; 区块链是一种分布式数据库技术&#xff0c;它以链式数据结构的形式存储数据&#xff0c;每个数据块与前一个数据块相关联&#xff0c;形成了一个不断增长的数据链。每个数据块中包含了一定数量的交易信息或其他数据&#xff0c;这些数据…...

【EXCEL 逻辑函数】AND、OR、XOR、NOT、IF、IFS、IFERROR、IFNA、SWITCH

目录 AND&#xff1a;当所有条件都为真时返回 TRUE&#xff0c;否则返回 FALSE OR&#xff1a;当任一条件为真时返回 TRUE&#xff0c;否则返回 FALSE XOR&#xff1a;当奇数个条件为真时返回 TRUE&#xff0c;否则返回 FALSE NOT &#xff1a;反转逻辑值 IF&#xff1a;根…...

ubuntu下gdb调试ROS

参考&#xff1a; 使用VsCode进行ROS程序调试_ros vscode 调试-CSDN博客 https://blog.csdn.net/weixin_45031801/article/details/134399664?spm1001.2014.3001.5506 一、调试准备 1.1 CMakeLists改动 注释文件中的 set(CMAKE_BUILD_TYPE "Release") #构建类…...

Docke_常用命令详解

这篇文章分享一下笔者常用的Docker命令供各位读者参考。 为什么要用Docker? 简单来说&#xff1a;Docker通过提供轻量级、隔离且可移植的容器化环境&#xff0c;使得应用在不同平台上保持一致性、易于部署和管理&#xff0c;具体如下 环境一致性&#xff1a; Docker容器使得…...

使用vue2.0或vue3.0创建自定义组件

Vue2.0创建自定义组件 在 Vue 2.0 中创建自定义组件是一个相对简单的过程。以下是一个详细的步骤指南&#xff0c;帮助你创建一个自定义组件。 步骤 1: 创建 Vue 组件文件 首先&#xff0c;你需要创建一个新的 Vue 文件&#xff08;.vue 文件&#xff09;。假设我们要创建一…...

Elasticsearch-DSL高级查询操作

一、禁用元数据和过滤数据 1、禁用元数据_source GET product/_search {"_source": false, "query": {"match_all": {}} }查询结果不显示元数据 禁用之前: {"took" : 0,"timed_out" : false,"_shards" : {&quo…...