Apache Kibble 数据采集实战详解

以真实 Git 仓库为例详解 Apache Kibble 采集器的配置与运行,覆盖数据源登记、四类 git 扫描器流水线、采集落库验证与统计 API 取数,附完整排障实录。

Apache KibbleKibbleApache数据分析2026/10/15

Apache Kibble 数据采集实战详解

本文是 Kibble 系列的实战篇:在 Ubuntu 22.04 服务器上(已完成服务端部署,见部署篇),以一个约 668 个提交的真实 Git 仓库为采集对象,完整走通"登记数据源 → 运行采集器 → 数据落库 → API 取数"全链路。所有命令与回显均为实测结果。Kibble 已退役进 Apache Attic,本文仅供学习研究。

一、采集体系总览

1.1 采集器程序与插件

采集器代码位于 apache/kibble-scanners 仓库,核心入口是 src/kibble-scanner.py。它本身不采集任何数据,而是完成三件事:加载 conf/config.yaml 配置、连接存储层取出"组织与数据源清单"、把每个数据源分派给已加载的扫描插件。实测启动时会加载全部 16 个插件:

[core]: Loaded plugins/scanners/git-sync v/0.1.2 (Sync plugin for Git repositories)
[core]: Loaded plugins/scanners/git-census v/0.1.0 (Census Scanner for Git)
[core]: Loaded plugins/scanners/git-sloc v/0.1.0 (SloC Counter for Git)
[core]: Loaded plugins/scanners/git-evolution v/0.1.0 (Git Evolution Scanner)
[core]: Loaded plugins/scanners/jira v/0.1.0 (Scanner for Atlassian JIRA)
[core]: Loaded plugins/scanners/ponymail v/0.1.0 (Scanner plugin for Apache Pony Mail)
[core]: Loaded plugins/scanners/ponymail-tone v/0.1.0 (Tone/Mood Scanner plugin for Apache Pony Mail)
[core]: Loaded plugins/scanners/ponymail-kpe v/0.1.0 (Key Phrase Extraction plugin for Apache Pony Mail)
[core]: Loaded plugins/scanners/pipermail v/0.1.0 (Scanner for GNU Mailman Pipermail)
[core]: Loaded plugins/scanners/github-issues v/0.1.0 (Scanner for GitHub Issues)
[core]: Loaded plugins/scanners/bugzilla v/0.1.0 (Scanner for BugZilla)
[core]: Loaded plugins/scanners/gerrit v/0.1.1 (Scanner for Gerrit Code Review)
[core]: Loaded plugins/scanners/jenkins v/0.1.0 (Scanner for Jenkins CI)
[core]: Loaded plugins/scanners/buildbot v/0.1.0 (Scanner for Buildbot)
[core]: Loaded plugins/scanners/travis v/0.1.0 (Scanner for Travis CI)
[core]: Loaded plugins/scanners/discourse v/0.1.0 (Scanner for Discourse Forums)
[core]: Kibble Scanner v/0.1.0 starting

每个插件声明自己"接受"的数据源类型,运行时按类型自动匹配,因此一条命令即可对组织内全部数据源执行所有适用的扫描。 16 个插件的构成透露了 Kibble 的度量视野:git 系四个覆盖代码维度(同步、提交普查、代码行、演进),ponymail/pipermail/discourse 系五个覆盖邮件与论坛等社区沟通维度,jira/bugzilla/github-issues 三个覆盖缺陷与议题维度,jenkins/buildbot/travis/gerrit 四个覆盖构建与评审维度。这个清单本身就是一份"开源社区活跃度由哪些信号组成"的答题卷,即便在今天做工程效能平台选型时,这四维划分依然是最常用的起点。另有一个值得注意的实现细节:插件按目录加载、版本号独立(git-sync 已到 0.1.2 而多数还是 0.1.0),说明插件的成熟度差异明显——这是插件化架构的真实生态形态,与理想的整齐划一相反。

1.2 两种对接模式

配置文件 conf/config.yaml 顶部定义了两种对接模式,二选一:

  • 直连模式(elasticsearch.enabled: true):采集器直接读写 Elasticsearch,适合采集器与服务端同机或同网的部署;
  • 代理模式(broker.enabled: true):采集器通过服务端 HTTP API 中转,适合采集器分散在多台机器的场景。

