WEBKT

Kibana 插件开发与定制:打造你的专属数据可视化利器

2 0 0 0

1. 为什么需要 Kibana 插件?

2. Kibana 插件开发基础

2.1 插件类型

2.2 开发环境准备

2.3 插件结构

3. 开发你的第一个 Kibana 插件:Hello World

3.1 创建插件目录

3.2 创建 package.json 文件

3.3 创建 public/index.js 文件

3.4 构建和运行插件

3.5 插件目录的优化

4. 插件开发进阶:定制你的 Kibana

4.1 插件的 UI 组件开发

4.2 插件的数据源扩展

4.3 插件的服务端开发

4.4 插件的配置管理

5. 实践案例:开发一个简单的自定义图表插件

5.1 需求分析

5.2 插件结构

5.3 package.json 文件

5.4 public/index.js 文件

5.5 public/MyChart.js 文件

5.6 server/index.js 文件

5.7 构建和运行插件

6. 插件开发最佳实践

7. 插件开发资源推荐

8. 总结

你好,我是老码农。在数据爆炸的时代,高效地数据可视化变得至关重要。而作为 Elastic Stack 中的重要一员,Kibana 以其强大的数据可视化能力,深受广大开发者的喜爱。你是不是也经常遇到这样的需求:Kibana 现有的功能无法满足你的特定业务场景?别担心,Kibana 提供了灵活的插件开发与定制机制,让你能够根据自己的需求,打造专属的数据可视化利器!

在这篇文章中,我将带你深入了解 Kibana 的插件开发与定制,包括如何开发自定义插件、扩展 Kibana 的功能、以及与第三方系统集成等。无论你是经验丰富的开发者,还是刚刚接触 Kibana 的新手,相信都能从中受益。

1. 为什么需要 Kibana 插件?

Kibana 本身已经提供了丰富的数据可视化功能,比如各种图表、仪表盘、地图等等。但现实世界的业务场景千变万化,很多时候,我们需要根据自己的特殊需求,对 Kibana 进行定制化扩展。

以下是一些常见的场景,这些场景表明了 Kibana 插件的重要性:

  • 自定义数据源支持: Kibana 默认支持 Elasticsearch 作为数据源,但如果你的数据存储在其他地方,比如关系型数据库、NoSQL 数据库、或者其他 API 中,你就需要开发插件来支持这些数据源。
  • 特定业务场景的图表: Kibana 提供的图表类型可能无法完全满足你的业务需求。比如,你可能需要一种特殊的网络拓扑图,或者一种能够展示时间序列数据的自定义图表。
  • 数据预处理和转换: 在将数据导入 Kibana 之前,你可能需要对数据进行预处理和转换。比如,你需要对数据进行清洗、转换数据格式、或者添加计算字段等。插件可以帮助你实现这些功能。
  • 与第三方系统集成: 你可能需要将 Kibana 与其他系统集成,比如报警系统、监控系统、或者其他业务系统。插件可以帮助你实现这些集成。
  • 增强用户体验: 你可以开发插件来增强 Kibana 的用户体验。比如,你可以开发自定义的界面组件、快捷键、或者其他功能,来提高用户的操作效率。

2. Kibana 插件开发基础

2.1 插件类型

Kibana 插件主要分为以下几种类型:

  • UI 插件 (UI Plugins): 这是最常见的插件类型,主要用于扩展 Kibana 的用户界面,比如添加新的页面、图表、组件等。UI 插件使用 React 构建,可以充分利用 React 的优势,实现灵活、交互性强的用户界面。
  • 数据源插件 (Data Source Plugins): 这种插件主要用于扩展 Kibana 的数据源支持,比如支持新的数据库类型或 API 接口。数据源插件需要实现与数据源的连接、数据查询、数据转换等功能。
  • 管理插件 (Management Plugins): 这种插件主要用于扩展 Kibana 的管理功能,比如添加新的设置选项、用户管理功能等。
  • 安全插件 (Security Plugins): 这种插件主要用于扩展 Kibana 的安全功能,比如添加自定义的身份验证方式、授权策略等。
  • 其他插件: 除了上述几种类型之外,Kibana 插件还可以用于实现其他各种功能,比如数据导出、数据导入、数据分析等等。

2.2 开发环境准备

