原文
环境背景, Kibana 7.4.0, Elasticsearch 7.4.0
创建插件(hello_kibana)
通过一下命令生成插件目录
node scripts/generate_plugin.js plugin_name
注意: Kibana 源码所在目录名必须为 kibana
, 否则可能造成创建插件失败;(见QA 2)
$ node scripts/generate_plugin.js hello_kibana # 此处以 hello_kibana 为例
? Provide a short description hello kibana # 输入简短插件描述
? What Kibana version are you targeting? 7.4.0 # 默认依赖 Kibana 版本
? Should an app component be generated? Yes # 选择 Y
? Should translation files be generated? Yes # 选择 Y
? Should a hack component be generated? Yes # 选择 Y
? Should a server API be generated? Yes # 选择 Y
? Should SCSS be used? Yes # 选择 Y
Initialized empty Git repository in ****/kibana/plugins/hello_kibana/.git/
yarn run v1.16.0
$ node scripts/kbn bootstrap
Running [bootstrap] command from [****\kibana]:
... # 开始下载 node 依赖包, 要等一段时间
@kbn/es-query: succ Complete
kibana: $ yarn build:types && node scripts/register_git_hook
x-pack: $ node legacy/plugins/canvas/scripts/clean_storybook_dll
kibana: $ tsc --p tsconfig.types.json
kibana: Registering Kibana pre-commit git hook...
kibana: success Kibana pre-commit git hook was installed successfully.
Bootstrapping completed!
Done in 1934.49s. # 插件目录生成完成
# 记得在 .i18nrc.json 文件中补充 "translations": ["translations/zh-CN.json"]
插件目录说明
/node_modules
# 插件依赖包/public
# 插件 web 客户端代码/public/hack.js
# 插件全局事件定义, 例如: 键盘事件/public/components/main/main.js
# 插件客户端代码, 含 UI 界面和客户端逻辑;/server
# 插件服务端代码;/server/routes/example.js
# 插件服务端处理逻辑;/translations
# 插件国际化;/package.json
# node.js 模块配置文件;/index.js
# node.js 初始化文件;
调试预览
在 kibana 根目录执行命令 yarn start
启动本地调试预览;
$ yarn start # 启动本地调试预览
yarn run v1.22.10
$ node --trace-warnings --trace-deprecation scripts/kibana --dev
log [11:31:10.528] [warning][plugins-discovery] Explicit plugin paths [****\kibana\x-pack] are only supported in development. Relative imports will not work in production.
log [11:31:11.026] [info][plugins-service] Plugin initialization disabled.
watching for changes (4730 files)
log [11:31:16.605] [info][server] basepath proxy server running at http://localhost:5601/bqi # 根据该地址访问本地 Kibana
...
效果如下图:
重点代码
后端(服务端)
在 ../hello_kibana/server/routes/example.js
中定义服务端接口, 为插件提供服务接口;
与 elasticsearch 通讯的方式
const { callWithRequest } = server.plugins.elasticsearch.getCluster('data');
server.route({
path: '/api/test_plugin/example',
method: 'GET',
handler: async req => {
const params = {...}
const resp = await callWithRequest(req, 'get', params);
return resp;
},
});
callWithRequest
的参数, 可以参考文件src\legacy\core_plugins\elasticsearch\index.d.ts
getCluster('data')
表示获取 elasticsearch 的数据客户端;
有几种与 ES 通信的方式:
# 方式一
const { client } = server.plugins.elasticsearch;
client.cluster.health().then(response => {
console.log(`cluster status is: #{response.status}`);
})
# 方式二
const { callWithRequest } = server.plugins.elasticsearch;
callWithRequest(request, 'cluster.health').then(response => {
console.log(`cluster status is: #{response.status}`);
})
# 方式三
const { callWithRequest } = server.plugins.elasticsearch.getCluster('data');
callWithRequest(request, 'cluster.health').then(response => {
console.log(`cluster status is: #{response.status}`);
})
# 方式四
const { callWithInternalUser } = server.plugins.elasticsearch.getCluster('data');
callWithInternalUser('cluster.health').then(response => {
console.log(`cluster status is: #{response.status}`);
})
方式一是直接获取 ES client, 根据 ES 的 API 直接调用请求即可;
方式二是要求必须是经过身份验证之后的用户发起的请求, 而且需要将 API 作为参数传递;
方式三是区分 admin
和 data
两个集群, admin
集群是包含 .kibana
索引和任何在 Kibana 持久化的状态, data
数据集群则包括所有建立索引的文档数据;
方式四适用于启动任务和迁移, 需要在 Kibana.yml
中指定凭证; 请求时省略 request
;
GET 方式
server.route({
path: '/api/hello_kibana/cat_nodes',
method: 'GET',
handler: async req => {
const params = {
v: null,
h: 'ip,node.role,master,name',
format: 'json',
};
const resp = await callWithRequest(req, 'cat.nodes', params);
return resp;
},
});
POST 方式
server.route({
path: '/api/hello_kibana/move_shard',
method: 'POST',
handler: async req => {
let errMsg = '';
const { selectedIndex, selectedShard, selectedFromNode, selectedToNode } = req.payload;
const resp = await callWithRequest(req, 'transport.request', {
method: 'POST',
path: '_cluster/reroute',
body: {
commands: [
{
move: {
index: selectedIndex,
shard: selectedShard,
from_node: selectedFromNode,
to_node: selectedToNode,
},
},
],
},
}).catch(err => {
console.log('>>>> catch=' + JSON.stringify(err));
errMsg = err;
});
if (errMsg) {
console.log('>>>> ' + errMsg);
return JSON.stringify(errMsg);
} else {
console.log('>>>> real=' + JSON.stringify(resp));
return resp;
}
},
});
req.payload
接收前端传递的参数;- POST 可以采用
transport.request
方式传递请求和参数;
前端(Kibana UI)
自定义样式
- 在
main.js
同级目录新建_main.scss
样式文件, 在该文件中添加自定义的样式; - 在
main.js
同级目录新建_index.scss
文件并添加样式引用:@import 'main';
- 最后在
app.scss
文件中添加引用:@import 'components/main/index;
框架结构
<EuiPage>
<EuiPageBody>
<EuiPageHeader>
# 页面标题
</EuiPageHeader>
<EuiPageContent>
<EuiPageContentHeader>
# 内容标题
</EuiPageContentHeader>
<EuiPageContentBody>
# 内容部分
</EuiPageContentBody>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
与后端交互
const { httpClient } = this.props;
httpClient.get('../api/head/cat_nodes').then(resp => {
let data = resp.data;
...
});
其他
调试时可以取消加载非第三方插件:
yarn start --oss # 详见 CONTRIBUTING.md 文件
参考资料
- Kibana Plugin Development Tutorial
- kibana-plugin-development-tutorial github
- kibana-plugin-notes
- Elastic 界面库
- https://www.elastic.co/cn/blog/kibana-plugin-developers-meet-elasticsearch-clusters
- kibana7.0.1插件开发说明