c++ 高性能日志库(muduo_AsyncLogging)

c++ 高性能日志库(muduo_AsyncLogging)

实现一个高效的网络日志库要解决那些问题?
首先明确一下问题的模型,这是一个典型的多生产者 单消费者问题,对于前端的日志库使用者来说,应该做到非阻塞添加,作为后端的文件写入,应该注意磁盘IO的瓶颈。

功能需求

  1. 日志的级别分级
  2. 发生时间和具体线程信息
  3. 线程安全

实现思路

多个线程共有一个前端,通过后端写入磁盘文件
异步日志是必须的,所以需要一个缓冲区,在这里我们使用的是多缓冲技术,基本思路是准备多块Buffer,前端负责向Buffer中填数据,后端负责将Buffer中数据取出来写入文件,这种实现的好处在于在新建日志消息的时候不必等待磁盘IO操作,前端写的时候也不会阻塞。

实现结构

LogStream 负责写入消息的格式化
LogFile 负责文件写入
AsyncLogging 负责实现 多缓冲技术 协调前后端

LogFile

如果有必要就给日至文件加锁

void LogFile::append(const char* logline, int len)
{
  if (mutex_)
  {
    MutexLockGuard lock(*mutex_);
    append_unlocked(logline, len);
  }
  else
  {
    append_unlocked(logline, len);
  }
}

LogStream

重写流操作符
构造一个格式转化类,给日志中消息提供一个统一的格式

AsyncLogging

是及实现采用了四个缓冲区,这样可以进一步减少前端等待,数据结构

typedef boost::ptr_vector<LargeBuffer> BufferVector;
typedef BufferVector::auto_type BufferPtr;
MutexLock lock;
Condition cond;
BufferPtr nextBuffer;
BufferVector buffers_;

append的具体实现
在当前的缓冲区和备用缓冲区中选择一个足够使用的进行写入。

void AsynLogging::append(const char* logline, int len){
    LockGuard(mutex);
    if(curbuf->avail() > len){//当前缓冲区足够
        curbuf->append(logline,len);
    }
    else{
        buffers.push_back(curbuf->release());
        if(nextbuf){
            curbuf = std::move(nextbuf);
        }
        else{
            curbuf.reset(new LargeBuffer);  
        }
        curbuf->append(logline, len);
        cond.notify();
    }
}
  • 接收方的后端实现
    首先准备好两块空闲的buffer,已备在临界区内交换,等待条件标量出发的条件又两个,超时或者是前端写满了一个或者多个Buffer,当条件满足时,先将当前缓冲移入buffer,并且立刻将空闲的newBuffer1作为当前缓冲,接下来将buffers和buffersToWrite交换,随后将buffersToWrite写入文件,重新设计设置Buffer。
void AsyncLogging::threadFunc(){
    BufferPtr nweBuffer1(new LargeBuffer);
    BufferPtr newBuffer2(new LargeBuffer);
    BufferVector bufferToWrite;
    while(running_){
    {
        MutexLockGuard lock(mutex);
        if(buffers.empty()){
            cond.wait_for(muted,flushInterval_);
        }
        buffers.push_back(currentBuffer_.release());
        currentBuffer = move(newBuffer1);
        buffersTowrite.swap(buffers_);
        if(!nextBuf){
            nextBuf = std::move(newBuffer2);
        }
    }
    }
}

交给后端去写入,以及重新设置两个缓冲区

       for (size_t i = 0; i < buffersToWrite.size(); ++i)
      {
        // FIXME: use unbuffered stdio FILE ? or use ::writev ?
        output.append(buffersToWrite[i].data(), buffersToWrite[i].length());
      }

      if (buffersToWrite.size() > 2)
      {
        // drop non-bzero-ed buffers, avoid trashing
        buffersToWrite.resize(2);
      }

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

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

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

转载于:https://www.cnblogs.com/joeylee97/p/8886326.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/****************************************************************************** Module: VC-Logger Purpose: 记录程序日志。 1. 把日志信息输出到指定文件 2. 对于 GUI 程序,可以把日志信息发送到指定窗口 3. 对于Console应用程序,可以把日志信息发往标准输出 (std::cout) Desc: 1、功能: -------------------------------------------------------------------------------------- a) 把日志信息输出到指定文件 b) 每日生成一个日志文件 c) 对于 GUI 程序,可以把日志信息发送到指定窗口 d) 对于Console应用程序,可以把日志信息发往标准输出 (std::cout) e) 支持 MBCS / UNICODE,Console / GUI,win32 / x64 程序 f) 支持动态加载和静态加载日志组件 DLL g) 支持 DEBUG/TRACE/INFO/WARN/ERROR/FATAL 等多个日志级别 2、可用性: -------------------------------------------------------------------------------------- a) 简单纯净:不依赖任何程序或框架 b) 使用接口简单,不需复杂的配置或设置工作 c) 提供 CStaticLogger 和 CDynamicLogger 包装类用于静态或动态加载以及操作日志组件,用户无需关注加载细节 d) 程序如果要记录多个日志文件只需为每个日志文件创建相应的 CStaticLogger 或 CDynamicLogger 对象 e) 只需调用 Log()/Debug()/Trace()/Info()/Warn()/Error()/Fatal() 等方法记录日志 f) 日志记录方法支持可变参数 g) 日志输出格式: 3、性能: -------------------------------------------------------------------------------------- a) 支持多线程同时发送写日志请求 b) 使用单独线程在后台写日志,不影响工作线程的正常执行 c) 采用批处理方式批量记录日志 Usage: 方法一:(静态加载 Logger DLL) -------------------------------------------------------------------------------------- 0. 应用程序包含 StaticLogger.h 头文件 1. 创建 CStaticLogger 对象(通常为全局对象) 2. 调用 CStaticLogger->Init(...) 初始化日志组件 3. 使用 CStaticLogger->Log()/Debug()/Trace()/Info()/Warn()/Error()/Fatal() 等方法写日志 4. 调用 CStaticLogger->UnInit(...) 清理日志组件(CStaticLogger 对象析构时也会自动清理日志组件) 方法二:(动态加载 Logger DLL) -------------------------------------------------------------------------------------- 0. 应用程序包含 DynamicLogger.h 头文件 1. 创建 CDynamicLogger 对象(通常为全局对象) 2. 调用 CDynamicLogger->Init(...) 初始化日志组件 3. 使用 CDynamicLogger->Log()/Debug()/Trace()/Info()/Warn()/Error()/Fatal() 等方法写日志 4. 调用 CDynamicLogger->UnInit(...) 清理日志组件(CDynamicLogger 对象析构时也会自动清理日志组件) 方法三:(直接用导出函数加载 Logger DLL) -------------------------------------------------------------------------

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值