Kibana4 可视化插件开发

上一节,我们看到了一个完整的 Kibana 插件的官方用例。一般来说,我们不太会需要自己从头到尾写一个 angular app 出来。最常见的情况,应该是在 Kibana 功能的基础上做一定的二次开发和扩展。其中,可视化效果应该是重中之重。本节,以一个红绿灯效果,演示如何开发一个 Kibana 可视化插件。

可视化开发示例 - 图1

插件目录生成

Kibana 开发组提供了一个简单的工具,辅助我们生成一个 Kibana 插件的目录结构:

  1. npm install -g yo
  2. npm install -g generator-kibana-plugin
  3. mkdir traffic_light_vis
  4. cd traffic_light_vis
  5. yo kibana-plugin

但是这个是针对完整的 app 扩展的,很多目录对于可视化插件来说,并没有用。所以,我们可以自己手动创建目录:

  1. mkdir -p traffic_light_vis/public
  2. cd traffic_light_vis
  3. touch index.js package.json
  4. cd public
  5. touch traffic_light_vis.html traffic_light_vis.js traffic_light_vis.less traffic_light_vis_controller.js traffic_light_vis_params.html

其中 index.js 内容如下:

  1. 'use strict';
  2. module.exports = function (kibana) {
  3. return new kibana.Plugin({
  4. uiExports: {
  5. visTypes: ['plugins/traffic_light_vis/traffic_light_vis']
  6. }
  7. });
  8. };

package.json 内容如下:

  1. {
  2. "name": "traffic_light_vis",
  3. "version": "0.1.0",
  4. "description": "An awesome Kibana plugin for red/yellow/green status visualize"
  5. }

这两个基础文件格式都比较固定,除了改个名就基本 OK 了。

然后我们看最关键的可视化对象定义 public/traffic_light_vis.js 内容:

  1. define(function (require) {
  2. // 加载样式表
  3. require('plugins/traffic_light_vis/traffic_light_vis.less');
  4. // 加载控制器程序
  5. require('plugins/traffic_light_vis/traffic_light_vis_controller');
  6. // 注册到 vis_types
  7. require('ui/registry/vis_types').register(TrafficLightVisProvider);
  8. function TrafficLightVisProvider(Private) {
  9. // TemplateVisType 基类,适用于基础的 metric 和数据表格式的可视化定义。实际上,Kibana4 的 metric_vis 和 table_vis 就继承自这个,
  10. // Kibana4 还有另一个基类叫 VisLibVisType,其他使用 D3.js 做可视化的,继承这个。
  11. var TemplateVisType = Private(require('ui/template_vis_type/TemplateVisType'));
  12. var Schemas = Private(require('ui/Vis/Schemas'));
  13. // 模板化的 visType 对象定义,用来配置和展示
  14. return new TemplateVisType({
  15. name: 'traffic_light',
  16. // 显示在 visualize 选择列表里的名称,描述,小图标
  17. title: 'Traffic Light',
  18. description: 'Great for one-glance status readings, the traffic light visualization expresses in green / yellow / red the position of a single value in relation to low and high thresholds.',
  19. icon: 'fa-car',
  20. // 可视化效果模板页面
  21. template: require('plugins/traffic_light_vis/traffic_light_vis.html'),
  22. params: {
  23. defaults: {
  24. fontSize: 60
  25. },
  26. // 编辑参数的页面
  27. editor: require('plugins/traffic_light_vis/traffic_light_vis_params.html')
  28. },
  29. // 在编辑页面上可以选择的 aggregation 类型。
  30. schemas: new Schemas([
  31. {
  32. group: 'metrics',
  33. name: 'metric',
  34. title: 'Metric',
  35. min: 1,
  36. defaults: [
  37. { type: 'count', schema: 'metric' }
  38. ]
  39. }
  40. ])
  41. });
  42. }
  43. // export the provider so that the visType can be required with Private()
  44. return TrafficLightVisProvider;
  45. });

