项目日志——日志器模块一部缓冲区的设计、实现、测试

news2024/11/25 15:27:29

文章目录

    • 异步缓冲区模块
      • 模块设计
      • 缓冲区设计
        • 单个缓冲区
      • 实现
      • 测试

异步缓冲区模块

模块设计

异步日志器的思想是为了避免业务线程因为写日志的过程时间较长而长时间阻塞

异步日志器的工作就是把业务输出的日志内容放入内存缓冲区中,使用专门的线程进行日志写入

这个模块的主要内容是

  1. 实现一个线程安全缓冲区
  2. 创建一个异步工作线程,专门负责缓冲区日志消息的落地操作

缓冲区设计

  1. 使用队列缓存日志消息,进行逐条处理,要求不涉及到空间的频繁申请和释放,否则会降低效率
  2. 使用环形队列,提前将空间申请号,然后对空间循环利用
  3. 缓冲区会涉及多线程,需要保证线程安全
  4. 写日志操作只需要一个线程,涉及到的锁冲突是生产者与生产者的互斥,生产者和消费者的互斥,冲突较为严重
  5. 双缓冲区思想,第一个为任务写入的缓冲区,第二个是任务处理缓冲区

请添加图片描述

双缓冲区的好处是,降低了生产者和消费者之间的冲突,只有在交换的适合需要冲突一次

单个缓冲区

单个缓冲区如果让进程使用LogMsg类来回访问,会有很多构造的过程,这里我们直接用格式化后的日志消息字符串

减少LogMsg频繁构造的消耗,并且可以针对缓冲区中的日志消息,一次进行全部的IO操作,减少IO次数,提高整体消息

缓冲区类的设计:

  1. 使用vector进行空间管理,用作存放字符串中的日志消息
  2. 一个当前写入位置的指针和一个读取位置的指针,分别指向可写和可读的第一个位置
  3. 当两个指针相遇时,表示数据取完了

实现

/*
    实现异步日志缓冲区
*/
#pragma once
#include "util.hpp"
#include <vector>
#include <cassert>
namespace Xulog
{
// 设置默认缓冲区大小10MB,阈值缓冲区大小100MB,增量大小10MB
#define DEFAULT_BUFFER_SIZE (10 * 1024 * 1024)
#define THRESHOLD_BUFFER_SIZE (100 * 1024 * 1024)
#define INCREMENT_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)
        {
            // 缓冲区空间不够扩容或者阻塞
            // 实际场景:固定大小,则直接返回阻塞
            // if (len > writeAbleSize())
            //     return;
            // 极限测试:动态空间,则扩容写入
            ensureEnoughSize(len);
            // 数据写入缓冲区
            std::copy(data, data + len, &_buffer[_writer_idx]);
            // 写指针偏移
            moveWriter(len);
        }
        // 返回可读数据的起始地址
        const char *begin()
        {
            return &_buffer[_reader_idx];
        }
        // 返回可读数据的长度
        size_t readAbleSize()
        {
            // 单方向写入读出的缓冲区
            return (_writer_idx - _reader_idx);
        }
        // 返回可写的数据长度
        size_t writeAbleSize()
        {
            // 扩容测试,总可写
            return (_buffer.size() - _writer_idx);
        }
        // 读指针的偏移操作
        void moveReader(size_t len)
        {
            assert(len <= readAbleSize());
            _reader_idx += len;
        }
        // 重写读写位置,初始化
        void reset()
        {
            _reader_idx = 0;
            _writer_idx = 0;
        }
        // 交换
        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 moveWriter(size_t len)
        {
            assert(len + _writer_idx <= _buffer.size());
            _writer_idx += len;
        }
        // 扩容操作
        void ensureEnoughSize(size_t len)
        {
            if (len <= writeAbleSize())
                return;
            size_t new_size = 0;
            if (_buffer.size() < THRESHOLD_BUFFER_SIZE)
                new_size = _buffer.size() * 2 + len; // 小于阈值则翻倍
            else
                new_size = _buffer.size() + INCREMENT_BUFFER_SIZE + len; // 大于阈值则线性增长
            _buffer.resize(new_size);
        }

    private:
        std::vector<char> _buffer;
        size_t _reader_idx; // 可读数据指针
        size_t _writer_idx; // 可写数据指针
    };
}