在开始开发 Kibana 插件之前,你需要准备好开发环境。主要包括以下几个方面:

  • Node.js 和 npm: Kibana 插件开发使用 Node.js 和 npm(或 yarn)作为包管理工具。你需要确保你的系统上已经安装了 Node.js 和 npm。你可以从 Node.js 官方网站 下载安装。
  • Kibana 源码: 你需要下载 Kibana 的源码,用于构建和测试你的插件。你可以从 Elastic 官方 GitHub 仓库 克隆 Kibana 源码。
  • 开发工具: 你可以使用任何你喜欢的文本编辑器或 IDE 进行开发。推荐使用 VS Code,因为它提供了丰富的插件支持,可以提高你的开发效率。
  • 依赖包: 插件开发需要用到一些依赖包,比如 @kbn/i18n@kbn/ui-frameworkreact 等。你需要在插件的 package.json 文件中声明这些依赖。

2.3 插件结构

A. 一个典型的 Kibana 插件目录结构如下所示:

my_plugin/
├── package.json # 插件的配置文件
├── public/ # 包含客户端代码,如 React 组件、样式表等
│ ├── index.js # 插件的入口文件
│ └── ...
├── server/ # 包含服务端代码,如 API 接口等
│ ├── index.js # 服务端入口文件
│ └── ...
├── i18n/ # 国际化文件
│ └── en.json # 英文翻译
└── ...

B. package.json 文件是插件的核心配置文件,它包含了插件的名称、版本、依赖、入口文件等信息。一个典型的 package.json 文件示例如下:

{
"name": "my_plugin",
"version": "0.1.0",
"kibana": {
"version": ">=8.0.0",
"pluginCategory": "ui",
"requiredPlugins": ["data", "expressions"]
},
"main": "./server/index.js",
"kbnPlugin": {
"id": "my_plugin",
"title": "My Plugin",
"description": "A sample Kibana plugin",
"version": "0.1.0",
"license": "Apache-2.0",
"dependencies": {
"@kbn/i18n": "*",
"@kbn/ui-framework": "*",
"react": "*",
"react-dom": "*"
}
},
"devDependencies": {
"@kbn/test": "*",
"@kbn/eslint-config": "*",
"eslint": "*",
"eslint-plugin-react": "*",
"webpack": "*",
"webpack-cli": "*"
},
"scripts": {
"build": "kbn-plugin-build",
"start": "kbn-plugin-run",
"test": "kbn-plugin-test",
"lint": "eslint --ext .js,.jsx ."
},
"engines": {
"node": ">=16.0.0",
"npm": ">=6.0.0"
}
}

C. public/index.js 文件是插件的客户端入口文件,它负责注册插件的 UI 组件、路由、以及其他客户端逻辑。

D. server/index.js 文件是插件的服务端入口文件,它负责注册插件的 API 接口、处理服务端逻辑。

3. 开发你的第一个 Kibana 插件:Hello World

现在,让我们通过一个简单的“Hello World”插件来了解 Kibana 插件的开发流程。

3.1 创建插件目录

首先,在你的 Kibana 源码目录下创建一个新的插件目录。比如,我们可以创建一个名为 hello_world 的目录。

cd kibana
mkdir plugins/hello_world
cd plugins/hello_world

3.2 创建 package.json 文件

hello_world 目录下创建一个 package.json 文件,并添加以下内容:

{
"name": "hello_world",
"version": "0.1.0",
"kibana": {
"version": ">=8.0.0",
"pluginCategory": "ui"
},
"main": "./public/index.js",
"kbnPlugin": {
"id": "hello_world",
"title": "Hello World",
"description": "A simple Kibana plugin",
"version": "0.1.0",
"license": "Apache-2.0"
},
"dependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"devDependencies": {
"@kbn/eslint-config": "*",
"eslint": "*",
"eslint-plugin-react": "*",
"webpack": "*",
"webpack-cli": "*"
},
"scripts": {
"build": "kbn-plugin-build",
"start": "kbn-plugin-run",
"test": "kbn-plugin-test",
"lint": "eslint --ext .js,.jsx ."
},
"engines": {
"node": ">=16.0.0",
"npm": ">=6.0.0"
}
}

3.3 创建 public/index.js 文件

hello_world 目录下创建一个 public 目录,然后在 public 目录下创建一个 index.js 文件,并添加以下内容:

import React from 'react';
import ReactDOM from 'react-dom/client';
import { CoreSetup, CoreStart, Plugin } from 'kibana/public';
class HelloWorldPlugin implements Plugin {
setup(core: CoreSetup) {
console.log('Hello World plugin setup');
core.application.register({
id: 'hello_world',
title: 'Hello World',
async mount(domNode: HTMLElement) {
ReactDOM.createRoot(domNode).render(
<h1>Hello, World!</h1>
);
return () => {
// Clean up any resources if needed
};
},
});
}
start(core: CoreStart) {
console.log('Hello World plugin start');
}
}
export const plugin = () => new HelloWorldPlugin();

3.4 构建和运行插件

现在,你需要在 Kibana 源码目录下运行以下命令,构建和运行你的插件:

npm install # 在 kibana 根目录下安装依赖
npm run build # 在 kibana 根目录下构建项目
./bin/kibana-plugin install hello_world # 安装插件, 需要进入到kibana根目录执行该命令,hello_world是插件的相对路径,插件目录是kibana/plugins/hello_world
./bin/kibana # 启动 kibana

启动 Kibana 后,在浏览器中访问 Kibana,你应该可以在左侧导航栏中看到“Hello World”的链接。点击该链接,你就可以看到“Hello, World!”的字样了。

3.5 插件目录的优化

为了让目录结构更清晰,我们来优化一下。

  1. 删除 main 字段:在package.json中删除main字段,因为我们不需要为服务端代码提供入口。
  2. 创建 publicserver 目录:在 hello_world 目录下创建 publicserver 目录。
  3. 移动 public/index.js:将原来的 index.js 文件移动到 public 目录下。
  4. 创建服务端入口:在 server 目录下创建一个 index.js 文件,这个文件可以为空,或者包含一些服务端逻辑。

修改后的目录结构如下:

hello_world/
├── package.json
├── public/
│ └── index.js
└── server/
└── index.js

package.json 文件中的 main 字段可以删除,或者设置为 ./public/index.js

4. 插件开发进阶:定制你的 Kibana

4.1 插件的 UI 组件开发

Kibana 的 UI 插件使用 React 构建,你可以使用 React 的各种特性,开发灵活、交互性强的 UI 组件。

以下是一些常见的 UI 组件开发技巧:

  • 使用 Kibana UI 框架: Kibana 提供了自己的 UI 框架,包括各种 UI 组件、样式、图标等。使用 Kibana UI 框架可以使你的插件与 Kibana 的整体风格保持一致。
  • 使用 React 组件: 你可以使用 React 组件来构建你的 UI。你可以使用各种 React 组件库,比如 Ant Design、Material UI 等,来快速构建 UI。
  • 使用状态管理: 如果你的插件需要管理状态,你可以使用 React 的状态管理库,比如 Redux、MobX 等。
  • 使用路由: 如果你的插件需要支持多页面,你可以使用 React 路由库,比如 React Router。
  • 使用 API: 如果你的插件需要与服务端进行交互,你可以使用 fetch API 或 axios 等库来发送 HTTP 请求。

4.2 插件的数据源扩展

如果你需要支持自定义的数据源,你需要开发数据源插件。数据源插件需要实现以下功能:

  • 连接数据源: 你需要实现与数据源的连接逻辑,比如连接数据库、API 接口等。
  • 查询数据: 你需要实现查询数据的逻辑,比如编写 SQL 查询语句、调用 API 接口等。
  • 转换数据: 你需要将数据源返回的数据转换为 Kibana 可以理解的格式,比如 JSON 格式。

4.3 插件的服务端开发

插件的服务端开发主要用于处理 API 请求,以及进行一些后端逻辑处理。

  • 创建 API 接口: 你可以使用 Node.js 的 Express 框架或其他 Web 框架来创建 API 接口。
  • 处理 API 请求: 你需要编写代码来处理 API 请求,比如获取请求参数、查询数据、返回数据等。
  • 访问数据源: 如果你的插件需要访问数据源,你需要编写代码来连接数据源、查询数据等。

4.4 插件的配置管理

