muduo异步日志

news/2024/7/5 3:47:24

muduo异步日志实现

陈硕老师的muduo网络库的异步日志的实现,今晚有点晚了,我明晚再把这个异步日志抽出来,作为一个独立的日志库。

所在文件

  • AsyncLogging.cc

  • AsyncLogging.h

  • LogFile.h

  • LogFile.cc

  • CountDownLatch.h

  • CountDownLatch.cc

    这个CountDownLatch有点像信号量,但是又只有down操作,上网查了以下类似的,作用有点像屏障

class AsyncLogging : noncopyable
{
 public:

  AsyncLogging(const string& basename,
               off_t rollSize,
               int flushInterval = 3);

  ~AsyncLogging()
  {
    if (running_)
    {
      stop();
    }
  }

  void append(const char* logline, int len);

  void start()
  {
    running_ = true;
    thread_.start();	// 启动线程
    latch_.wait();		// 这里的wait调用其实并不会阻塞
						// 主线程
  }

  void stop() NO_THREAD_SAFETY_ANALYSIS
  {
    running_ = false;
    cond_.notify();
    thread_.join();
  }

 private:
  // 默认的守护进程执行的函数
  void threadFunc();

  /*
	Buffer的数据结构
	 private:
	  std::vector<char> buffer_;
	  size_t readerIndex_;
	  size_t writerIndex_;

	  static const char kCRLF[];
  */
  
  typedef muduo::detail::FixedBuffer<muduo::detail::kLargeBuffer> Buffer;
  typedef std::vector<std::unique_ptr<Buffer>> BufferVector;
  typedef BufferVector::value_type BufferPtr;	// 表示容器元素的类型

  const int flushInterval_;						// 刷盘的时间间隔
  std::atomic<bool> running_;					// 是否运行
  const string basename_;						// 文件名
  const off_t rollSize_;						// 日志回滚的大小
  muduo::Thread thread_;						// 日志类自带一个守护线程来写
  muduo::CountDownLatch latch_;					
  muduo::MutexLock mutex_;
  muduo::Condition cond_ GUARDED_BY(mutex_);
  BufferPtr currentBuffer_ GUARDED_BY(mutex_);	// 当前缓冲
  BufferPtr nextBuffer_ GUARDED_BY(mutex_);		// 预备缓冲
  BufferVector buffers_ GUARDED_BY(mutex_);		// 已经写满并等待落盘的缓冲
};
#include <stdio.h>

using namespace muduo;

AsyncLogging::AsyncLogging(const string& basename,
                           off_t rollSize,
                           int flushInterval)
  : flushInterval_(flushInterval),
    running_(false),
    basename_(basename),
    rollSize_(rollSize),
    thread_(std::bind(&AsyncLogging::threadFunc, this), "Logging"),
    latch_(1),
    mutex_(),
    cond_(mutex_),
    currentBuffer_(new Buffer),
    nextBuffer_(new Buffer),
    buffers_()
{
  currentBuffer_->bzero();
  nextBuffer_->bzero();
  buffers_.reserve(16);
}

void AsyncLogging::append(const char* logline, int len)
{
  muduo::MutexLockGuard lock(mutex_);
  if (currentBuffer_->avail() > len)
  {
    currentBuffer_->append(logline, len);
  }
  else
  {
    buffers_.push_back(std::move(currentBuffer_));

    if (nextBuffer_)
    {
      currentBuffer_ = std::move(nextBuffer_);
    }
    else
    {
	  // 四个缓冲区都写满了
      currentBuffer_.reset(new Buffer); // Rarely happens
    }
    currentBuffer_->append(logline, len);
	// 这里的notify不一定什么时候都有效
	// 如果此时守护线程正在工作
	// 那么这个信后就会丢失,但是没有造成影响
	// 但是有可能守护线程正在条件变量上睡眠
    cond_.notify();
  }
}

