使用 Elastic Workflows 监控 Kibana 仪表板访问数据

第一章:问题背景与解决方案概述

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"              // ← 支持时间范围查询
      }
    }
  }
}

类型选择详解:

  • keyword vs text :ID 和名称用于聚合统计(如"Top 10 仪表板"),而非全文搜索,因此使用 keyword
  • integer vs long :单日访问次数不可能超过 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 :选择 HideLinear 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 FormulaRuntime 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)进行高级分析
  • 文档化自定义扩展,确保团队知识传承

参考资源:

相关推荐
用户69371750013842 小时前
实测!Gemma 4 成功跑在安卓手机上:离线 AI 助手终于来了
android·前端·人工智能
陈天伟教授2 小时前
如何选择云端 CI/CD 平台
人工智能·安全·机器学习
jeffsonfu2 小时前
偏差与方差的权衡:深度学习的“中庸之道”
人工智能·深度学习
七夜zippoe2 小时前
OpenClaw TTS 语音合成详解:让 AI 助手开口说话
人工智能·ai·语音合成·tts·openclaw
rm6fEx0Z72 小时前
AUC 与 GAUC:从全局排序到用户内排序的理解
人工智能·算法·机器学习
健康人猿2 小时前
Business 降价导致 Codex 额度减少?GPT 各类套餐在 Codex 的使用限制是多少?
人工智能·gpt·chatgpt
初心未改HD2 小时前
从Java转行大模型应用,LangGraph核心能力学习
人工智能
OFIRM碳基硅基2 小时前
宇宙学终极篇:OFIRM本源场中的信息传播动力学与宇宙学唯象定量推导:从因果律重构到暗物质引力与哈勃张力的精确拟合(V2.1)
人工智能·agi·意识·宇宙学·ofirm颠覆性