你可以通过插件的配置管理功能,让用户可以自定义插件的配置。

  • 定义配置选项: 你需要在插件的 package.json 文件中定义配置选项。
  • 获取配置选项: 你可以在插件的客户端和服务端代码中获取配置选项。
  • 更新配置选项: 你可以提供 UI 界面,让用户可以更新配置选项。

5. 实践案例:开发一个简单的自定义图表插件

现在,让我们通过一个实践案例,来开发一个简单的自定义图表插件。

5.1 需求分析

我们的需求是开发一个简单的柱状图插件,它可以展示 Elasticsearch 中某个索引的数据,并根据某个字段的值进行分组和统计。

5.2 插件结构

我们的插件目录结构如下:

my_chart/
├── package.json
├── public/
│ ├── index.js
│ ├── MyChart.js # 自定义图表组件
│ └── ...
└── server/
└── index.js

5.3 package.json 文件

{
"name": "my_chart",
"version": "0.1.0",
"kibana": {
"version": ">=8.0.0",
"pluginCategory": "ui",
"requiredPlugins": ["data"]
},
"main": "./public/index.js",
"kbnPlugin": {
"id": "my_chart",
"title": "My Chart",
"description": "A custom chart plugin",
"version": "0.1.0",
"license": "Apache-2.0",
"dependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0",
"@elastic/eui": "*",
"echarts": "*"
}
},
"devDependencies": {
"@kbn/eslint-config": "*",
"eslint": "*",
"eslint-plugin-react": "*",
"webpack": "*",
"webpack-cli": "*"
},
"scripts": {
"build": "kbn-plugin-build",
"start": "kbn-plugin-run",
"test": "kbn-plugin-test",
"lint": "eslint --ext .js,.jsx ."
},
"engines": {
"node": ">=16.0.0",
"npm": ">=6.0.0"
}
}

5.4 public/index.js 文件

import React from 'react';
import ReactDOM from 'react-dom/client';
import { CoreSetup, CoreStart, Plugin } from 'kibana/public';
import { MyChart } from './MyChart';
import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
class MyChartPlugin implements Plugin {
setup(core: CoreSetup) {
console.log('MyChart plugin setup');
core.application.register({
id: 'my_chart',
title: 'My Chart',
async mount(domNode: HTMLElement) {
ReactDOM.createRoot(domNode).render(
<EuiFlexGroup direction="column" gutterSize="s">
<EuiFlexItem>
<MyChart core={core} />
</EuiFlexItem>
</EuiFlexGroup>
);
return () => {
// Clean up any resources if needed
};
},
});
}
start(core: CoreStart) {
console.log('MyChart plugin start');
}
}
export const plugin = () => new MyChartPlugin();

5.5 public/MyChart.js 文件

import React, { useState, useEffect, useRef } from 'react';
import { EuiSelect, EuiFormRow, EuiButton, EuiText, EuiPanel } from '@elastic/eui';
import * as echarts from 'echarts';
export const MyChart = ({ core }) => {
const [index, setIndex] = useState('');
const [field, setField] = useState('');
const [data, setData] = useState(null);
const chartRef = useRef(null);
const [indices, setIndices] = useState([]);
useEffect(() => {
const fetchIndices = async () => {
try {
const { data: indices } = await core.http.get('../api/my_chart/indices');
setIndices(indices);
} catch (error) {
console.error('Failed to fetch indices:', error);
}
};
fetchIndices();
}, [core.http]);
useEffect(() => {
if (data) {
const chartDom = chartRef.current;
if (!chartDom) return;
const myChart = echarts.init(chartDom);
const option = {
title: {
text: 'My Chart',
},
tooltip: {},
xAxis: {
type: 'category',
data: data.map(item => item.key),
},
yAxis: {
type: 'value',
},
series: [
{
data: data.map(item => item.doc_count),
type: 'bar',
},
],
};
myChart.setOption(option);
window.addEventListener('resize', () => {
myChart.resize();
});
return () => {
window.removeEventListener('resize', () => {
myChart.resize();
});
};
}
}, [data]);
const handleIndexChange = (e) => { setIndex(e.target.value); };
const handleFieldChange = (e) => { setField(e.target.value); };
const fetchData = async () => {
if (!index || !field) return;
try {
const { data: results } = await core.http.post('../api/my_chart/search', {
index,
field,
});
setData(results);
} catch (error) {
console.error('Failed to fetch data:', error);
setData(null);
}
};
return (
<EuiPanel>
<EuiText><h2>My Custom Chart</h2></EuiText>
<EuiFormRow label="Index">
<EuiSelect
options={indices.map(idx => ({
value: idx,
text: idx,
}))}
value={index}
onChange={handleIndexChange}
/>
</EuiFormRow>
<EuiFormRow label="Field">
<EuiSelect
options={[
{ value: 'status', text: 'status' },
{ value: 'method', text: 'method' },
{ value: 'agent', text: 'agent' },
]}
value={field}
onChange={handleFieldChange}
/>
</EuiFormRow>
<EuiButton onClick={fetchData} fill>Fetch Data</EuiButton>
<div ref={chartRef} style={{ width: '100%', height: '400px' }} />
</EuiPanel>
);
};