测试

    // 测试异步缓冲区
    // 读取文件数据,逐步写入缓冲区,最终将缓冲区写入文件,判断新文件是否与原文件是否一致
    std::ifstream ifs("./log/test.log", std::ios::binary);
    if (ifs.is_open() == false)
        return -1;
    ifs.seekg(0, std::ios::end); // 跳转至文件末尾
    size_t fsize = ifs.tellg();  // 获取文件长度
    ifs.seekg(0, std::ios::beg); // 跳转至文件开头
    std::string body;
    body.resize(fsize);
    ifs.read(&body[0], fsize);
    if (ifs.good() == false)
    {
        std::cout << "read error\n";
        return -1;
    }
    ifs.close();
    Xulog::Buffer buffer;
    for (int i = 0; i < body.size(); i++)
    {
        buffer.push(&body[i], 1);
    }
    std::ofstream ofs("./log/tmp.log", std::ios::binary);
    // 一次写入
    // ofs.write(buffer.begin(), fsize);
    // 逐字节写入
    size_t read_size = buffer.readAbleSize();
    for (int i = 0; i < read_size; i++)
    {
        ofs.write(buffer.begin(), 1);
        buffer.moveReader(1);
    }
    ofs.close();

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一款高效、简洁的帧动画生成工具

在现代网页设计和移动应用开发中&#xff0c;帧动画是一种常见的动画实现方式&#xff0c;它通过连续显示一系列静态图片来模拟动画效果。然而&#xff0c;手动创建和管理这些帧动画图片不仅耗时费力&#xff0c;而且效率低下。为此&#xff0c;gka 应运而生&#xff0c;它是一…

翻车率这么高!今年11月软考论文应该如何备考?

随着最近2024年5月软考成绩的出炉&#xff0c;大家发现论文及格绝大多数都是45分&#xff0c;有许多高级考生三科中只有论文不合格&#xff0c;与软考证书失之交臂。而下半年除高项&#xff0c;其他4个高级科目都将开考&#xff0c;那么高级中至关重要的论文科目该如何备考呢&a…

AI应用开发平台Dify本地Ubuntu环境部署结合内网穿透远程管理大模型

文章目录 前言1. Docker部署Dify2. 本地访问Dify3. Ubuntu安装Cpolar4. 配置公网地址5. 远程访问6. 固定Cpolar公网地址7. 固定地址访问 前言 本文主要介绍如何在Linux Ubuntu系统使用Docker快速部署大语言模型应用开发平台Dify,并结合cpolar内网穿透工具实现公网环境远程访问…

微信小程序:wx.login或调用uni.login时报错the code is a mock one

微信小程序&#xff0c;调用wx.login或调用uni.login方法&#xff0c;返回the code is a mock one 原因与解决 原因:没有关联真实的 appid&#xff0c;解决办法&#xff1a;绑定真实的微信小程序的appid

OpenCV结构分析与形状描述符(9)检测轮廓相对于其凸包的凹陷缺陷函数convexityDefects()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 查找一个轮廓的凸性缺陷。 下图显示了一个手部轮廓的凸性缺陷&#xff1a; convexityDefects 是 OpenCV 库中的一个函数&#xff0c;用于检测轮…

文件压缩项目(基于Huffman编码)

目录 文件压缩压缩本质huffman树huffman树的构建Huffman编码的压缩过程获取Huffman编码构建压缩信息 Huffman编码的解压缩过程解压压缩的数据 写压缩函数的注意事项文件指针移动到头 测试过程对文本文件进行压缩纯英文文本测试中文文本测试中英文文本测试 对图片进行压缩解压缩…

第 1 课 编程是一门技术 ——认识Dev-C++

1.什么是编程&#xff1f; 软件由程序和文档组成&#xff0c;每个程序都是由一条条计算机能够识别和执行的指令组成的&#xff0c;每一天指令指挥计算机完成指定的操作。编写程序又称编程&#xff0c;他是一门技术。通俗地讲&#xff0c;编程就是告诉计算机&#xff0c;你要帮我…

合成控制法SCM

研究D的处理效应&#xff0c;找一个相似的样本&#xff0c;他们的差异就是处理效应。但&#xff1a;难点就在如何找到相似的样本。那么就通过合成法来合成一个虚拟的重庆。 案例&#xff1a;美国加州香烟法案出台 依靠权重来合成一个新的y 假设我们不用SCM&#xff0c;直接靠着…

文心快码前端工程师观点分享:人机协同关系总结与展望

&#x1f381;&#x1f449;点击进入文心快码 Baidu Comate 官网&#xff0c;体验智能编码之旅&#xff0c;还有超多福利&#xff01;&#x1f381; 本系列视频来自百度工程效能部的前端研发经理杨经纬&#xff0c;她在由开源中国主办的“AI编程革新研发效能”OSC源创会杭州站1…

CloudberryDB 内核分享:Directory Table 底层逻辑与实现原理讲解

在之前的直播中&#xff0c;我们向大家介绍了&#x1f517;&#xff0c;为企业AI应用创新提供更高质量的非结构化数据语料输入和知识库支持&#xff0c;感兴趣的朋友可以点击链接阅读。 随着我们的开源数据仓库产品Cloudberry Database&#xff08;简称“CloudberryDB”&#…

杂七杂八-系统环境安装

杂七杂八-系统&环境安装 1. 系统安装2.环境安装 仅个人笔记使用&#xff0c;感谢点赞关注 1. 系统安装 Windows安装linux子系统WSL2&#xff1a;使用windows系统跑linux程序(大模型) 2.环境安装 目前仅专注于 NLP 大模型 机器学习和前后端的技术学习和分享 感谢大家的关注…

基于spring的博客系统(总)

通过前⾯课程的学习, 我们掌握了Spring框架和MyBatis的基本使⽤, 并完成了图书管理系统的常规功能 开发, 接下来我们系统的从0到1完成⼀个项⽬的开发&#xff1b; 1. 项⽬介绍 使⽤SSM框架实现⼀个简单的博客系统 共5个⻚⾯ 1. 用户登录 2. 博客发表⻚ 3. 博客编辑⻚ 4. 博客…

零工市场小程序是灵活就业的新趋势?

人力资源社会保障部曾发文《人力资源社会保障部 民政部 财政部 住房和城乡建设部 国家市场监管总局关于加强零工市场建设 完善求职招聘服务的意见》。 找零工在传统情况下会有比较多的困难&#xff0c;比如能能够掌握的信息较少、不知道工作单位是否靠谱等等的问题&#xff0c…

心觉:如何打破用脑学习的瓶颈?教你用心学习,实现真正蜕变!

Hi&#xff0c;我是心觉&#xff0c;与你一起玩转潜意识、脑波音乐和吸引力法则&#xff0c;轻松掌控自己的人生&#xff01; 挑战每日一省写作167/1000天 我们经常听到父母对孩子说要“用心学习&#xff0c;用心学习” 大概意思是告诉孩子学习的时候要专注&#xff0c;要认真…

【9月持续更新】国内ChatGPT-4中文镜像网站大全

一、国内大模型与ChatGPT的区别 &#x1f9e0; 国内大模型&#xff1a;专注于国内市场&#xff0c;支持本土企业及用户&#xff0c;适用于中文语境下的客服、教育、内容生成等应用场景。ChatGPT&#xff1a;全球适用性强&#xff0c;但在中文环境下的本地化程度不如国内大模型…

Arm GIC-v3中断原理及验证(通过kvm-unit-tests)

一、参考连接 gic-v3相关原理可参考https://zhuanlan.zhihu.com/p/520133301 本文主要通过开源测试工具kvm-unit-tests&#xff0c;针对GIC的中断进行一系列验证&#xff0c;这样可以直入中断底层&#xff0c;熟悉整个原理。 kvm-unit-tests官网为kvm-unit-tests / KVM-Unit…

『 Linux 』协议的定制

文章目录 协议的概念序列化和反序列化网络计算器套接字接口的封装服务端大致框架协议的定制Request的序列化与反序列化Response的序列化与反序列化报头的封装的解包网络服务服务端的封装已提取报文的移除客户端的封装客户端的调用服务端接收多个请求 JSON 自动序列化反序列化使…

QT打开摄像头采集

QT打开摄像头采集 今天好不容易把opencv的环境装好&#xff0c;然后想学习一下人脸识别的功能&#xff0c;但是在图书馆坐了4个多小时了&#xff0c;屁股疼就先写个摄像头采集的功能&#xff0c;明天继续学习吧&#xff0c;废话不多&#xff0c;嚼个奶片开始发车&#xff01;&…

3.安卓逆向-java语言控制流和数据类型

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;图灵Python学院 上一个内容&#xff1a;2.安卓逆向-初识java语言 上一个内容里写了编写java语言代码的环境搭建&#xff0c;也就是下载…

你的个人生成式AI创新课程

我曾经写过许多博客文章&#xff0c;讨论如何使用生成式AI&#xff08;GenAI&#xff09;工具&#xff0c;例如OpenAI ChatGPT、微软Copilot和Google Gemini来提升专业效率和个人发展。然而&#xff0c;我们必须从仅仅使用这些GenAI工具来提高生产力的思维模式&#xff0c;转变…