如何快速的把日志输出到磁盘上

    不管是做客户端业务,还是做服务端业务,日志子系统都是非常重要的一个组件。

    日志系统的输出目的地可以是disk,也可以是tty,更可以是network。

    我的日志系统可以输出到tty,不同log level可以有不同的color,这样看日志非常的醒目,当然这里着重谈的是如何快速的把log内容写到磁盘上。

    

    其实,如何快速的把log内容写到磁盘上,网上文章已经汗牛充栋,真正高质量的没多少,本篇可能也是狗尾续貂之作。不过,我的log子系统能够达到106M/s的输出速率。

    详细介绍我的log系统之前,推荐大家看看陈硕大牛的《Linux 多线程服务端编程:使用 muduo C++ 网络库》一书中关于muduo log的实现,muduo的log的思路和实现都是非常漂亮的。网上还有相关的ppt,这里面有很多的干货。

    

    log系统如何快速的把log内容写到磁盘上?其关键就在于写log时要进行顺序写,即每次写log的大小要为4k或者4k的倍数。

    鉴于语言描述非本人强项,下面先呈上关键代码,然后再详述之。

    struct log_t {
        int                            file;
        pthread_mutex_t mutex;
        int                           buf_cursor;
        char                       buf[4 * 1024];
        struct iovec           iovec_[2];
    };
    
    int log_write(log_t* log, char* buf, int len)
    {
        int ret = 0;
    
        pthread_mutex_lock(&log->mutex);
        do  {
            size = log->buf_cursor + len;
            if (size < sizeof(log->buf))  {
                memcpy(log->buf + log->buf_cursor, buf, len);
                log->buf_cursor = size;
                break;
            }
    
            log->iovec_[0].iov_base = log->buf;
            log->iovec_[0].iov_len = log->buf_cursor;
            log->iovec_[1].iov_base = buf;
            log->iovec_[1].iov_len = len;
            ret = (int)writev(log->file, log->iovec_, 2);
            if (ret != size))  {
                ret = -2;
            }
    
            log->cursor += size;
            log->buf_cursor = 0;
        } while(0);
        pthread_mutex_unlock(&log->mutex);
    
        return ret;
    }
    看上面这段代码的,log有一个4k的buf,如果本次输出的内容能够放到buf里面,那么就把内容拷贝进去,然后退出,否则就调用writev函数把内容写进log文件。

    其思路跟陈硕大牛的muduo的log比起来当然是云泥之别,没那么高大上。其方法的关键就是减小锁的粒度、合并多次write为一次write以进行顺序写log内容至磁盘上。

    

    log系统有同步和异步两种区别,上面的实现方式本质是一种同步方式。另外,writev并不是一个原子操作,它一次可能并不能把log->iovec中所有的内容都写到磁盘上,所以考虑到其是一种同步实现方式,实际应用中应该通过循环方式保证log->iovec所有的内容都写到磁盘上(这个脏活留给你,天下没有免费的午餐)。

    所谓的异步输出log就是专门启动一个日志线程,它可以有一个log队列,其他线程作为生产者把内容输出至队列,日志线程就作为消费者从队列中取出log内容,然后把它写到磁盘上。

    我最近为公司实现了一种异步log系统,其关键流程当然就是上面一段的思路。但是,仅仅靠上面的这种方法是无法实现快速地进行异步log输出的。除了log线程的队列外,参考陈硕的muduo log的思路,我还添加了一个log buf(struct log_buf {  int cursor;  char buf[1 * 1024 * 1024];  };),其思路跟同步方式一样,先把log内容放到这个log_buf中,待log_buf快溢出的时候,把log_buf放入队列中。

   由于代码的版权归公司所有,所以这里就不贴代码了。异步log的最终效果能达到140M/s的输出速率。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
当服务器上的Docker挂载的磁盘总是满时,您可以考虑以下优化措施: 1. 清理无用的Docker镜像和容器:使用`docker system prune`命令清理无用的镜像、容器和网络资源。这可以释放磁盘空间并提高系统性能。 2. 设置日志轮转:对于产生大量日志的容器,可以设置日志轮转,限制日志文件大小并保留一定数量的日志文件。这样可以避免单个容器的日志文件过大,占满磁盘空间。您可以使用日志轮转工具如logrotate来实现。 3. 调整Docker日志设置:Docker默认将容器的标准输出和错误输出重定向到日志文件中。您可以通过修改Docker的日志驱动配置,将日志输出限制在适当的级别,或者将日志输出到外部日志管理系统,以减少对磁盘空间的占用。 4. 调整容器存储卷的使用:某些容器可能会频繁写入或产生大量数据,导致磁盘空间快速填满。您可以考虑将这些数据写入专门的存储卷,而不是直接写入容器本身。这样可以分离容器文件系统和数据存储,避免磁盘空间被耗尽。 5. 监控和限制容器资源使用:使用Docker的资源限制功能,如CPU和内存限制,可以避免某个容器过度消耗系统资源而导致磁盘空间不足。您可以通过设置适当的资源限制来平衡容器的性能和资源使用。 6. 定期清理无用的数据:定期清理不再需要的数据文件、日志文件和临时文件等,以释放磁盘空间。您可以编写脚本或使用定时任务工具来自动执行清理操作。 请根据您的具体情况选择适合的优化方法,并确保在进行任何操作之前备份重要数据。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值