本文实测使用直连模式。 两种模式的取舍值得展开。直连模式少一跳、少一个单点,采集器挂了只影响采集本身;但每个采集器都要持有 Elasticsearch 的访问权,网络的暴露面与凭证管理随之扩大。代理模式把存储访问收敛到服务端一处,采集器只需要能访问 API 并持有账户凭证,适合采集节点分散在多地(各项目机房、CI 节点)的组织级部署;代价是服务端成为采集链路的必经节点,服务端不可用时采集全停。以 Kibble 面向 Apache 基金会的原始场景看,代理模式是主线设计(基金会各项目的采集点天然分散),直连则是单机验证的便利通道——本文因服务端与采集器同机部署选直连,符合最小依赖原则。

二、采集前准备

2.1 安装采集器依赖

采集器依赖已在部署篇的虚拟环境中覆盖大半(elasticsearch、pyyaml、python-dateutil、certifi、requests),补装 psutil 即可;同时确认系统已安装 git 与 cloc:

/opt/kibble-venv/bin/pip install psutil
apt-get install -y cloc

cloc 是 git-sloc 与 git-evolution 两个插件的外部依赖,实测版本 1.90。若缺失,采集运行时会报出真实错误(见 4.3 节排障实录二)。

2.2 克隆采集器源码并建立配置软链

cd /opt
git clone https://github.com/apache/kibble-scanners.git
ln -s /opt/kibble-scanners/conf /opt/kibble-scanners/src/conf

第二条命令是必须的:主程序以相对路径 conf/config.yaml 读取配置,而仓库中 conf 目录与 src 目录是平级的,从 src 目录运行时找不到(实测报 FileNotFoundError: conf/config.yaml,这是 4.1 节排障实录一)。

2.3 确认配置文件

/opt/kibble-scanners/conf/config.yaml 的关键段落(保持以下值):

elasticsearch:
    enabled:    true
    hostname:   localhost
    port:       9200
    ssl:        false
    uri:        ""
    database:   kibble
    versionHint: 8

database 与部署篇初始化时的索引前缀一致(kibble);versionHint: 8 声明对接 Elasticsearch 8。scratchdir 默认为 /tmp,git-sync 插件会把仓库克隆到该目录下按数据源哈希命名的子目录中;若机器会清理 /tmp,建议改到持久目录。 配置文件还有几段与本文相关:threads 段控制工作线程上限(代码固定上限 4);balance 段用于多采集器分片(六章调度一节引用); BROKER 段在代理模式下填写服务端 API 地址与账户。三个易错点在此一并提示:hostname 必须与服务端实际监听一致(部署篇把 ES 绑定在 127.0.0.1,此处 localhost 恰好匹配);database 必须与初始化时的 -d 参数一致,不一致时采集器会连接成功但读写到错误前缀的索引,表现为采集"成功"而服务端查不到数据;ssl: false 需与服务端实际协议一致,误开 ssl 会在握手阶段报连接错误。

三、登记数据源

数据源登记是采集链路的起点:只有写进 kibble_source 索引的源才会被采集器发现与处理。登记动作本身很轻(一条 API 调用),但挂靠关系(组织、账户默认组织)必须先行就位,本节按"准备仓库 → API 登记 → 存储确认"的顺序推进。

3.1 准备一个真实 Git 仓库