void AsyncLogging::threadFunc()
{
  assert(running_ == true);
  latch_.countDown();		// 计数器减一,latch_ == 0,并唤醒主线程
  LogFile output(basename_, rollSize_, false);	// 初始化一个输出流
  BufferPtr newBuffer1(new Buffer);
  BufferPtr newBuffer2(new Buffer);
  newBuffer1->bzero();
  newBuffer2->bzero();
  BufferVector buffersToWrite;	// 相当于一个前后端交互的单元
								// 将写满的buffer装到vector中
								// 在传输到后端
  buffersToWrite.reserve(16);
  while (running_)
  {
    assert(newBuffer1 && newBuffer1->length() == 0);
    assert(newBuffer2 && newBuffer2->length() == 0);
    assert(buffersToWrite.empty());

    {
      muduo::MutexLockGuard lock(mutex_);
	  // 使用条件变量完成定时任务
      if (buffers_.empty())  // unusual usage!
      {
        cond_.waitForSeconds(flushInterval_);
      }
      buffers_.push_back(std::move(currentBuffer_));
      currentBuffer_ = std::move(newBuffer1);
      buffersToWrite.swap(buffers_);	// buffers_变成一个空的buffers_
      if (!nextBuffer_)
      {
        nextBuffer_ = std::move(newBuffer2);
      }
    }

    assert(!buffersToWrite.empty());
	
	// 如果日志太多了
    if (buffersToWrite.size() > 25)
    {
      char buf[256];
      snprintf(buf, sizeof buf, "Dropped log messages at %s, %zd larger buffers\n",
               Timestamp::now().toFormattedString().c_str(),
               buffersToWrite.size()-2);
      fputs(buf, stderr);
	  // 先输出一个报警的日志
      output.append(buf, static_cast<int>(strlen(buf)));
	  // 清除多余的日志
      buffersToWrite.erase(buffersToWrite.begin()+2, buffersToWrite.end());
    }

    for (const auto& buffer : buffersToWrite)
    {
      // FIXME: use unbuffered stdio FILE ? or use ::writev ?
      output.append(buffer->data(), buffer->length());
    }

    if (buffersToWrite.size() > 2)
    {
      // drop non-bzero-ed buffers, avoid trashing
	  // vector底层是智能指针不用担心内存泄露
      buffersToWrite.resize(2);
    }

    if (!newBuffer1)
    {
      assert(!buffersToWrite.empty());
      newBuffer1 = std::move(buffersToWrite.back());
      buffersToWrite.pop_back();
      newBuffer1->reset();
    }

    if (!newBuffer2)
    {
      assert(!buffersToWrite.empty());
      newBuffer2 = std::move(buffersToWrite.back());
      buffersToWrite.pop_back();
      newBuffer2->reset();
    }

    buffersToWrite.clear();
    output.flush();
  }
  output.flush();
}


http://lihuaxi.xjx100.cn/news/2122486.html

相关文章

BMS中电磁干扰(EMI)的相关解决方案

电磁干扰&#xff08;EMI&#xff09;是电子设备设计和运行中常见的问题&#xff0c;它可能导致设备性能下降、数据丢失或系统故障。在电池管理系统&#xff08;BMS&#xff09;中&#xff0c;EMI 可能会影响通信的可靠性和电池组的稳定性。以下是一些具体的解决方案&#xff0…

前端面试集中复习 - http篇

1. http请求方式 HTTP请求方式有哪些&#xff1a;GET POST PUT DELETE OPTIONS 1) GET POST 的区别&#xff1f; 场景上&#xff1a; GET 用于获取资源而不对服务器资源做更改提交的请求&#xff0c;多次执行结果一致。用于获取静态数据&#xff0c;幂等。 POST&#xff1…

二刷代码随想录——单调栈day58

文章目录 前言单调栈知识点 单调栈的特点一、739. 每日温度二、496. 下一个更大元素 I总结 前言 一个本硕双非的小菜鸡&#xff0c;备战24年秋招&#xff0c;计划二刷完卡子哥的刷题计划&#xff0c;加油&#xff01; 二刷决定精刷了&#xff0c;于是参加了卡子哥的刷题班&…

【Java程序设计】【C00372】基于(JavaWeb)Springboot的社区网格化管理系统(有论文)

TOC 博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;博客中有上百套程序可供参考&#xff0c;欢迎共同交流学习。 项目简介 项目获取 &#x1f345;文末点击卡片…

Vue 发送Ajax请求多种方式

1. 发送ajax请求的方式 方案一&#xff1a;jq 的ajax&#xff08;在 vue 中不推荐同时使用&#xff09;方案二&#xff1a;js 原始官方 fetch方法方案三&#xff1a;axios 第三方 2. 方案一 后端视图函数 from rest_framework.viewsets import ViewSet from rest_framework…

小红书矩阵批量发布工具,一键发布笔记软件

昨日&#xff0c;我收到了一条充满渴望与期待的私信&#xff0c;来自一位小红书的矩阵账号博主。他手握多个账号&#xff0c;渴望寻找一款能够助力他批量发布笔记的神器&#xff0c;每日能够轻松达到百篇的发布量。这份迫切的需求&#xff0c;我深感体会&#xff0c;因为这正是…

【链表】Leetcode 146. LRU 缓存【中等】

LRU 缓存 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存int get(int key) 如果关键字 key 存在于缓存中&#xff0c;则返回关键字的值&#xff0c;否…

再次度过我的创作纪念日

机缘 写博客的机缘巧合已经在上一篇博客中写到了&#xff0c;至于收获和成就也不一一赘述了。想和大家聊的呢就这最近这一年左右的经历吧 日常 自从2022年开始&#xff0c;入职了一家大型的项目外派公司&#xff0c;名字就不说了。开始了我的保险公司系统的开发工作。工作地点…