记录这篇博客原因有二:
1. 由于工作中需要使用kibana,并且有一些特殊需求需要用到百度的图标库echarts,所以就研究了下如何开发kibana的插件;
2. ElasticSearch官网上的文档以及推荐的博客等等都是较老的版本,考虑到公司内部已经开始使用5.4的版本并且在学习过程中发现版本的差异还是会有不小的影响,因此记录在此以备后用。
上面说过kibana的插件开发,因为版本的不同可能会有差异,所以这里这里有必要写清楚我的开发环境:
ElasticSearch:5.4.0
Kibana:5.4.0
node:6.10.0
npm:3.10.10
系统为:
window 7 64位
由于kibana是nodejs开发的,所以需要安装nodejs,这一步在这篇博客中不做记录,从官网下载安装即可,另外npm是随node的,node安装完,npm也就自动安装完了~
下面是官网提到了一篇tutorial,可以作为参考(特别是这部分开发环境的搭建,写的还是蛮清楚的,后面的实际代码可能由于版本的原因会有一些出入,可以仅做参考):
https://www.timroes.de/2015/12/02/writing-kibana-4-plugins-basics/
开发kibana插件自然需要安装kibana,但由于kibana是从ElasticSearch中查询数据的,所以安装Kibana之前必须要安装ElasticSearch。
安装ElasticSearch
安装ElasticSearch其实很简单,到官网(上面链接)下载ElasticSearch(注意选择正确的版本,本篇使用5.4.0),解压到本地即可。如下图所示:
解压完成后,可以进入安装目录\bin,在cmd中运行elasticsearch.bat来启动。如下图所示:
启动完成后可以在浏览器中输入localhost:9200/ 来查看是否启动成功,看到以下信息则ElasticSearch已成功启动了:
安装Kibana
下载开发版:https://github.com/elastic/kibana
下载kibana的时候需要注意,从官网上下载的是正式版,并非开发版,不能作为开发插件使用的版本,需要在github上下载源码,另外,在github上下载的时候也要注意选择tag,即正确的版本。下载完成后和上面elasticsearch一样解压即可。
安装依赖
打开cmd,进入kibana的安装目录,直接运行npm install 命令安装依赖。
需要注意的是,这里npm install安装依赖的时候往往不成功,需要多次重试。什么原因我也并没有搞清楚,如果有前端大神对npm和nodejs了解深入的希望可以不吝赐教~~多谢啦
启动
安装完成后,同样在安装目录运行npm start命令启动kibana
看到如下信息,即为启动成功:
插件开发
以上,完成后开发环境就算搭建完成了,下面可以开始插件的开发啦!
官网上提供了一个插件生成器,本人木有用过,不知道怎么样,但是为了了解开发的过程,这篇文章没有使用这个生成器,而是纯手打,因为觉得这样印象深刻一些。
我们的开发在plugins目录下进行。在之前的版本中(官网的那篇tutorial中介绍),是在installedPlugins下进行的,不知是不是由于版本的变动,总之,现在是在plugins中进行开发了。
首先,在plugins目录下,新建一个folder,名字就改为需要创建的插件名,比如:xx-myplugin。
进入刚新建的插件目录,新建一个package.json文件,代码如下。
这是一个npm module必须的文件,里面提供了这个模块的名称、版本等等信息,还可以在里面加入依赖的包及版本,类似于java web开发中的pom文件。
package.json
{
"name": "xx-myplugin", // 必须与插件目录的名字一致
"version": "5.4.0" // 必须指定版本,但在查看源码时,发现这里也可以直接写kibana,原理未知
}
然后再新建一个文件index.js。这个文件是npm module的主文件,可以理解成java文件的main函数(可能不是很恰当,但是理解那个意思就行),就是一个主要的入口。在这个文件中我们定义插件的类型并返回一个kibana插件的实例。
index.js
export default function (kibana) {
return new kibana.Plugin({
uiExports: {
// 这里的visType就是kibana插件的类型,除了visTypes(视图类型)还有app(应用)等不同的类型
visTypes: [
'plugins/xx-myplugin/xx-myplugin' //2. 这里其实个路径,指向插件中的一个js文件,同时这个js名字也建议与插件名一致
]
}
})
}
上面注释2处提到了visTypes中配置的是js的路径,那个xx-myplugin.js是不是就在插件目录下呢?并不是,我们需要在插件目录下新建一个public目录(这也是npm模块的开发的规范),在public目录下新建这个xx-myplugin.js
xx-myplugin.js
import visTypes from 'ui/registry/vis_types';
import lineVisTypeProvider from 'plugins/xx-myplugin/line'; //这个demo.js中的内容其实可以直接写到当前文件中,但是这样写是为了以后扩展,并且符合node开发模块化的思想
// import demoVisTypeProvider from 'plugins/xx-myplugin/demo';
visTypes.register(lineVisTypeProvider); // 利用kibana注册新建的这个视图类型的插件。
//visTypes.register(demoVisTypeProvider);
接下来在public目录下新建line.js(这个js是最主要的),在看这个文件之前,可以先到最下,看一下页面,可以直观一些
line.js
import VisVisTypeProvider from 'ui/vis/vis_type';
import VislibVisTypeVislibVisTypeProvider from 'ui/vislib_vis_type/vislib_vis_type';
import VisSchemasProvider from 'ui/vis/schemas';
import pointSeriesTemplate from 'plugins/xx-myplugin/editors/point_series.html';
//import xxTemplate from 'plugins/xx-myplugin/xxTemplate.html';
import image from './images/icon-line.svg';
export default function PointSeriesVisType(Private) {
const VisType = Private(VisVisTypeProvider);
const VislibVisType = Private(VislibVisTypeVislibVisTypeProvider);
const Schemas = Private(VisSchemasProvider);
// 这个js模块,返回视图类型插件的一个实例,这里面的配置相当丰富,按照本篇博客的代码可以实现一个空的视图类型的插件
// 下面的配置中,params和schemas配置的是下图中1和2的位置,而point_series.html配置的则是3的位置。
// 具体各个参数是什么意思,可以试着自己摸索一下~因为工作时间有限,并没有全部去了解。
return new VislibVisType({
name: 'echarts line',
title: 'Echarts Line',
image,
description: 'Plugin of Echarts Line',
category: VisType.CATEGORY.OTHER,
requiresSearch: false,
// template: xxTemplate, //如果这里新建了模板,可以在最下面的图中4的位置加上自己的内容,比如替换掉原来的图标,转而用echart来呈现等等
params: {
defaults: {
grid: {
categoryLines: false,
style: {
color: '#fff'
}
},
categoryAxes: [
{
id: 'CategoryAxis-1',
type: 'category',
position: 'bottom',
show: true,
style: {
},
scale: {
type: 'linear'
},
labels: {
show: true,
truncate: 100
},
title: {}
}
],
valueAxes: [
{
id: 'ValueAxis-1',
name: 'LeftAxis-1',
type: 'value',
position: 'left',
show: true,
style: {
},
scale: {
type: 'linear',
mode: 'normal'
},
labels: {
show: true,
rotate: 0,
filter: false,
truncate: 100
},
title: {}
}
],
seriesParams: [],
addTooltip: true,
addLegend: true,
legendPosition: 'right',
showCircles: true,
interpolate: 'linear',
scale: 'linear',
drawLinesBetweenPoints: true,
radiusRatio: 9,
times: [],
addTimeMarker: false,
defaultYExtents: false,
setYExtents: false
},
positions: ['top', 'left', 'right', 'bottom'],
chartTypes: [{
value: 'line',
text: 'line'
}, {
value: 'area',
text: 'area'
}, {
value: 'histogram',
text: 'bar'
}],
axisModes: ['normal', 'percentage', 'wiggle', 'silhouette'],
scaleTypes: ['linear', 'log', 'square root'],
chartModes: ['normal', 'stacked'],
interpolationModes: [{
value: 'linear',
text: 'straight',
}, {
value: 'cardinal',
text: 'smoothed',
}, {
value: 'step-after',
text: 'stepped',
}],
editor: pointSeriesTemplate,
optionTabs: [
{
name: 'advanced',
title: 'Metrics & Axes',
editor: '' +
' '
},
{ name: 'options', title: 'Panel Settings', editor: pointSeriesTemplate },
],
},
schemas: new Schemas([
{
group: 'metrics',
name: 'metric',
title: 'Y-Axis',
min: 1,
defaults: [
{ schema: 'metric', type: 'count' }
]
},
{
group: 'metrics',
name: 'radius',
title: 'Dot Size',
min: 0,
max: 1,
aggFilter: ['count', 'avg', 'sum', 'min', 'max', 'cardinality', 'top_hits']
},
{
group: 'buckets',
name: 'segment',
title: 'X-Axis',
min: 0,
max: 1,
aggFilter: '!geohash_grid'
},
{
group: 'buckets',
name: 'group',
title: 'Split Series',
min: 0,
max: 1,
aggFilter: '!geohash_grid'
},
{
group: 'buckets',
name: 'split',
title: 'Split Chart',
min: 0,
max: 1,
aggFilter: '!geohash_grid'
}
])
});
}
插件目录下新建editors,在editors中新建point_series.html文件
插件目录下新建images,在images中存放图片
editors/point_series.html
<div>
-- Global settings -->
<div class="kuiSideBarSection">
<div class="kuiSideBarSectionTitle">
<div class="kuiSideBarSectionTitle__text">
Settings
div>
div>
<div class="kuiSideBarFormRow">
<div class="kuiSideBarFormRow__control">
div>
div>
<div class="kuiSideBarFormRow">
<div class="kuiSideBarFormRow__control">
class="kuiCheckBox" id="showCursor" type="checkbox" ng-model="vis.params.addTooltip">
div>
div>
<div class="kuiSideBarFormRow" ng-show="vis.hasSchemaAgg('segment', 'date_histogram')">
<div class="kuiSideBarFormRow__control">
class="kuiCheckBox" id="currentTimeMarker" type="checkbox"
ng-model="vis.params.addTimeMarker"
ng-checked="vis.params.addTimeMarker"
>
div>
div>
<div class="kuiSideBarFormRow" ng-show="!vis.hasSchemaAgg('segment', 'date_histogram')">
<div class="kuiSideBarFormRow__control">
class="kuiCheckBox" id="orderBuckets" type="checkbox" ng-model="vis.params.orderBucketsBySum">
div>
div>
div>
div>
至此,一个简单的插件就已经开发完成了,这时候 npm start 启动kibana,点击Visualize,就可以看到新建的试图类型啦~!此外就像上面说过的,还可以新建一个app类型的插件,只需在一开始的index.js中替换掉visTypes部分:
uiExports: {
// Register the app component of our plugin to uiExports
app: {
// The tite of the app(will be shown to the user)
title: 'Indices',
// An description of the application.
description: 'An awesome Kibana plugin',
// The require reference to the Javascript file for this application.
main: 'plugins/chebada-myplugin/app',
// The require reference to the icon of the app
icon: 'plugins/chebada-myplugin/icon.svg'
}
},
忍不住吐槽一下,kibana的文档虽然写的很多详细,但是对于插件开发来说实在是有点困难。这次的开发由于我本身是做javaweb开发的,对前端只停留在简单的jquery,所以这次学习过程还是比较痛苦的,花费了很多的时间,包括了解ELK开始,到学习nodejs,还有npm,curl等等知识,还需要了解一点less,angular的知识,整个学习成本还是非常高的。
最后在这里贴一些资料(都可以在官网上找到)
kibana插件开发官网提供的博客: https://www.timroes.de/2015/12/02/writing-kibana-4-plugins-basics/
youtube上插件开发环境搭建的视频: https://www.youtube.com/watch?v=FZEKhPz7SPQ