采集对象可以是任何 Git 仓库:远程仓库(https://github.com/apache/kibble-1.git)或本地裸仓库均可。实测先克隆一份完整历史的目标仓库:

cd /opt
git clone https://github.com/apache/kibble-1.git kibble-full
cd kibble-full && git log --oneline | wc -l

实测回显 668,即 668 个提交可采集。注意:务必使用完整克隆。实测曾用带 --depth 1 的浅克隆做采集源,结果整个仓库只有 1 个提交,且统计插件对该提交的日志解析产生了意外的空结果(见 4.4 节排障实录四)。 仓库形态的选择上,本地路径(file://)与远程 URL(https://)对后续流水线完全等价——git-sync 都是以 git 命令对接,差异只在克隆阶段的网络开销与凭证需求。实测选择把 kibble-1 完整克隆为 kibble-full 作为采集对象,有一个一举两得的用意:被度量的对象恰好是 Kibble 自己的开发历史,采集产出的每一个数字(提交曲线、代码行、贡献者名单)都可以与仓库的公开事实交叉验证,数据的真实性获得了天然的对账基准。

3.2 通过 API 登记数据源

数据源必须挂在某个组织下,且账户设置了默认组织(部署篇已完成 demoorg 组织创建与默认组织设置)。登记命令:

curl -s -H "Cookie: kibble_session=<会话ID>" \
  -X PUT http://127.0.0.1:8080/api/sources \
  -H "Content-Type: application/json" \
  -d '{"sources":[{"type":"git","sourceURL":"file:///opt/kibble-full"}]}'

实测回显:

{"message": "Sources added/updated", "added": 1, "updated": 0}

服务端用 SHA-224 对 "git-file:///opt/kibble-full" 生成 sourceID 并写入 kibble_source 索引。登记后可到 Elasticsearch 确认:

curl -s "http://127.0.0.1:9200/kibble_source/_search?pretty&filter_path=hits.hits._source.sourceURL"

实测回显:

{
  "hits" : {
    "hits" : [ {
      "_source" : { "sourceURL" : "file:///opt/kibble-full" }
    }, {
      "_source" : { "sourceURL" : "file:///opt/kibble-1" }
    } ]
  }
}

四、运行采集器

4.1 排障实录一:配置文件路径错误

第一次从 src 目录运行:

cd /opt/kibble-scanners/src
/opt/kibble-venv/bin/python kibble-scanner.py -o demoorg

真实报错:

FileNotFoundError: [Errno 2] No such file or directory: 'conf/config.yaml'

按 2.2 节建立软链后,程序正常启动。也可以用 -f 参数显式指定配置文件绝对路径绕开该问题。

4.2 首次全量采集

cd /opt/kibble-scanners/src
/opt/kibble-venv/bin/python kibble-scanner.py -o demoorg

-o 参数限定只处理 demoorg 组织(省略则处理存储层中全部组织)。实测关键输出:

[core]: Loaded YAML config from conf/config.yaml
[core]: Using direct ElasticSearch broker model
[core]: Connecting to ElasticSearch database at localhost:9200...
[core]: Connected!
[core]: This is a type-less DB, expanding database names instead.
[core]: We're using ES >= 7.x, NO DOC_TYPE!
[core]: Processing organisation demoorg
[core]: Initialized thread 1
[core]: Initialized thread 2
[core]: Initialized thread 3
[core]: Initialized thread 4
[thread#3:plugins/scanners/git-sync]: Checking out file:///opt/kibble-full as 2141a7bce6934ea43d5ce6535ab5f0003fb236a6ae1968ee9e55525f
[thread#3:plugins/scanners/git-sync]: Repo /tmp/demoorg/git/2141a7bce6934ea43d5ce6535ab5f0003fb236a6ae1968ee9e55525f is new, cloning...!
[thread#3:plugins/scanners/git-census]: Parsing log for 2141a7bce6934ea43d5ce6535ab5f0003fb236a6ae1968ee9e55525f (file:///opt/kibble-full)...
[thread#3:plugins/scanners/git-census]: Scanning file changes for file:///opt/kibble-full
[thread#3:plugins/scanners/git-sloc]: Running SLoC count for file:///opt/kibble-full
[thread#3:plugins/scanners/git-evolution]: Doing evolution scan of 2141a7bce6934ea43d5ce6535ab5f0003fb236a6ae1968ee9e55525f
[core]: All done scanning for now, found 1 organisations and 2 sources to process.

可以清楚看到 git 类源的四级流水线依次执行:git-sync(克隆到 /tmp/demoorg/git/<sourceID 哈希>)、git-census(解析提交日志)、git-sloc(代码行统计)、git-evolution(按季度的历史回溯)。程序默认开最多 4 个工作线程并行处理多个数据源。

命令行还有几个实用参数:--age N 跳过 N 小时内刚扫描过的源(增量调度)、--source <sourceID 或 URL> 只扫指定源、--exclude <插件名列表> 排除不需要的插件。若数据源里没有 JIRA、Jenkins 等对应资源,相关插件自动跳过,无需显式排除。 把关键日志逐行读一遍,每行都对应一个机制点。Using direct ElasticSearch broker model 确认直连模式生效;Connected! 之后的 This is a type-less DB 与 NO DOC_TYPE 两行是 ES 8 兼容层在起作用——ES 7 起移除了文档类型概念,采集器按 versionHint 8 切换到"扩展数据库名"的写入策略,这两行日志是兼容性正常的最直观证据;Initialized thread 1-4 是线程池就绪;git-sync 行的 Checking out 后面跟着的正是 sourceID(SHA-224 哈希),与登记 API 生成的标识一致,说明主程序从存储层正确取回了源清单;is new, cloning...! 表明这是首次同步(scratchdir 下还没有该源的副本);最后 All done 一行的 found 1 organisations and 2 sources 汇总了任务面(组织内两条 git 源——登记过的 kibble-full 与部署篇克隆的 kibble-1 都在清单里)。

4.3 排障实录二:缺少 cloc 工具

首次采集时系统未装 cloc,git-sloc 插件抛出真实异常:

/bin/sh: 1: cloc: not found
Exception in thread Thread-1:
Traceback (most recent call last):
  File "/opt/kibble-scanners/src/kibble-scanner.py", line 108, in run
    scanner.scan(self.bit, obj)
  File "/opt/kibble-scanners/src/plugins/scanners/git-sloc.py", line 66, in scan
    languages, codecount, comment, blank, years, cost = plugins.utils.sloc.count(gpath)
  File "/opt/kibble-scanners/src/plugins/utils/sloc.py", line 32, in count
    inp = subprocess.check_output("cloc --quiet --progress-rate=0 --processes=4 " + path, shell = True)
subprocess.CalledProcessError: Command 'cloc --quiet --progress-rate=0 --processes=4 /tmp/demoorg/git/2141a7bce6934ea43d5ce6535ab5f0003fb236a6ae1968ee9e55525f' returned non-zero exit status 127.

执行 apt-get install -y cloc 后重跑采集恢复正常。该异常会中断当前工作线程,导致排在后面的插件(git-evolution)与该源的数据不完整,因此 cloc 属于采集前的必备依赖。

4.4 排障实录四:浅克隆源导致提交统计为空

使用浅克隆(--depth 1)的仓库做采集源时,采集日志显示 git-census 正常启动、解析、结束,但 kibble_code_commit 索引始终为 0 文档。手动复现日志解析可以定位原因:census 插件用正则表达式解析 git log --numstat 的输出,每条提交记录需要"提交行 + 变更行 + 下一个冒号"三段式结构,而浅克隆仓库只有 1 条提交且位于日志末尾、后面没有分隔符,正则匹配数为 0。将采集源换成完整克隆(668 个提交)后,同一段解析逻辑正常产出数据。结论:Kibble 的 git 统计插件要求完整提交历史,不要用浅克隆仓库作为采集源。 这个坑的排查路径有普适参考价值:症状是"流水线走完、结果为空",第一直觉是插件 bug,但换源后同一段代码工作正常——变量隔离(同一个插件、两种源)把怀疑对象收敛到数据形态;再手动跑一次 git log --numstat 目视输出结构,与插件源码中的正则逐段比对,根因水落石出。整个定位过程没有改一行代码,靠的是对照实验加源码阅读,这是处理归档项目(无社区支持可问)问题的标准姿势。

4.5 采集结果验证

采集完成后统计各索引文档量:

for idx in kibble_code_commit kibble_code_commit_unique kibble_evolution kibble_file_history kibble_person; do
  printf "%s: " $idx; curl -s "http://127.0.0.1:9200/$idx/_count"; echo
done

实测回显:

kibble_code_commit: {"count":648}
kibble_code_commit_unique: {"count":648}
kibble_evolution: {"count":44}
kibble_file_history: {"count":202}
kibble_person: {"count":15}

抽查一条提交文档,确认字段结构完整(用 filter_path 只取首条):

curl -s "http://127.0.0.1:9200/kibble_code_commit/_search?size=1&filter_path=hits.hits._source"

实测回显(节选,完整文档中 files_changed 为全部 18 个变更文件路径): 五组数字值得对账。648 与 668 的差异:git log 共 668 个提交,落库 648 条,差距来自无文件变更的提交(如纯合并提交)不产生 numstat 变更行、解析正则按"含变更记录的提交"取数——用 git log --numstat | grep -c '^commit' 一类的交叉核对可以进一步确认口径;unique 索引与主索引同为 648,说明该仓库无跨源重复提交;evolution 的 44 条对应约 11 年历史按季度采样(代码库 2013 年启动);person 的 15 条即贡献者去重人数,与 git shortlog -sn 的人数口径一致。抽查文档的字段结构里,author 与 committer 是两个独立维度(该提交的作者是 G M Kallidis,committer 是 GitHub 网页合并操作产生的 noreply 账户),这是分析"写代码的人"与"合入代码的人"时必须区分的字段;files_changed 的 18 个路径对应本次提交的实际变更集。

{"hits":{"hits":[{"_source":{"id":"2141a7bce6934ea43d5ce6535ab5f0003fb236a6ae1968ee9e55525f/392068106252a66f52b85aa12ece87347a9de3f0","sourceID":"2141a7bce6934ea43d5ce6535ab5f0003fb236a6ae1968ee9e55525f","sourceURL":"file:///opt/kibble-full","organisation":"demoorg","ts":1732263249,"tsday":1732233600,"date":"2024/11/22 08:14:09","committer_name":"GitHub","committer_email":"noreply@github.com","author_name":"G M Kallidis","author_email":"gk@apache.org","insertions":249,"deletions":85,"vcs":"git","files_changed":["api/pages/org/sourcetypes.py","api/yaml/openapi/combine.py","docs/source/setup.rst","setup/setup.py"],"doctype":"code_commit","@version":1}}]}}

五、通过 API 取回统计结果

采集的数据最终以图表数据的形式通过服务端 API 暴露。以下均为实测调用(会话 Cookie 获取方式见部署篇 6.3 节)。 取数环节是部署价值的最终兑现:前三章落库的数据,在这一步变成可供图表与报表消费的时间序列与分布数据。API 全部要求会话 Cookie(未携带时返回 403),均为 POST 或带参数的 GET/PUT,请求体与响应体都是 JSON——这套接口同时服务 Vue 前端与第三方程序,本节的 curl 调用模拟的正是前端的底层动作。

5.1 月度提交时间序列

curl -s -H "Cookie: kibble_session=<会话ID>" -X POST "http://127.0.0.1:8080/api/code/commits" \
  -H "Content-Type: application/json" \
  -d '{"from":1483228800,"to":1792000000,"interval":"month","quick":true}'

实测回显(节选前几个数据点):

{"widgetType": {"chartType": "bar"}, "timeseries": [
  {"date": 1504224000, "commits": 117},
  {"date": 1506816000, "commits": 300},
  {"date": 1509494400, "commits": 4},
  {"date": 1512086400, "commits": 16},
  {"date": 1514764800, "commits": 24},
  {"date": 1517443200, "commits": 56}
], "interval": "day", "okay": true}

from/to 为 Unix 时间戳,不传时默认只查最近 6 个月——对历史仓库而言窗口外的数据会被过滤掉,返回空序列,这是使用时最容易踩的参数坑。interval 支持 day、week、month。 回显里有两处值得注意的细节:其一,请求传的是 interval month,响应里却回显 interval day——这是归档代码中回显字段固定值的实现瑕疵,实际分桶按请求参数生效(月度数据点间隔可从时间戳间距验证),图表消费方应以数据点本身为准;其二,数据点是稀疏的(2017 年 9 月出现 117、300 的峰值后骤降),这正是社区活跃度的真实形态——项目在大版本发布期提交量激增,平缓期回落数条,时间序列的价值就在于把这种节奏显性化。首尾数据点的跨度与 from/to 请求窗口一致,说明聚合边界按请求参数裁剪,未被默认 6 个月窗口截断。

5.2 语言代码行分布

curl -s -H "Cookie: kibble_session=<会话ID>" "http://127.0.0.1:8080/api/code/sloc?quick=true"

实测回显(节选):

{"languages": {
  "javascript": {"code": 17312, "comment": 400, "blank": 708},
  "python": {"code": 16720, "comment": 10066, "blank": 3260},
  "css": {"code": 15996, "comment": 332, "blank": 1384},
  "coffeescript": {"code": 10464, "comment": 722, "blank": 1280},
  "html": {"code": 10018, "comment": 450, "blank": 1048},
  "yaml": {"code": 9562, "comment": 322, "blank": 92},
  "svg": {"code": 1772, "comment": 2, "blank": 2}
}}

这份数据由 git-sloc 插件调用 cloc 生成,按语言给出代码行、注释行、空行三类计数。

5.3 代码量演进时间序列

curl -s -H "Cookie: kibble_session=<会话ID>" "http://127.0.0.1:8080/api/code/evolution?quick=true"

实测回显(节选):

{"widgetType": {"chartType": "line", "stack": true}, "timeseries": [
  {"code": 37444, "comments": 4878, "blanks": 3441, "date": 1512086400},
  {"code": 39312, "comments": 5428, "blanks": 3752, "date": 1519862400},
  {"code": 40509, "comments": 5954, "blanks": 3931, "date": 1527811200},
  {"code": 40549, "comments": 5956, "blanks": 3934, "date": 1535760000}
]}

该数据由 git-evolution 插件按季度对历史快照执行 cloc 得到,反映代码库规模随时间的增长曲线。 把 5.2 与 5.3 两份数据对照着读,能看出"当前快照"与"历史演进"两种统计的本质差异:sloc 接口给出的是采集时刻对最新代码树的横截面(javascript 17312 行居首,恰好对应 ui/ 目录的 Vue 生态),evolution 接口给出的则是历史时点的纵切面序列(2017 年末约 37444 行代码,逐季爬升到 40000 行级)。两条曲线合起来回答了两个不同的问题:代码库现在长什么样、它是怎么长成这样的。evolution 的采样成本也高得多——每个季度都要 checkout 历史快照并全量执行 cloc,这也是它作为四级流水线最后一环、被 --age 增量机制跳过最频繁的原因。

5.4 数据源状态查看

curl -s -H "Cookie: kibble_session=<会话ID>" -X PUT http://127.0.0.1:8080/api/sources \
  -H "Content-Type: application/json" -d '{"types":["git"]}'

返回当前组织全部 git 源及其 steps 字段——里面记录着每个扫描插件最近一次运行的开始/结束状态,可用于判断某条源是否采集成功、上次采集时间等。

六、增量采集与调度

6.1 增量机制

Kibble 的增量以"源 + 插件步骤"为粒度:每条源文档的 steps 字段记录每个插件上次运行的时间戳。两个途径利用它做增量:

  • 命令行 --age N:主程序在派发任务前检查该源是否在 N 小时内被任何插件处理过,是则跳过;
  • git-census 插件内部:发现之前跑过普查时,只解析上次时间点往前约两个月窗口之后的提交日志。 两级增量的设计各有一个巧思:--age 是粗粒度闸门(整个源跳不跳),census 内部窗口是细粒度漏斗(源要跑、但只解析新提交),两级叠加让"每小时调度 + 只处理新数据"的成本接近线性于新增提交数而非仓库总量。两个月窗口的存在是为了容忍漏采与补采——窗口内的提交会被重复解析一遍再按 id 幂等覆盖,用少量重复换取不漏数据的确定性。

6.2 定时调度建议

生产化场景(历史方案)通常以系统定时任务驱动,例如每小时执行一次:

0 * * * * cd /opt/kibble-scanners/src && /opt/kibble-venv/bin/python kibble-scanner.py --age 2 >> /var/log/kibble-scanner.log 2>&1

多台采集器并行时,可用配置中的 balance 选项(如 1/4 表示四台中的第一台)对任务做分片,避免重复扫描。 这套调度方案在今天的视角看依然成立,只是实现载体可以现代化:把 crontab 换成流水线的定时任务、把日志文件换成集中日志、把 --age 的"小时数"换成调度器的心跳间隔,机制本身不变。balance 分片的实现是按 sourceID 哈希取模,四台机器各自只认领自己的份额,扩缩容时改分母即可,无需中心协调器——对归档项目而言,这种无状态分片是难得的低运维设计。验证分片是否生效,看各节点日志的 found N sources 行即可:总量应等于全部源数,各节点之和等于总量。

七、常见问题(FAQ)

1. 采集器报 FileNotFoundError: conf/config.yaml 怎么办? 主程序以相对路径找配置。要么按本文 2.2 节在 src 下建立指向 conf 的软链,要么用 -f /opt/kibble-scanners/conf/config.yaml 显式指定。

2. 采集器报 cloc: not found 或 exit status 127? git-sloc/git-evolution 依赖 cloc 命令行工具,apt-get install -y cloc 即可。缺它不仅丢统计,还会中断工作线程殃及同源的其他插件。

3. 采集正常结束但 code_commit 索引是空的? 最常见原因是采集源是浅克隆(--depth 1)仓库:提交历史不完整,日志解析正则匹配不到完整记录块。换完整克隆的源即可。其次是确认 API 登记源时的 organisation 与运行采集器时 -o 的组织一致。

4. 明明登记了两条源,采集器只处理了一条? 多为 Elasticsearch 近实时刷新延迟:登记 API 写入后默认约 1 秒才对外可见,登记后立刻启动采集器可能读不到新源。等几秒重跑即可;生产上可在登记与采集之间加短暂等待。

5. -t git 参数为什么什么都扫不出来? -t 匹配的是插件完整名(如 git-sync、git-census),不是数据源类型。要限定插件就用完整名(-t git-census),要扫全部类型则省略 -t。

6. 如何只增量扫最近有变化的仓库? 用 --age 参数:kibble-scanner.py --age 12 表示 12 小时内处理过的源一律跳过。配合定时任务即可实现常规增量调度。

7. 采集远程 GitHub 仓库与本地仓库有区别吗? 对 git 类型源没有本质区别:git-sync 都是用 git 命令克隆/更新。远程仓库需要网络与凭证(私有仓库用 URL 内嵌或凭证助手段),本地路径(file://)最快,适合验证。

8. 邮件列表、JIRA 等其它源怎么接? 同样先通过 /api/sources 登记对应类型的源(JIRA 填项目 URL、pipermail 填列表归档地址等),采集器运行时对应插件自动接手。注意 ponymail 系插件依赖 Apache Pony Mail 的数据接口,twitter/github-issues 插件可能需要外部 API 凭证,配置文件中有相应段落。

9. 数据能删除重来吗? 可以。删除对应前缀索引(如 curl -X DELETE http://127.0.0.1:9200/kibble_code_commit)后重跑采集即可重建;只重扫单条源可用 --source <sourceID>。注意 source 登记文档在 kibble_source 索引,删除统计索引不影响登记。

10. 采集器的线程数可以调吗? 工作线程数取 CPU 核数与 4 的较小值(实测机器 12 核,实际开 4 线程),代码中固定上限 4,不可通过配置调整。大仓库场景瓶颈通常在 git log 解析与 cloc 运行,多开线程收益有限。

11. 采集时 Elasticsearch 报 429(too many requests)怎么办? 说明写入压力超过了单节点处理能力,常见于大批量首次导入。可以减少并行源数量(用 --source 逐个跑)、把 Elasticsearch 堆内存适当调大,或在采集器与 ES 之间加队列缓冲。

12. API 返回的 date 字段是什么格式? Unix 时间戳(秒)。图表渲染时由前端转换为可读日期;用脚本处理时可用 date -d @<时间戳> 快速查看。

13. 同一仓库想重新采集一遍,需要先删数据吗? 不必。采集插件的写入按提交 id 幂等(id 由 sourceID 与提交哈希拼接),重跑同源会覆盖更新已有文档而非追加重复。只有当 sourceID 变了(如换了 URL 登记)才会产生第二套文档,此时按旧 sourceID 删除即可。实测中"改完配置重跑"是排障的常规动作,幂等写入让这个过程无需清理步骤。

14. steps 字段里的时间戳能当采集监控用吗? 能,但要注意它是"最近一次尝试"而非"最近一次成功":插件启动时会写入开始状态,异常中断时结束状态可能缺失,判断成功与否需要结合对应统计索引的文档增长。生产化监控建议直接对统计索引做定时计数,比解析 steps 更可靠。

八、参考链接