然后就是可视化效果的模板页面了,traffic_light_vis.html 毫无疑问也是一个 angular 风格的:

  1. <div ng-controller="TrafficLightVisController" class="traffic-light-vis">
  2. <div class="metric-container" ng-repeat="metric in metrics">
  3. <div class="traffic-light-container" ng-style="{'width': vis.params.width+'px', 'height': (2.68 * vis.params.width)+'px' }">
  4. <div class="traffic-light">
  5. <div class="light red" ng-class="{'on': (!vis.params.invertScale && metric.value <= vis.params.redThreshold) || (vis.params.invertScale && metric.value >= vis.params.redThreshold) }"></div>
  6. <div class="light yellow" ng-class="{'on': (!vis.params.invertScale && metric.value > vis.params.redThreshold && metric.value < vis.params.greenThreshold) || (vis.params.invertScale && metric.value < vis.params.redThreshold && metric.value > vis.params.greenThreshold) }"></div>
  7. <div class="light green" ng-class="{'on': (!vis.params.invertScale && metric.value >= vis.params.greenThreshold) || (vis.params.invertScale && metric.value <= vis.params.greenThreshold) }"></div>
  8. </div>
  9. </div>
  10. <div>{{metric.label}}</div>
  11. </div>
  12. </div>

这里可以看到:

  1. 把 div 绑定到了 TrafficLightVisController 控制器上,这个也是之前在 js 里已经加载过的。
  2. 通过 ng-repeat 循环展示不同的 metric,也就是说模板渲染的时候,收到的是一个 metrics 数组。这个来源当然是在控制器里。
  3. 然后具体的数据判断,即什么灯亮什么灯灭,通过了 vis.params.* 的运算判断。这些变量当然是在编辑页面里设置的。

所以下一步看编辑页面 traffic_light_vis_params.html

  1. <div class="form-group">
  2. <label>Traffic light width - {{ vis.params.width }}px</label>
  3. <input type="range" ng-model="vis.params.width" class="form-control" min="30" max="120"/>
  4. </div>
  5. <div class="form-group">
  6. <label>Red threshold <span ng-bind-template="({{!vis.params.invertScale ? 'below':'above'}} this value will be red)"></span></label>
  7. <input type="number" ng-model="vis.params.redThreshold" class="form-control"/>
  8. </div>
  9. <div class="form-group">
  10. <label>Green threshold <span ng-bind-template="({{!vis.params.invertScale ? 'above':'below'}} this value will be green)"></span></label>
  11. <input type="number" ng-model="vis.params.greenThreshold" class="form-control"/>
  12. </div>
  13. <div class="form-group">
  14. <label>
  15. <input type="checkbox" ng-model="vis.params.invertScale">
  16. Invert scale
  17. </label>
  18. </div>

内容很简单,就是通过 ng-model 设置绑定变量,跟之前 HTML 里的联动。

最后一步,看控制器 traffic_light_vis_controller.js

  1. define(function (require) {
  2. var module = require('ui/modules').get('kibana/traffic_light_vis', ['kibana']);
  3. module.controller('TrafficLightVisController', function ($scope, Private) {
  4. var tabifyAggResponse = Private(require('ui/agg_response/tabify/tabify'));
  5. var metrics = $scope.metrics = [];
  6. $scope.processTableGroups = function (tableGroups) {
  7. tableGroups.tables.forEach(function (table) {
  8. table.columns.forEach(function (column, i) {
  9. metrics.push({
  10. label: column.title,
  11. value: table.rows[0][i]
  12. });
  13. });
  14. });
  15. };
  16. $scope.$watch('esResponse', function (resp) {
  17. if (resp) {
  18. metrics.length = 0;
  19. $scope.processTableGroups(tabifyAggResponse($scope.vis, resp));
  20. }
  21. });
  22. });
  23. });

要点在:

  1. $scope.$watch('esResponse', function(resp){}) 监听整个页面的请求响应,在有新数据过来的时候更新页面效果;
  2. agg_response/tabify/tabify 把响应结果转换成二维表格形式。

最后加上一段样式表,这里就不贴了,见:https://github.com/logzio/kibana-visualizations/blob/master/traffic_light_vis/traffic_light_vis.less

本节介绍的示例,出自 logz.io 官方博客和对应的 github 开源项目。logz.io 是基于 Kibana4.1 写的插件。我这里修正成了基于最新 Kibana4.3 的实现。