第一章:问题背景与解决方案概述
1.1 核心痛点
Kibana 作为 Elastic Stack 的可视化层,虽然会追踪每个仪表板的访问次数,但这些数据被隐藏在系统内部,用户无法通过任何内置界面查看:
- ❌ 不知道哪些仪表板最受欢迎
- ❌ 无法追踪访问趋势变化
- ❌ 难以识别"僵尸"仪表板(长期无人访问)
- ❌ 无法基于使用数据优化界面布局
1.2 数据隐藏在哪里?
Kibana 将配置和元数据存储在内部 Saved Objects 系统中,仪表板访问计数使用特殊的 usage-counter 类型存储。这些数据可以通过 API 访问,但没有原生界面展示。
1.3 解决方案架构
我们使用 Elastic Workflows(Kibana 9.3+ 内置的自动化引擎)构建一个数据采集管道:
数据应用层
ElasticWorkflows
Kibana内部
API调用
用户访问仪表板
Usage Counter计数器
Saved Objects API
定时触发器
每30分钟
获取访问数据
解析仪表板名称
写入ES索引
时序数据索引
自定义分析仪表板
告警规则
数据导出
方案优势:
- ✅ 零外部依赖,完全内置于 Elastic Stack
- ✅ 自动认证,无需管理 API 密钥
- ✅ YAML 配置,低代码实现
- ✅ 可扩展至其他 Saved Objects 数据
第二章:技术原理深度解析
2.1 Kibana Usage Counter 机制
2.1.1 数据存储模型
Kibana 使用每日重置的计数器模型追踪仪表板访问:
tracks / records
DASHBOARD
string
id
PK
Unique Dashboard ID
string
title
Display Title
json
panels
Configuration Data
datetime
createdAt
Creation Time
USAGE_COUNTER
string
composite_key
PK
Redis Key / Composite ID
string
dashboard_id
FK
Ref to Dashboard
int
daily_count
View Count
date
stat_date
YYYY-MM-DD
关键特性:
| 特性 | 说明 | 影响 |
|---|---|---|
| 每日独立文档 | 每个仪表板每天一个计数器对象 | 历史数据分散在多个文档中 |
| 累计计数 | count 字段在一天内持续增长 |
次日 0 点重置为 0 |
| 对象 ID 编码日期 | ID 包含日期后缀(如 20260310) |
可通过 ID 识别数据日期 |
2.1.2 原始数据结构
通过 Saved Objects API 获取的原始数据示例:
json
{
"id": "usage-counters:viewed:server:20260310",
"type": "usage-counter",
"attributes": {
"domainId": "dashboard",
"counterName": "sales-revenue-dashboard", // ← 仪表板ID
"counterType": "viewed",
"count": 24, // ← 当日访问次数
"date": "2026-03-10"
}
}
重要发现:
counterName存储的是仪表板 ID,而非人类可读的名称- 需要二次查询才能获取仪表板标题
- 已删除的仪表板可能仍保留计数器记录(孤儿数据)
2.2 数据模型设计决策
2.2.1 两种建模方案对比
Workflows采集模型
每次执行一个文档
时间序列存储
优点: 保留完整历史
支持任意粒度分析
缺点: 数据量较大
需要数据保留策略
Kibana原生模型
每日一个文档
按仪表板分组
优点: 存储紧凑
缺点: 丢失时序细节
无法追踪日内趋势
本文采用方案 B 的理由:
| 需求 | 方案 A | 方案 B |
|---|---|---|
| 追踪日内访问趋势 | ❌ 不支持 | ✅ 支持 |
| 计算平均访问间隔 | ❌ 不支持 | ✅ 支持 |
| 识别访问高峰时段 | ❌ 不支持 | ✅ 支持 |
| 检测异常访问模式 | ❌ 不支持 | ✅ 支持 |
| 长期存储效率 | ✅ 高 | ⚠️ 需配置 ILM |
2.2.2 索引映射设计
json
PUT /dashboard-views
{
"mappings": {
"properties": {
"dashboard_id": {
"type": "keyword" // ← 用于精确匹配和分组
},
"dashboard_name": {
"type": "keyword" // ← 用于展示和聚合
},
"view_count": {
"type": "integer" // ← 支持数值运算,节省存储
},
"captured_at": {
"type": "date" // ← 支持时间范围查询
}
}
}
}
类型选择详解:
keywordvstext:ID 和名称用于聚合统计(如"Top 10 仪表板"),而非全文搜索,因此使用keywordintegervslong:单日访问次数不可能超过 20 亿(integer上限),且比long节省 50% 存储date格式:ISO 8601 格式确保跨时区一致性
第三章:完整实施步骤
3.1 环境准备
3.1.1 版本要求
| 组件 | 最低版本 | 说明 |
|---|---|---|
| Elasticsearch | 9.3 | 支持 Workflows 所需的 API |
| Kibana | 9.3 | Workflows 功能内置 |
| 部署方式 | Cloud 或自托管 | 功能无差异 |
3.1.2 启用 Workflows
导航路径:Stack Management → Advanced Settings → Workflows
登录 Kibana
Stack Management
Advanced Settings
搜索 workflows
启用相关功能
3.2 步骤一:验证原始数据
在 Dev Tools 中执行以下查询,确认能获取到 Usage Counter 数据:
http
GET /api/saved_objects/_find?type=usage-counter&per_page=10000&filter=usage-counter.attributes.domainId:"dashboard"%20and%20usage-counter.attributes.counterType:"viewed"
预期响应结构:
json
{
"page": 1,
"per_page": 10000,
"total": 15,
"saved_objects": [
{
"id": "usage-counters:viewed:server:20260405",
"type": "usage-counter",
"attributes": {
"domainId": "dashboard",
"counterName": "ecommerce-revenue",
"counterType": "viewed",
"count": 42
}
}
]
}
故障排查:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 返回空数组 | 今日尚无仪表板被访问 | 访问几个仪表板后重试 |
| 返回 403 | 权限不足 | 确保用户有 saved_objects 读取权限 |
| 返回 404 | API 路径错误 | 检查 Kibana 版本是否 ≥ 9.3 |
3.3 步骤二:创建目标索引
在 Dev Tools 中执行:
json
PUT /dashboard-views
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"dashboard_id": { "type": "keyword" },
"dashboard_name": { "type": "keyword" },
"view_count": { "type": "integer" },
"captured_at": { "type": "date" }
}
}
}
生产环境优化建议:
json
PUT /dashboard-views
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1,
"index.lifecycle.name": "dashboard-views-policy", // ← 绑定 ILM 策略
"index.lifecycle.rollover_alias": "dashboard-views"
},
"mappings": {
"properties": {
"dashboard_id": { "type": "keyword" },
"dashboard_name": {
"type": "keyword",
"fields": {
"text": { "type": "text" } // ← 多字段映射,支持搜索
}
},
"view_count": { "type": "integer" },
"captured_at": { "type": "date" },
"@timestamp": { "type": "date" } // ← 兼容 Elastic 生态
}
}
}
3.4 步骤三:创建 Workflows
3.4.1 导航路径
Stack Management
Workflows
New Workflow
粘贴 YAML 配置
保存并启用
3.4.2 完整 YAML 配置
yaml
name: dashboard-views-ingestion
description: "每30分钟采集一次仪表板访问数据,用于使用分析"
triggers:
- type: scheduled
with:
every: 30m # 支持: m(分钟), h(小时), d(天)
steps:
# 步骤1: 获取所有仪表板的访问计数器
- name: fetch_dashboard_views
type: kibana.request
with:
method: GET
path: >-
/api/saved_objects/_find?type=usage-counter&per_page=10000&filter=usage-counter.attributes.domainId:"dashboard"%20and%20usage-counter.attributes.counterType:"viewed"
# 步骤2: 遍历每个计数器并索引
- name: index_each_dashboard
type: foreach
foreach: "{{ steps.fetch_dashboard_views.output.saved_objects }}"
steps:
# 子步骤2.1: 获取仪表板人类可读的名称
- name: fetch_dashboard_name
type: kibana.request
with:
method: GET
path: /api/saved_objects/dashboard/{{ foreach.item.attributes.counterName }}
on-failure:
continue: true # ← 关键: 防止已删除仪表板导致整个工作流失败
# 子步骤2.2: 将数据写入 Elasticsearch
- name: index_doc
type: elasticsearch.request
with:
method: POST
path: /dashboard-views/_doc
body:
dashboard_id: "{{ foreach.item.attributes.counterName }}"
dashboard_name: "{{ steps.fetch_dashboard_name.output.attributes.title | default: 'Unknown Dashboard' }}"
view_count: "${{ foreach.item.attributes.count | plus: 0 }}"
captured_at: "{{ execution.startedAt | date: '%Y-%m-%dT%H:%M:%SZ' }}"
workflow_execution_id: "{{ execution.id }}"
3.4.3 配置逐段解析
触发器部分:
yaml
triggers:
- type: scheduled
with:
every: 30m
| 参数 | 选项 | 建议场景 |
|---|---|---|
every |
5m |
实时监控,高频采集 |
every |
30m |
平衡实时性与系统负载(推荐) |
every |
1h |
低频分析,资源敏感环境 |
type |
webhook |
事件驱动触发(替代定时) |
数据获取部分:
yaml
- name: fetch_dashboard_views
type: kibana.request
- 自动认证:Workflows 引擎自动附加当前用户的认证头
- 上下文感知:在 Kibana 内部执行,无需处理 CORS 或网络策略
循环处理部分:
yaml
type: foreach
foreach: "{{ steps.fetch_dashboard_views.output.saved_objects }}"
foreach语法:Liquid 模板,引用上一步输出的数组- 并行度:默认顺序执行,适合中小规模(<1000 个仪表板)
错误处理部分:
yaml
on-failure:
continue: true
为什么需要这个配置?
有错误处理
仪表板已删除
continue: true
获取计数器列表
处理仪表板1
API 404错误
记录警告,继续执行
处理仪表板2...N
工作流成功完成
无错误处理
仪表板已删除
获取计数器列表
处理仪表板1
API 404错误
整个工作流失败
后续仪表板无法处理
数据写入部分:
关键语法详解:
| 语法 | 含义 | 示例输出 |
|---|---|---|
{``{ }} |
Liquid 变量插值 | "sales-dashboard" |
| `${``{ | plus: 0 }}` | 强制数值类型 |
| ` | default: '...'` | 默认值过滤器 |
| ` | date: '...'` | 日期格式化 |
类型强制转换的重要性:
正确方式
${{ count | plus: 0 }}
42
ES映射为integer
支持sum/avg/max等
错误方式
{{ count }}
42
ES映射为text
无法做数值聚合
3.5 步骤四:验证工作流执行
3.5.1 调试界面
保存工作流后,点击 Execute 手动触发一次执行,观察调试面板:
执行详情页面
执行概览
步骤时间线
fetch_dashboard_views
耗时: 45ms
index_each_dashboard
迭代次数: 15
每次迭代的子步骤
输入/输出数据
3.5.2 验证数据写入
在 Dev Tools 中查询:
http
GET /dashboard-views/_search?sort=captured_at:desc&size=5
预期响应:
json
{
"hits": {
"hits": [
{
"_source": {
"dashboard_id": "ecommerce-revenue",
"dashboard_name": "[eCommerce] Revenue Dashboard",
"view_count": 24,
"captured_at": "2026-04-05T23:00:00Z"
}
}
]
}
}
第四章:可视化与高级应用
4.1 创建分析仪表板
4.1.1 创建数据视图
导航:Stack Management → Data Views → Create Data View
| 配置项 | 值 |
|---|---|
| Name | Dashboard Analytics |
| Index pattern | dashboard-views |
| Timestamp field | captured_at |
4.1.2 推荐可视化面板
面板 1:实时访问排行(Top N)
Bar Chart
X轴: dashboard_name
Terms聚合
Y轴: last_value
view_count
排序: 降序
Size: 10
配置详解:
- 使用
last_value(view_count)而非max,确保获取最新时点的准确计数 - 添加 Top values of dashboard_name 过滤,排除测试仪表板
面板 2:访问趋势时序图
Line Chart
X轴: captured_at
Date Histogram
Y轴: last_value
view_count
Split: dashboard_name
Top 5
关键配置:
- Interval :根据时间范围自动调整,或固定为
1h观察小时级趋势 - Missing values :选择
Hide或Linear interpolation
面板 3:当前状态快照表
Data Table
Metrics: last_value
view_count
Rows: dashboard_name
Sort: view_count
Descending
增强功能:
- 添加 Trendline 列:使用
differences函数计算较上时段变化 - 添加 Sparkline 迷你图展示最近 24 小时趋势
4.2 高级分析场景
4.2.1 计算访问增长率
使用 Lens Formula 或 Runtime Fields:
javascript
// Runtime field: growth_rate
if (doc['view_count'].size() == 0) {
return 0;
}
long current = doc['view_count'].value;
long previous = doc['view_count'].value - params.delta;
if (previous <= 0) {
return 0;
}
return (current - previous) / previous * 100;
4.2.2 识别僵尸仪表板
创建告警规则(Alerting):
yaml
条件: last_value(view_count) < 5
时间窗口: last 7 days
动作: 发送邮件给仪表板所有者
4.2.3 用户活跃度热力图
扩展方案
采集用户级数据
增加user_id字段
创建热力图可视化
X轴: 小时
Y轴: 星期
颜色: 访问密度
注意:这需要修改工作流,从 Kibana 审计日志或安全事件中提取用户维度数据,超出本文基础方案范围。
第五章:生产环境优化与故障排除
5.1 性能优化
5.1.1 大规模部署优化(>1000 仪表板)
当仪表板数量庞大时,原方案可能存在性能瓶颈:
yaml
# 优化方案:分页处理
steps:
- name: fetch_dashboard_views
type: kibana.request
with:
method: GET
path: >-
/api/saved_objects/_find?type=usage-counter&per_page=1000&page={{ execution.vars.page | default: 1 }}&filter=...
- name: check_has_more
type: condition
condition: "{{ steps.fetch_dashboard_views.output.total > steps.fetch_dashboard_views.output.saved_objects | size }}"
then:
- name: trigger_next_page
type: webhook # 触发下一页处理
5.1.2 索引生命周期管理(ILM)
json
PUT _ilm/policy/dashboard-views-policy
{
"policy": {
"phases": {
"hot": {
"min_age": "0ms",
"actions": {
"rollover": {
"max_age": "30d",
"max_size": "10GB"
}
}
},
"warm": {
"min_age": "7d",
"actions": {
"shrink": { "number_of_shards": 1 },
"forcemerge": { "max_num_segments": 1 }
}
},
"cold": {
"min_age": "90d",
"actions": {
"freeze": {}
}
},
"delete": {
"min_age": "365d",
"actions": {
"delete": {}
}
}
}
}
}
5.2 常见故障与解决方案
| 故障现象 | 根因分析 | 解决方案 |
|---|---|---|
工作流执行失败,提示 404 Not Found |
仪表板已被删除但计数器仍存在 | 确认 on-failure: continue: true 已配置 |
view_count 显示为字符串,无法聚合 |
未使用 `${``{ | plus: 0 }}` 语法 |
captured_at 不是日期类型 |
未使用 date 过滤器 |
添加 ` |
| 数据重复采集 | 工作流执行间隔过短,前次未完成 | 增加 every 间隔,或添加去重逻辑 |
| 部分仪表板名称为空 | 已删除仪表板的孤儿计数器 | 使用 ` |
5.3 监控工作流本身
创建元监控工作流,监控数据采集工作流的健康状况:
yaml
name: monitor-ingestion-workflow
triggers:
- type: scheduled
with:
every: 1h
steps:
- name: check_last_execution
type: elasticsearch.request
with:
method: POST
path: /dashboard-views/_search
body:
size: 0
query:
range:
captured_at:
gte: "now-1h"
- name: alert_if_no_data
type: condition
condition: "{{ steps.check_last_execution.output.hits.total.value == 0 }}"
then:
- name: send_alert
type: webhook
with:
url: "{{ secrets.webhook_url }}"
body:
message: "Dashboard views ingestion has stopped!"
第六章:总结与扩展
6.1 核心要点回顾
Elastic Workflows
仪表板监控
数据采集
Usage Counter API
每30分钟定时触发
自动认证
数据处理
ID解析为名称
错误容错
类型转换
数据存储
时序索引设计
ILM生命周期
映射优化
数据应用
可视化仪表板
趋势分析
告警规则
6.2 扩展应用场景
本文介绍的模式可扩展至:
| 应用场景 | API 端点 | 数据价值 |
|---|---|---|
| 搜索使用分析 | /api/saved_objects/_find?type=search |
识别热门查询模式 |
| 可视化使用统计 | /api/saved_objects/_find?type=visualization |
优化可视化库 |
| 索引模式追踪 | /api/saved_objects/_find?type=index-pattern |
发现未使用的数据源 |
| 告警规则监控 | /api/alerting/rules/_find |
追踪告警触发频率 |
| 连接器使用统计 | /api/actions/connectors |
优化通知渠道配置 |
6.3 最佳实践清单
- 生产环境配置 ILM 策略,避免数据无限增长
- 为
dashboard_name添加default过滤器,处理孤儿数据 - 定期审查工作流执行日志,识别失败模式
- 为关键仪表板设置访问阈值告警
- 考虑将数据导出至 BI 工具(如 Tableau、PowerBI)进行高级分析
- 文档化自定义扩展,确保团队知识传承
参考资源: