kids日志系统引见

kids日志系统介绍

随着业务和系统的规模增大,我们后台的机器也逐渐增多,带来了很多新的问题,比如系统的统一发布升级、监控等等。如何解决这些问题相信大家都有自己的思路和实践,这篇博文也不打算就这些问题展开,而是从其中一个点——日志说起。不论实现怎样一个系统,日志都不可或缺,后台系统也是如此。无论是基于日志来做业务监控,还是遇到突发事故通过查找日志来定位问题,日志扮演着很重要的角色。在单机时代,可以简单地把日志写到本地磁盘。日志文件增大以后,要考虑日志切分、日志分类、甚至日志清理等问题。有很多现有的工具可以用来解决这些问题,比如log4j、log4cpp等。而到了多台机器,写本地磁盘的做法已经不能满足业务的需求。比如出现某个业务问题,要及时还原现场找到问题的症结,按照以往的方式你可能会先去查找日志流水,但多台机器你怎么查?一台台ssh上去查找?这是很不现实的一种做法,效率低下而且浪费时间。这时候你可能需要将多台机器上的日志汇集到一起查找,kids是一个可以参考的选择。

Kids是知乎团队开发的一款分布式日志系统,缩写是“kids is a data stream”,有点GNU的味道。最初在技术选型的时候大概有三种思路,一种是自己手写一个传输工具把日志发到一个固定的server上,第二种是结合pubsub消息队列来收集日志,第三种是已有的分布式日志系统。最开始是比较倾向于使用第二种方案来做,比如kafka、rabbitmq等。由于后台技术栈主要还是以C++为主,不甘心继续找,后来无意中看到facebook开源的scribe,但这玩意一是很久没更新,第二这玩意要依赖thrift,并不是一个轻量级的东西。再后来无意中在infoq上看到一篇关于知乎架构的文章,文章里介绍了他们团队开发的日志系统kids,正好也开源了,于是从github上clone下来用了用。第一感觉上手很快,配置很简单很清晰,觉得可以试用下。

由于线上系统的gcc不支持C++11,只有一台测试机支持,倒腾了下把libstdc++做了静态编译放到了线上系统试用。由于没有做很严格的性能测试,事实上也没有性能测试的思路,只把日志量很少的部分业务用到了kids,每天的日志大概100多K,每台机器上的kids进程占50多M内存,而且就算Kids crash掉也不影响实际业务。kids的日志不会实时写到日志,而是会缓冲到内存一段时间再写入,可以在配置文件中设置flush的频率,确保内存中的数据及时写入文件。后面有空可以研究下它的实现机制,看有没有什么可以优化的空间。大概是这么个情况。

Kids具有实时订阅、分布式收集、消息持久化、多线程、采用Redis协议、没有第三方依赖等特点。Kids在每台服务器上可以配置成agent或server。agent直接接受来自应用的消息,把消息汇集之后,可以打给下一个agent或者直接打给中心server。订阅日志时,可以从server上获取,也可以从中心节点的一些agent上获取。

kids日志系统引见

Kids的配置很简单,重点说下Kids配置里的store,它是在使用Kids最需要关注的配置项,前面提到的agent和server也是通过这个配置来实现的。store的配置有两种形式:简单配置和混合配置。

简单配置里有三种方式:FileStore、NetworkStore和NullStore:

  • FileStore,文件存储,多用于 Server 的持久化和 Agent 的 Buffer 存储的文件缓存。
store file {
  path /path/to/logs/[topic]/[date];
  name [time].log;
  rotate 1hour; # 时间单位支持hour与min
}

采用该配置后,kids 会将日志按照 /topic/date/time.log 的形式存储,每小时进行一次文件的切分。 此外,还可以在文件存储配置块中添加flush 5s之类的配置来确保内存中buffer的数据被及时写入到文件中。 这在处理量少但是较为关键的log类型时可能会带来帮助,但大部分时间你并不需要flush选项,或者使用订阅模式来获得真正实时的数据。

  • NetworkStore,网络存储,将日志通过网络转发至指定主机。
store network {
  host kidsserver;
  port 3388;
}
  • NullStore,空存储,此时 kids 不会对指定形式的日志进行任何方式的存储,可与其他存储方式组合对指定日志进行过滤。
store null {
  topic kids.ignoretopic;
}

混合配置也分为三种方式:PriorityStore、BufferStore和MultipleStore:

  • PriorityStore,日志按照配置文件中的顺序尝试存储,如果某一存储方式存储成功,则不再继续对该日志进行后续存储。
store priority {
  store null {
    topic kids.ignoretopic1;
    topic kids.ignoretopic2;
  }

  store network {
    host kidsserver;
    port 3388;
  }

  store file {
    path /path/to/logs/[topic]/[date];
    name [time].log;
    rotate 1hour;
  }
}

采用该配置后 kids 会忽略 kids.ignoretopic1 与 kids.ignoretopic2 的存储,对于其他 topic,会转发至 kidsserver:3388,若转发失败,会保存到指定文件中。

  • BufferStore,使用 Primary 与 Secondary 两级存储, 当 Primary 存储出现异常时,会将日志缓存至 Secondary 存储中,当 Primary 恢复后,会将 Secondary 中的缓存重新存储至 Primary。
store buffer {
  store network primary { # primary
    host kidsserver;
    port 3388;
  }

  store file secondary { # secondary
    path /data/kidsbuf;
    rotate 5min;
  }
}
  • MultipleStore,MultipleStore 会将日志存储至多个位置,通过定义success的类型,判断存储的成功与失败success的值可选any或者all
store multiple {
  success any;
# master
  store buffer {
    store network primary {
      host kidsserver;
      port 3388;
    }
    store file secondary {
      path /tmp/kidsbuf;
      rotate 10min;
    }
  }

# backup
  store buffer {
    store network primary {
      host kidsbackup;
      port 3388;
    }
    store file secondary {
      path /tmp/kidsbuf2;
      rotate 10min;
    }
  }
}

参考链接:

  • kids开源地址
  • kids设计初衷
  • kids在知乎的使用和实现细节
  • 从0到100——知乎架构变迁史