关于 custom-visualization-builder
用于构建 Splunk 自定义仪表板可视化的技能,面向开发者,支持 JavaScript 或 React,并可将结果打包为 Splunk 应用。
主要功能
- 01创建可视化项目骨架
- 02使用 JavaScript 或 React 开发
- 03接收 Splunk 搜索数据
- 04构建并打包为 .spl
- 05迁移旧版 RequireJS 可视化
适用场景
- 01为 Dashboard Studio 新建自定义图表
- 02向现有扩展项目添加可视化
- 03将可视化打包为 Splunk 应用
- 04迁移旧版 RequireJS 可视化
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ splunk/ splunk-agent-skills --skill custom-visualization-builder安装后在 Claude Code 中输入/custom-visualization-builder确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlreferences/api-reference.mdcanvas.mdconfig-json.mddrilldown-and-tokens.mdmigration.mdtroubleshooting.md详细介绍
自定义可视化构建器
使用 @splunk/dashboard-studio-extension 框架搭建、构建、打包并安装自定义 Splunk 仪表板可视化。可视化在沙盒化 iframe 中运行,并接收 Splunk 搜索的数据。你可以使用纯 JavaScript 或 React 构建,具体取决于可视化的需求。
开发时不需要本地 Splunk 实例。在本地构建并打包,然后将 .spl 安装到受支持的实例中。需要 Splunk Enterprise 10.4+ 或 Splunk Cloud Platform(同等版本)。
适用场景
当用户要求创建新的 Splunk 自定义可视化、向现有 dashboard-studio-extension 项目添加可视化、将可视化打包为 Splunk 应用,或将旧版 RequireJS 自定义可视化迁移到当前扩展框架时,请使用此技能。
除非用户明确确认目标环境,否则不要在生产环境或共享 Splunk 环境中安装或更新可视化。不要向用户索取原始凭据;使用已配置的本地 Splunk 身份验证,或让用户自行运行安装命令。
前置条件
- Node.js >= 22
- Yarn >= 1.22
- 目标实例:Splunk Enterprise 10.4+ 或 Splunk Cloud Platform(同等版本)
选择框架
JavaScript(@splunk/dashboard-studio-extension) | React(@splunk/dashboard-studio-extension/react) | |
|---|---|---|
| 适用场景 | Canvas 渲染、D3、纯 DOM、依赖项最少 | 组件较多的 UI、基于 hooks 的状态管理、大多数新项目 |
| API 风格 | 事件侦听器 + getter | Hooks(useDataSources、useDimensions、useOptions、…) |
| 入口文件 | visualization.js | visualization.jsx |
| 额外依赖 | 无 | react、react-dom(peer 依赖,由脚手架添加) |
项目结构
项目是一个 Splunk 应用,可在 visualizations/ 下包含一个或多个可视化。每个可视化都有自己的目录,其中包含 config.json、src/ 和 SPL.md。
my-viz-project/
├── package.json
├── build.mjs # esbuild orchestrator
├── build-plugins/
│ └── css-and-size.mjs # CSS injection + asset inlining plugin
├── package.mjs # .spl packager
├── package/
│ └── app/
│ └── app.conf # Splunk app identity (source of truth for id/version)
├── visualizations/
│ ├── <viz-one>/
│ │ ├── SPL.md # SPL reference for this viz (you create this)
│ │ ├── config.json
│ │ └── src/
│ │ ├── visualization.js(x)
│ │ └── visualization.css
│ └── <viz-two>/
│ ├── SPL.md
│ ├── config.json
│ └── src/
│ ├── visualization.js(x)
│ └── visualization.css
└── dist/ # build output (generated)
构建系统会自动发现 visualizations/ 下的所有目录,并构建其中的所有项目。项目文件夹应以可视化集合命名,而不是以单个可视化命名(例如,splunk-custom-vizzes,而不是 gauge)。
工作流概览
1. 收集需求
生成任何代码之前,先询问用户(或从上下文中提取)以下信息:
- 项目名称 — 用作 Splunk 应用 ID。只能包含字母、数字、点和下划线。不能以点或下划线开头或结尾。例如:
my_custom_viz。 - 可视化 slug —
visualizations/下的目录名。只能包含小写字母、数字和下划线([a-z0-9_])。不能包含点、斜杠或空格。拒绝任何会将文件放到visualizations/<viz-slug>/之外的值。例如:network_graph。 - 可视化显示标签 — 在 Splunk UI 中显示的易读名称,例如“Network Graph”。
- 描述 — 用一行描述该可视化的功能。
- 作者 — 用于
app.conf。 - 预期 SPL 列 — 搜索必须生成哪些字段。区分必需字段和可选字段。
- 可配置设置 — 用户应能够调整的内容(颜色、阈值、开关)。对每项设置说明名称、类型和默认值。
- 渲染方式 — 要绘制或渲染的内容(Canvas 形状、图表、表格、地图等),用于确定框架选择。
除非用户的请求或上下文明确不适用,否则请采用无障碍设计与安全渲染中的默认设置。
如果用户的请求含糊不清,请在搭建项目前先询问。
2. 确定项目位置
如果用户尚未指定,请询问项目的创建位置。可以是任何空目录——不需要现有的 dashboard-enterprise 仓库。
向现有项目添加可视化:如果项目已有构建基础设施,则完全跳过第 3 步。只需创建 visualizations/<viz-name>/,并添加 config.json、src/visualization.js(x)、src/visualization.css 和 SPL.md。构建系统会自动发现所有可视化目录,无需注册。然后转到第 5 步。
3. 搭建项目脚手架
@splunk/create CLI 目前需要通过交互式提示输入 label、description、author 和 template——只能将项目名称作为位置参数传入。完整的非交互式支持计划后续推出。请让用户运行命令并回答其余提示:
“请在终端中运行此命令并按照提示操作,完成后告知我:”
mkdir <project-name> && cd <project-name> npx @splunk/create@latest --mode=dashboard-studio-extension <app_id>遇到提示时,请使用以下值:
- 显示标签:
<Human Readable Label>- 描述:
<description>- 作者:
<author>- 模板:JavaScript 或 React(根据上文选择的框架)
然后运行
yarn安装依赖项。
请等待用户确认脚手架搭建完成后再继续。
搭建脚手架后,请验证 package/app/app.conf 中的应用身份信息是否正确。
脚手架生成的结构如下(值由你在提示中提供的答案填入):
[package]
id = <app_id>
[id]
version = 0.1.0
name = <app_id>
[ui]
is_visible = 1
label = <Human Readable Label>
show_in_nav = 0
[launcher]
author = <author>
description = <description>
version = 0.1.0
[install]
is_configured = 0
build = <@- buildNumber @>
check_for_updates = 0
[manifest]
category = Custom
应用 id:只能包含字母、数字、点和下划线。不能以点或下划线开头或结尾。
请原样保留
build = <@- buildNumber @>。 这是有意使用的模板标签—— 打包工具(yarn package)会在暂存应用时替换为实际的构建编号。 不要将其替换为固定值。
然后在 viz 目录中创建 SPL.md(visualizations/<viz-name>/SPL.md):
# <Display Label> — SPL Reference
## Expected Columns
| Column | Type | Required? | Description |
|--------|------|-----------|-------------|
| <col> | <type> | Yes/Optional | <what it is> |
## Notes
- Key data assumptions, units, fallback behavior, etc.
## Full SPL
\`\`\`spl
<full working SPL query>
\`\`\`
## Time range
`rt-1m` to `rt` (or `-60m` to `now` for historical)
4. 创建可视化
visualizations/<viz-name>/config.json — 完整字段参考(包括 optionsSchema 和 editorConfig)请参阅 references/config-json.md:
{
"showTitleAndDescription": true,
"includeInToolbar": true,
"includeInVizSwitcher": true,
"showDrilldown": false,
"canSetTokens": [],
"hasEventHandlers": false,
"config": {
"name": "<Human Readable Name>",
"description": "<description>",
"category": "Custom",
"icon": null,
"dataContract": {
"requiredDataSources": ["primary"],
"optionalDataSources": []
},
"size": { "initialWidth": 600, "initialHeight": 500 },
"optionsSchema": {},
"editorConfig": []
}
}
JavaScript — visualization.js:
数据始终以列式格式到达:{ fields: [{name}, ...], columns: [[col0val0, col0val1, ...], ...] }。所有值都是字符串——请显式解析数值。监听器方法会返回一个清理函数。
import { VisualizationAPI } from '@splunk/dashboard-studio-extension';
import './visualization.css';
const container = document.createElement('div');
container.className = 'viz-container';
document.body.appendChild(container);
const state = { data: null, loading: false, options: {}, width: 0, height: 0, theme: 'light' };
function render() {
if (state.loading) { container.innerHTML = '<div class="loading">Loading...</div>'; return; }
if (!state.data) { container.innerHTML = '<div class="empty">No data</div>'; return; }
const { fields, columns } = state.data;
if (!columns?.length || !columns[0].length) {
container.innerHTML = '<div class="empty">No data</div>';
return;
}
// Always provide fallbacks — options may be unset even if optionsSchema declares defaults
const myOption = state.options.myOption ?? 'default';
const isDark = state.theme === 'dark';
// implement visualization here
}
VisualizationAPI.addDataSourcesListener(
({ dataSources, loading }) => {
state.loading = loading;
state.data = dataSources?.primary?.data ?? null;
render();
},
{ invokeImmediately: true }
);
VisualizationAPI.addOptionsListener(({ options }) => {
state.options = options;
render();
});
VisualizationAPI.addDimensionsListener(({ width, height }) => {
state.width = width;
state.height = height;
render();
}, { invokeImmediately: true });
VisualizationAPI.addThemeListener(({ theme }) => {
state.theme = theme;
render();
}, { invokeImmediately: true });
React — visualization.jsx:
直接在组件中调用这些钩子。
import {
useDataSources,
useDimensions,
useOptions,
useTheme,
} from '@splunk/dashboard-studio-extension/react';
import { createRoot } from 'react-dom/client';
import './visualization.css';
function Viz() {
const { dataSources, loading } = useDataSources();
const { width, height } = useDimensions();
const { options } = useOptions();
const { theme } = useTheme();
// Always provide fallbacks — options may be unset even if optionsSchema declares defaults
const myOption = options?.myOption ?? 'default';
const isDark = theme === 'dark';
if (loading) return <div className="loading">Loading...</div>;
const data = dataSources?.primary?.data;
if (!data?.columns?.length || !data.columns[0].length)
return <div className="empty">No data</div>;
const { fields, columns } = data;
// implement visualization here
return <div style={{ width, height }} />;
}
createRoot(document.getElementById('root')).render(<Viz />);
5. 开发
# Watch mode — rebuilds on file changes
yarn dev
请确认已创建 dist/<viz-name>/visualization.js。硬刷新浏览器以查看更改。
6. 构建生产版本
yarn build:prod
7. 打包
yarn package
输出:dist/<app-id>-<version>-<hash>.spl
打包工具会根据在 dist/ 中找到的可视化自动生成 default/visualizations.conf(其中包含 framework_type = studio_visualization)和 metadata/default.meta。
8. 安装到 Splunk
Splunk Enterprise(10.4+):依次选择 Apps > Manage Apps > Install app from file → 选择 .spl。无需重启——应用会立即加载。
Splunk Cloud Platform:安装私有应用需要 ACS CLI 和 AppInspect 审核。不要引导 Cloud 用户使用 Splunk Web 安装界面——该界面无法安装私有应用。请引导用户查阅 Splunk ACS 文档,了解私有应用提交流程。
本地 Docker:将 .spl 复制到容器中,然后优先使用 splunkctl
通过 REST 接口安装到已配置的本地测试实例:
docker cp dist/<app-id>-<version>-<hash>.spl <container>:/tmp/app.spl
splunkctl --yes app install /tmp/app.spl --update 1 --output json
splunkctl v0.1.0 要求此安装 POST 命令带有 --yes。只有在用户明确批准确切的本地实例、软件包、更新行为和范围受限的非生产环境安装后,才能使用此选项。此选项绝不授权在生产环境或共享环境中安装。
如果无法使用 splunkctl,请说明这一点,并且仅在能够保持相同的非生产目标和安装范围时,使用客户明确批准且客户首选的现有本地安装方式。绝不要擅自安装或配置客户端;生产环境或共享环境中的安装必须另行获得明确批准。
9. 告知用户
- Splunk 应用 ID
- 用于仪表板 JSON 的可视化类型字符串:
<app-id>.<viz-name> - 用于接入数据的 SPL 查询示例
在报告可视化已完成之前,请验证以下内容(除非用户的请求明确表明不适用):
- 将不可信的搜索值写入 DOM 前已进行转义
- 交互元素可通过键盘访问,并具有无障碍标签
- 文本和图标颜色在浅色和深色主题下均符合 WCAG AA 对比度要求
- 动画遵循
prefers-reduced-motion
示例
从头开始创建新的画布可视化:
“为我构建一个显示主机之间连接关系的网络图可视化。”
技能会收集需求、请用户搭建脚手架、创建使用画布渲染的 visualization.js,接入数据/维度/主题监听器,并打包 .spl。
向现有项目添加可视化:
“向我现有的自定义可视化项目添加一个仪表盘可视化。”
技能会跳过搭建脚手架,在 visualizations/gauge/ 中创建 config.json、src/visualization.js 和 SPL.md,并指导用户重新构建。
迁移旧版可视化:
“将我的 RequireJS 自定义可视化迁移到新框架。”
技能会读取现有可视化,将 updateView 映射为 render(),用 VisualizationAPI 监听器替换 SplunkVisualizationBase,移除 RequireJS 封装,并重新搭建项目脚手架。
更新可视化(快速迭代)
仅修改可视化代码时,可直接复制构建好的 JS,无需重新安装:
yarn dev # or yarn build:prod
cp dist/<viz-name>/visualization.js \
$SPLUNK_HOME/etc/apps/<app-id>/appserver/static/visualizations/<viz-name>/visualization.js
对浏览器执行硬刷新。无需重启 Splunk。
如需修改 app.conf、config.json 或添加新的可视化,请完整重新安装(步骤 7–8)。
需要重启才能启用
showDrilldown:在已安装的应用中启用showDrilldown: true后,必须重启 Splunk — REST 层会在启动时缓存config.json,仅重新安装无法更新“交互”面板。
主题支持
可视化应响应 Splunk 的浅色/深色主题。iframe 处于沙盒中,不会继承宿主页面的 CSS 自定义属性。请使用硬编码的企业主题值。
JavaScript:
const THEME_COLORS = {
light: { background: '#ffffff', text: '#1a1a1a', accent: '#4e9cf5' },
dark: { background: '#1a1c21', text: '#c3cbd4', accent: '#4e9cf5' },
};
VisualizationAPI.addThemeListener(({ theme }) => {
state.theme = theme;
render();
}, { invokeImmediately: true });
function render() {
const colors = THEME_COLORS[state.theme] ?? THEME_COLORS.light;
container.style.background = colors.background;
// use colors.text, colors.accent in drawing logic
}
React:
const THEME_COLORS = {
light: { background: '#ffffff', text: '#1a1a1a', accent: '#4e9cf5' },
dark: { background: '#1a1c21', text: '#c3cbd4', accent: '#4e9cf5' },
};
function Viz() {
const { theme } = useTheme();
const colors = THEME_COLORS[theme] ?? THEME_COLORS.light;
return <div style={{ background: colors.background, color: colors.text }}>...</div>;
}
无障碍与安全渲染
以下是默认做法,除非用户的请求或上下文明确表明不适用,否则请遵循。
将不可信的搜索值写入 DOM 前先进行转义。 切勿将来自 dataSources 的原始字段值与 innerHTML 或 insertAdjacentHTML 一起使用。请改用 textContent、createElement 或清理辅助函数。
// safe
const el = document.createElement('div');
el.textContent = row.label;
// unsafe — do not do this
container.innerHTML = `<div>${row.label}</div>`;
确保交互元素可通过键盘访问。 如果点击或悬停会触发行为,请确保元素具有 tabIndex="0",并能处理 Enter/Space 对应的 keydown 事件。
el.setAttribute('tabindex', '0');
el.setAttribute('role', 'button');
el.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') handleClick();
});
提供文本替代内容。 画布元素和仅含图标的控件需要无障碍标签。
canvas.setAttribute('role', 'img');
canvas.setAttribute('aria-label', 'Network graph showing host connections');
符合 WCAG AA 对比度要求。 前景文本和图标相对于背景的对比度至少为 4.5:1(大号文本为 3:1)。主题支持 中硬编码的 THEME_COLORS 值已满足默认文本的要求,请以此为基准,并验证任何自定义强调色或状态颜色。
遵循 prefers-reduced-motion。 如果可视化使用动画,请检查媒体查询;启用后应跳过或尽量减少动画。
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!reduceMotion) requestAnimationFrame(animate);
画布
有关尺寸、交互和下钻模式,请参阅 references/canvas.md。
脚手架生成的构建如何选择入口文件
脚手架生成的 build.mjs 不会自动检测每个 viz 的扩展名。每个
package.json 脚本都会传入一个全局的 --entry=<filename>,构建过程会将
这个文件名应用于 visualizations/ 下的每个目录:
const entryArg = process.argv.find((a) => a.startsWith('--entry='));
const entryFileName = entryArg ? entryArg.split('=')[1] : 'visualization.js';
// for each viz: build visualizations/<viz>/src/<entryFileName>
// if that file is missing: "Warning: <viz>/src/<entryFileName> not found, skipping"
只有当 entryFileName 以 .jsx 结尾时,才会启用 jsx: 'automatic'。
需要据此进行设计:
- 同一个脚手架项目中的所有 viz 共用一种扩展名。 使用 JavaScript 脚手架生成的
项目(
--entry=visualization.js)只会构建visualization.js文件;使用 React 脚手架生成的 项目(--entry=visualization.jsx)只会构建visualization.jsx文件。 - 扩展名不匹配的文件会被静默跳过——构建会发出警告并以状态码 0 退出,因此某个 viz
可能会在没有明显错误的情况下从
dist/中消失。如果某个 viz 没有出现在dist/中,请检查 它的入口文件是否与项目的--entry扩展名匹配。
添加 React viz
-
**React 脚手架项目:**只需添加
visualizations/<viz>/src/visualization.jsx—— 它会自动构建。 -
**JavaScript 脚手架项目,且没有值得保留的 JS viz:**最简单的方法是将整个项目切换为
.jsx。添加 React(yarn add react react-dom),将现有入口文件重命名为visualization.jsx,并在package.json中将每个--entry=visualization.js改为--entry=visualization.jsx。jsx: 'automatic'会自动启用。 -
**确实需要在一个项目中同时使用
.js和.jsxviz:**脚手架生成的单一--entry构建方式无法实现这一点。请将build.mjs中的入口查找逻辑替换为逐个 viz 解析(优先使用.jsx,找不到时回退到.js),并始终设置jsx: 'automatic':const buildOptions = { jsx: 'automatic', /* ... */ }; function resolveEntry(vizName) { for (const name of ['visualization.jsx', 'visualization.js']) { const p = join(visualizationsDir, vizName, 'src', name); if (existsSync(p)) return p; } return null; }然后从
package.json脚本中移除--entry标志。(这是本地修改,并非@splunk/create生成的内容。)
**避免在入口文件中使用
@splunk/react-ui。**脚手架 React 模板中的默认 viz 会导入@splunk/react-ui组件(Table、Paragraph、WaitSpinner),但这些组件是预先打包的 CommonJS 模块,其默认导出无法通过 脚手架的 esbuild ESM 流程保留下来——构建后的 viz 加载时会抛出Minified React error #130(收到的是对象而不是组件)。请使用普通元素(或你自己的组件)构建 UI。如果必须使用@splunk/react-ui,请做好先向build.mjs添加 esbuild 互操作 配置的准备。
迁移旧版自定义 viz
请参阅 references/migration.md,了解从 SplunkVisualizationBase 迁移到新框架的完整对应关系和逐步迁移说明。
故障排除
请参阅 references/troubleshooting.md。
关键约束
app.conf使用字面值,但build = <@- buildNumber @>除外——该模板标签是有意保留的,由打包工具填入。请勿将其替换为字面值。- 应用
id:只能包含字母、数字、点和下划线。不能以点或下划线开头或结尾。 - Viz slug:只能包含
[a-z0-9_]。拒绝包含点、斜杠或空格的值。确认解析后的路径为visualizations/<viz-slug>/,且不包含路径遍历。 - CSS 由构建插件注入 iframe——无需单独的
<link>标签 - 图片、SVG 和字体会由构建过程内联为 data URL——直接导入即可
- 运行时不要从外部 URL 加载字体或其他资源。请将它们存放在本地,并从
src/导入,以便构建过程将其内联为 data URL。 - 在视图模式下,
setOptions会被静默忽略——它仅在编辑模式下有效 - 始终提供选项回退值——运行时不保证
optionsSchema默认值可用 - viz 在沙盒 iframe 中运行——无法访问
window.parent,也不会继承宿主页面的 CSS 自定义属性
参考资料
references/api-reference.md— JavaScript 和 React 的完整 API 列表references/config-json.md—config.json字段参考、optionsSchema、editorConfig编辑器类型references/drilldown-and-tokens.md— 下钻模式和令牌读写references/canvas.md— 画布尺寸、交互性和下钻模式references/migration.md— 从旧版SplunkVisualizationBase可视化组件迁移references/troubleshooting.md— 常见错误及修复方法