一个 Prometheus Exporter,用于采集 Cloudflare 用量指标并通过 /metrics 暴露,覆盖:
- zone 级别 HTTP Analytics
- zone 级别 Spectrum Analytics
- 基于已配置 zones 的 account 汇总视图
- 每日完整数据和本月累计数据
采集模式已经调整为“上游按计划拉取一次,结果缓存在内存中”:
- 启动时会先预热一次缓存
- 之后按计划拉取一次 Cloudflare 数据,默认每 30 分钟刷新一次内存缓存
- Prometheus 抓取
/metrics时只读取内存缓存,不会触发新的 Cloudflare 请求 - 如果采集失败,会按配置重试;连续失败达到阈值后发送 Lark 机器人告警
- 如果仅部分上游查询失败(例如 Spectrum 某个窗口 500),exporter 会保留上一轮成功值,并暴露
cloudflare_analytics_partial_failure=1
当前版本重点暴露 bytes、requests 和 Spectrum ingress/egress,适合做总体流量、成本趋势和预算告警。对于 Spectrum,bytes_total 按 bytesIngress + bytesEgress 计算。
Exporter 会输出以下核心指标:
cloudflare_bytes_total_todaycloudflare_bytes_ingress_todaycloudflare_bytes_egress_todaycloudflare_bytes_cached_todaycloudflare_requests_total_todaycloudflare_bytes_total_dailycloudflare_bytes_ingress_dailycloudflare_bytes_egress_dailycloudflare_bytes_cached_dailycloudflare_requests_total_dailycloudflare_bytes_total_monthlycloudflare_bytes_ingress_monthlycloudflare_bytes_egress_monthlycloudflare_bytes_cached_monthlycloudflare_requests_total_monthlycloudflare_bytes_total_last_monthcloudflare_bytes_ingress_last_monthcloudflare_bytes_egress_last_monthcloudflare_bytes_cached_last_monthcloudflare_requests_total_last_monthcloudflare_bytes_total_closed_monthcloudflare_bytes_ingress_closed_monthcloudflare_bytes_egress_closed_monthcloudflare_bytes_cached_closed_monthcloudflare_requests_total_closed_monthcloudflare_bytes_total_last_month_to_datecloudflare_bytes_ingress_last_month_to_datecloudflare_bytes_egress_last_month_to_datecloudflare_bytes_cached_last_month_to_datecloudflare_requests_total_last_month_to_datecloudflare_analytics_query_total_dailycloudflare_analytics_last_success_timestampcloudflare_analytics_up
其中 cloudflare_analytics_query_total_daily 表示按 UTC 自然日累计的 Cloudflare 上游 API 调用次数,每天自动归零;在 Asia/Shanghai 等 UTC+8 视角下会表现为每天早上 08:00 左右切日。
所有用量指标都带有这些标签:
scope:zone或accountaccount_idzone_idzone_namezone_domainproduct:all/http/spectrum
其中 zone_id 默认表示主查询 zone。若配置了单独的 spectrum_zone_id,Spectrum 上游查询会使用该值,但指标标签仍沿用当前条目的 zone_id、zone_name、zone_domain 作为业务归属标识。
不再使用 date / month 这类时间标签。时间范围由 exporter 查询逻辑决定:
*_today: 当日从 00:00 UTC 到当前时刻的累计*_daily: 昨日完整自然日*_monthly: 本月 1 日到当前时刻的累计*_last_month: 上一个完整自然月*_closed_month: 以当前调度时区为准,最近一个已经结束的完整自然月;查询窗口按 UTC 月边界对齐,适合和 Cloudflare 月封账接口直接比对*_last_month_to_date: 上个月从 1 日到“与今天对应的自然日”为止的累计,若上月天数不足则自动截到月底
这样 Prometheus 不会因为月份标签变化而持续创建新时序,Grafana 做日趋势、月累计和月环比也更直接。
zone_domain 会作为指标标签暴露;文档中的配置和查询示例统一使用占位域名 example.com。
cloudflare:
api_token: ""
account_id: ""
zones:
- zone_id: ""
spectrum_zone_id: ""
name: ""
domain: ""
metrics:
listen_addr: ":9589"
schedule:
timezone: "Asia/Shanghai"
interval_minutes: 30
daily:
hour: 1
minute: 5
retry:
max_attempts: 5
delay_seconds: 300
alerting:
lark:
webhook_url: ""
secret: ""
title: "Cloudflare Analytics Exporter Alert"
dashboard_url: ""
mention_user_ids: "ou_xxxxxxxxxx,ou_yyyyyyyyyy"配置说明:
schedule.interval_minutes: 固定间隔轮询,单位分钟;大于 0 时优先使用,例如30表示每半小时刷新一次内存缓存schedule.daily: 兼容旧版的按天定时执行;仅当interval_minutes <= 0时才会生效schedule.timezone: 只影响任务调度在本地何时执行,不影响 Cloudflare 查询窗口,也不影响 API 调用计数的归零时间schedule.retry.max_attempts: 单次计划任务失败后的最大重试次数schedule.retry.delay_seconds: 每次重试之间的等待秒数alerting.lark.webhook_url: Lark 自定义机器人 webhook 地址,留空则不发送告警alerting.lark.secret: 机器人开启签名校验时填写alerting.lark.title: 告警标题alerting.lark.dashboard_url: 可选,告警里附带的 Grafana 看板链接alerting.lark.mention_user_ids: 可选,逗号分隔的 Larkuser_id,告警会在富文本消息中@这些人- 应用启动时会先读取与
config.yaml同目录下的.env - 如果
.env与config.yaml同时存在,.env的值优先级更高
推荐把敏感信息放到 .env:
CF_API_TOKENCF_ACCOUNT_IDCF_ZONE_IDCF_SPECTRUM_ZONE_IDCF_ZONE_NAMECF_ZONE_DOMAINLARK_WEBHOOK_URLLARK_SECRETLARK_MENTION_USER_IDS
如果你的 HTTP Analytics 和 Spectrum Analytics 不是同一个 zone,可以这样配置:
cloudflare:
zones:
- zone_id: "http-zone-id"
spectrum_zone_id: "spectrum-zone-id"
name: "example-zone"
domain: "example.com"或在 .env 中设置:
CF_ZONE_ID=http-zone-id
CF_SPECTRUM_ZONE_ID=spectrum-zone-id当前 Spectrum 查询会使用你提供的这类接口:
GET /client/v4/zones/<spectrum_zone_id>/spectrum/analytics/events/bytime?metrics=bytesIngress,bytesEgress&time_delta=day&limit=100000&since=...&until=...
并使用返回体中的 totals.bytesIngress + totals.bytesEgress 作为 Spectrum 的 bytes_total。
为避免时区偏移导致日/月总量不一致,Exporter 的 Cloudflare 查询窗口统一按 UTC 计算;schedule.timezone 仅用于决定本地什么时候触发采集任务。
换句话说:
- Cloudflare 上游查询时间窗:统一使用
UTC - 容器里的
TZ/schedule.timezone:只影响日志显示时间、每日任务在本地几点执行、以及“最近完整月”按哪个本地日期切换
因此这里配置时区不是为了让 Cloudflare API 用本地时区查询,而是为了让 exporter 在本地运维视角下更可控、更容易排障。
.env 示例请参考 .env.example。
配置文件说明:
config.yaml: 正式运行配置,给真实 exporter 使用config.verify-failure.yaml: 告警演练配置,主要用于验证重试和 Lark 通知链路
两者的主要区别:
config.yaml使用真实 Cloudflare token,采集应该成功config.verify-failure.yaml通常配合临时坏 token 覆盖使用,让采集失败,从而触发重试和告警config.verify-failure.yaml的retry.delay_seconds一般会设得更短,方便快速演练
调度配置示例:
schedule:
timezone: "Asia/Shanghai"
interval_minutes: 30
daily:
hour: 1
minute: 5
retry:
max_attempts: 5
delay_seconds: 300含义:
timezone: 调度执行使用的时区interval_minutes: 每隔多少分钟刷新一次内存缓存,这里表示每30分钟采集一次daily.hour/daily.minute: 仅在interval_minutes <= 0时使用,这里表示每天01:05retry.max_attempts: 当次采集失败后最多重试几次retry.delay_seconds: 每次重试之间等待多少秒
本地运行:
go run . -config config.yamlDocker 运行:
docker build -t cloudflare-analytics-metrics-exporter .
docker run -d --name cf-analytics-exporter \
-v $(pwd)/config.yaml:/app/config.yaml:ro \
-p 9589:9589 \
--restart unless-stopped \
cloudflare-analytics-metrics-exporter容器运行说明:
- 运行镜像时会创建并切换到非 root 的
app用户 - exporter 进程默认以
app用户身份运行
Docker Compose 运行:
仓库已包含 docker-compose.yaml,默认会:
- 构建当前目录下的镜像
- 将宿主机的配置文件挂载到容器
/app/config.yaml - 暴露可配置端口,默认
9589:9589 - 设置
restart: unless-stopped - 通过
.env读取容器名、时区、端口和配置文件路径
先准备 .env:
cp .env.example .env默认的 .env.example 中定义了:
CONTAINER_NAMEHOST_PORTCONTAINER_PORTTZCONFIG_PATHCF_API_TOKENCF_ACCOUNT_IDCF_ZONE_IDCF_SPECTRUM_ZONE_IDCF_ZONE_NAMECF_ZONE_DOMAINLARK_WEBHOOK_URLLARK_SECRETLARK_TITLELARK_DASHBOARD_URLLARK_MENTION_USER_IDS
启动:
docker compose up -d --build说明:
docker-compose.yaml同时包含build和image- 执行
docker compose up -d --build时,会优先使用当前工作区代码重新构建镜像 - 如果只执行
docker compose up -d,可能继续使用本地已有镜像或远端已拉取镜像 - 当前运行层使用
debian:bookworm-slim - 镜像中已安装
tzdata,程序本身也内置了 Gotime/tzdata,可正常识别Asia/Shanghai - 这里不需要挂载宿主机的
/etc/localtime或/usr/share/zoneinfo TZ只是容器内的时区环境变量,用于日志时间和调度触发时间
查看日志:
docker compose logs -f停止:
docker compose down如果你需要覆盖默认配置,可以参考 docker-compose.override.yaml.example:
- 配合
.env修改宿主机映射端口 - 补充环境变量
- 调整配置文件挂载路径
使用方式:
cp docker-compose.override.yaml.example docker-compose.override.yaml然后按需编辑 docker-compose.override.yaml,之后直接执行:
docker compose up -d --builddocker compose 会自动合并 docker-compose.yaml 和 docker-compose.override.yaml。
Makefile 快捷命令:
仓库已包含 Makefile,常用命令如下:
make fmt
make lint
make build
make run
make up
make logs
make ps
make down
make restart
make test-alert说明:
make fmt: 执行gofmtmake lint: 执行基础 Go 检查(当前为go test ./...)make build: 本地编译二进制make run: 使用config.yaml本地运行make up: 使用 Docker Compose 启动服务make logs: 查看 Docker Compose 日志make ps: 查看容器状态make down: 停止 Docker Composemake restart: 重建并重启 Docker Composemake test-alert: 使用config.verify-failure.yaml,并通过临时坏 token 覆盖.env,触发失败告警演练
运行行为说明:
- 进程启动后会先执行一次采集来填充缓存
- 后续只在每日计划时间向 Cloudflare 拉取数据
- 如果重试全部失败,
/metrics仍会继续暴露上一次成功的缓存 - 如果最近一次采集失败,
/healthz会返回503 - Lark 告警会包含失败 zone、失败阶段、最近一次成功时间和可选 dashboard 链接
- 当采集整体成功但部分查询降级复用上一轮值时,会发送一次“部分采集失败”告警,避免每轮重复刷屏
- Lark 告警使用富文本
post格式,支持标题、链接和@user_id
- HTTP 用量来自 Cloudflare GraphQL Analytics API
- Spectrum 用量来自 Cloudflare Spectrum Analytics Summary API
之所以这样组合,是因为 Cloudflare 官方文档明确给出了 GraphQL 的 HTTP analytics 查询方式,以及 Spectrum 独立 analytics summary endpoint 的官方接口定义。
不同月份天数已经按自然月边界处理:
- 本月累计从当月 1 日 00:00:00 开始
- 上月总量从上月 1 日开始,到本月 1 日前一秒结束
- 上月同期累计会取“上个月对应日期”的同一时刻;如果本月是 30/31 日而上月没有这一天,则自动使用上月最后一天
- 月度调度如果配置了
day: 31,遇到 2 月会自动落到 2 月最后一天,而不是报错
仓库里已经提供可导入的 Grafana 看板:
grafana/dashboards/cloudflare-analytics-overview.jsongrafana/dashboards/cloudflare-spectrum-cost-overview.json
建议在 Grafana 中把 Prometheus 数据源命名为 Prometheus,然后直接导入对应 JSON。
使用建议:
cloudflare-analytics-overview.json: 通用总览,看板默认打开为 Spectrum 视角,但仍可切换到http/allcloudflare-spectrum-cost-overview.json: 运营/成本对账视角,聚焦 Spectrum 的日流量、月封账、环比和采集健康状态
看板包含这些视图:
- 今日累计流量
- 今日累计请求数
- 昨日完整流量
- 本月累计流量
- 最近完整月流量
- 上月同期累计流量
- 今日累计流量趋势
- 今日累计请求趋势
- 本月累计 vs 上月同期累计
- 日环比百分比
- 本月同期环比
如果你的目标是做 Spectrum 成本核对,建议优先导入 cloudflare-spectrum-cost-overview.json。
关键 PromQL 示例:
如果你的目标是做 Spectrum 用量、成本估算和月结核对,建议优先使用 product="spectrum"。
cloudflare_bytes_total_daily{scope="zone", zone_domain="example.com", product="spectrum"}
cloudflare_bytes_total_monthly{scope="zone", zone_domain="example.com", product="spectrum"}
cloudflare_bytes_total_closed_month{scope="zone", zone_domain="example.com", product="spectrum"}
cloudflare_bytes_total_last_month_to_date{scope="zone", zone_domain="example.com", product="spectrum"}
(
cloudflare_bytes_total_daily{scope="zone", zone_domain="example.com", product="spectrum"}
-
cloudflare_bytes_total_daily{scope="zone", zone_domain="example.com", product="spectrum"} offset 1d
)
/
clamp_min(
cloudflare_bytes_total_daily{scope="zone", zone_domain="example.com", product="spectrum"} offset 1d,
1
) * 100
(
cloudflare_bytes_total_monthly{scope="zone", zone_domain="example.com", product="spectrum"}
-
cloudflare_bytes_total_last_month_to_date{scope="zone", zone_domain="example.com", product="spectrum"}
)
/
clamp_min(
cloudflare_bytes_total_last_month_to_date{scope="zone", zone_domain="example.com", product="spectrum"},
1
) * 100
仓库里提供了一份可直接修改后使用的 Spectrum 告警规则:
当前示例规则覆盖:
CloudflareAnalyticsCollectionFailed: exporter 连续 10 分钟没有有效快照CloudflareSpectrumDailyTrafficSpike: 昨日 Spectrum 流量较前一天增长超过 50%,且绝对值超过 500 GBCloudflareSpectrumMonthlyBudgetWarning: 本月 Spectrum 累计流量超过 5 TBCloudflareSpectrumMonthlyBudgetExceeded: 本月 Spectrum 累计流量超过 10 TB
接入 Prometheus 示例:
rule_files:
- /etc/prometheus/rules/cloudflare-spectrum-alerts.yaml如果你用的是 Docker / Kubernetes,请把规则文件挂载到 Prometheus 容器或 ConfigMap 中,然后 reload Prometheus。
Kubernetes 示例文件:
k8s/cloudflare-spectrum-alerts-configmap.yaml: 适合原生 Prometheus,通过 ConfigMap 挂载规则文件k8s/cloudflare-spectrum-alerts-prometheusrule.yaml: 适合 Prometheus Operator / kube-prometheus-stack
原生 Prometheus 落地步骤:
风险提示:修改规则文件后,Prometheus reload 前不会生效;若挂载路径或 rule_files 配置错误,可能导致新规则不加载。
kubectl -n monitoring apply -f k8s/cloudflare-spectrum-alerts-configmap.yaml
kubectl -n monitoring rollout restart deploy/prometheusPrometheus Operator 落地步骤:
风险提示:请先确认集群里 CRD prometheusrules.monitoring.coreos.com 已安装,且当前 namespace 被对应 Prometheus 实例的 rule selector 选中。
kubectl -n monitoring apply -f k8s/cloudflare-spectrum-alerts-prometheusrule.yaml验证命令:
kubectl -n monitoring get prometheusrule cloudflare-spectrum-alerts
kubectl -n monitoring get configmap cloudflare-spectrum-alerts如果你使用 kube-prometheus-stack,还可以进一步检查:
kubectl -n monitoring describe prometheusrule cloudflare-spectrum-alerts建议上线前至少调整这两个阈值:
CloudflareSpectrumMonthlyBudgetWarningCloudflareSpectrumMonthlyBudgetExceeded
默认阈值只是示例值,实际应该结合你的:
- 月预算
- 历史最近完整月流量
- 业务波峰
- Cloudflare Spectrum 计费方式
验证命令:
promtool check rules prometheus/rules/cloudflare-spectrum-alerts.yaml回滚方案:
kubectl -n monitoring delete -f k8s/cloudflare-spectrum-alerts-prometheusrule.yaml
kubectl -n monitoring delete -f k8s/cloudflare-spectrum-alerts-configmap.yaml