5.6 server/index.js 文件

import { schema } from '@kbn/config-schema';
export function defineRoutes(router) {
router.get(
{
path: '/api/my_chart/indices',
validate: false,
options: {
tags: ['access:kibana'],
},
},
async (context, request, response) => {
const client = context.core.elasticsearch.client.asCurrentUser;
try {
const { body } = await client.cat.indices({
format: 'json',
});
return response.ok({
body: body.map(item => item.index),
});
} catch (error) {
console.error('Failed to fetch indices:', error);
return response.internalError({
body: { message: 'Failed to fetch indices' },
});
}
}
);
router.post(
{
path: '/api/my_chart/search',
validate: {
body: schema.object({
index: schema.string(),
field: schema.string(),
}),
},
options: {
tags: ['access:kibana'],
},
},
async (context, request, response) => {
const client = context.core.elasticsearch.client.asCurrentUser;
const { index, field } = request.body;
try {
const { body } = await client.search({
index,
size: 0,
body: {
aggs: {
group_by_field: {
terms: {
field: field,
},
},
},
},
});
return response.ok({
body: body.aggregations.group_by_field.buckets,
});
} catch (error) {
console.error('Failed to search data:', error);
return response.internalError({
body: { message: 'Failed to search data' },
});
}
}
);
}

5.7 构建和运行插件

构建和运行插件的步骤与“Hello World”插件相同。

npm install
npm run build
./bin/kibana-plugin install my_chart
./bin/kibana

启动 Kibana 后,你可以在左侧导航栏中看到“My Chart”的链接。点击该链接,你可以选择索引和字段,然后点击“Fetch Data”按钮,就可以看到自定义的柱状图了。

6. 插件开发最佳实践

  • 代码风格: 保持一致的代码风格,使用 ESLint 等工具进行代码规范检查。
  • 错误处理: 做好错误处理,提供友好的错误提示,方便用户排查问题。
  • 测试: 编写单元测试和集成测试,确保插件的质量和稳定性。
  • 文档: 编写详细的文档,包括插件的安装、配置、使用方法等。
  • 性能优化: 关注插件的性能,避免出现性能瓶颈。
  • 版本控制: 使用版本控制工具,比如 Git,管理你的插件代码。
  • 安全性: 关注插件的安全性,避免出现安全漏洞。

7. 插件开发资源推荐

  • Elastic 官方文档: Kibana 插件开发文档 - 这是最权威的文档,提供了详细的插件开发指南。
  • Kibana 源码: Elastic 官方 GitHub 仓库 - 你可以在这里找到 Kibana 的源码,学习其他插件的实现方式。
  • Kibana 插件示例: Kibana 官方提供了一些插件示例,可以帮助你快速入门。
  • 社区论坛: Elastic 社区论坛是一个很好的交流平台,你可以在这里提问,与其他开发者交流经验。

8. 总结

Kibana 插件开发为我们提供了无限可能,通过插件,你可以根据自己的需求,定制个性化的数据可视化方案,扩展 Kibana 的功能,并与其他系统集成。希望这篇文章能够帮助你入门 Kibana 插件开发,并为你的数据可视化之旅提供帮助。

记住,不断学习和实践是掌握插件开发的关键。 祝你开发愉快,创造出更多有用的 Kibana 插件!

老码农 Kibana插件开发数据可视化Elasticsearch

评论点评

打赏赞助
sponsor

感谢您的支持让我们更好的前行

分享

QRcode

https://www.webkt.com/article/8254