<1>推薦一個網址:http://blog.leanote.com/post/nixon/%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91
Grafana Panel Plugin 開發
Grafana 插件開發
上一次分享提到過Grafana的插件氛圍三種類型(Panel, Datasource, App),這一次主要記錄Panel類型的插件開發.
0. 起手式
根據官網的描述, 插件包一般放在
/var/lib/grafana/plugins
或者
data/plugins
(相對Grafana源碼目錄). 前者是生産環境, 後者是開發環境. 是以我們這一次選擇後者, 畢竟一會兒要從源碼修改Grafana的前端代碼, 然後啟動go語言編寫的後端.
基礎知識準備:
angularjs, systemjs, typescript, jquery, echarts, grunt, nodejs and golang.
1. 目錄結構
先把官網上說明需要的檔案目錄加上.
-
johnnyb-awesome-datasource
-
|-- dist # 存放編譯打包後的插件代碼
-
|-- spec # 測試代碼
-
| |-- datasource_spec.js
-
| |-- query_ctrl_spec.js
-
| |-- test-main.js
-
|-- src # 插件源碼
-
| |-- img # 需要的圖檔, 比如LOGO, 可選
-
| | |-- logo.svg
-
| |-- partials # 界面檔案, 可選
-
| | |-- annotations.editor.html
-
| | |-- config.html
-
| | |-- query.editor.html
-
| |-- datasource.js
-
| |-- module.js # 唯一入口檔案
-
| |-- plugin.json # 插件描述檔案
-
| |-- query_ctrl.js
-
|-- Gruntfile.js # Grunt任務
-
|-- LICENSE
-
|-- package.json
-
|-- README.md # 這也會顯示在插件說明中
README.md: The only difference from how GitHub renders markdown is that html is not allowed.
實際上并不需要嚴格按照上述目錄存放檔案, 關鍵點在于: 插件目錄下需要有
src/
,
dist/
.
src/
中需要有
module.js
和
plugin.json
. 其他開心就好.
是以實際上, 我的目錄結構是這樣:
-
practice-panel
-
|-- dist # 存放編譯打包後的插件代碼
-
|-- src # 插件源碼
-
| |-- partials # 界面檔案, 可選
-
| | |-- module.html
-
| | |-- module.editor.html
-
| |-- module.js # 唯一入口檔案
-
| |-- plugin.json # 插件描述檔案
-
| |-- controller.js # 分離出來的Controller, 也可全部放在module.js裡面
-
|-- Gruntfile.js # Grunt任務描述
-
|-- package.json
-
|-- README.md
2. 插件描述檔案(.json)
然後來認識一個檔案, 這個檔案是用來描述插件的. 包括插件的唯一辨別, 名字, 作者, 版本等等. 全文是這樣的:
-
{
-
"id": "",
-
"type": "",
-
"name": "",
-
"info": {
-
"description": "",
-
"author": {
-
"name": "",
-
"url": ""
-
},
-
"keywords": [],
-
"logos": [
-
"small": "",
-
"large": ""
-
],
-
"version": ""
-
},
-
"dependencies": {
-
"grafanaVersion": "",
-
"plugins": []
-
}
-
}
具體的含義, 參考附錄中的官方說明連結. 本次的内容參考源碼檔案.
3. 入口檔案
這基本上就是約定, Grafana會從插件包的
dist/
第一個讀取的檔案就是
module.js
檔案. 不管用什麼語言編寫, 最終都要保證
dist/module.js
可讀.
4. 可用依賴資源
Grafana的前端子產品化方案采用的Systemjs. 如果熟悉任何一種子產品化方案都行, 這裡的影響并不是很大, 除了需要添加依賴資源. 在插件開發過程中, 開發者是無法添加任何依賴資源的. 因為插件是作為Grafana前端整體的一部分在運作. 所有的資源配置都寫在Grafana源碼目錄的
public/app/system.conf.js
檔案中, 資源都放在
public/vendor/
目錄中.
預設可用資源如下:
-
virtual-scroll
-
mousetrap
-
remarkable
-
tether
-
eventemitter3
-
tether-drop
-
moment
-
jquery
-
lodash-src
-
lodash
-
angular
-
bootstrap
-
angular-route
-
angular-sanitize
-
angular-ui
-
angular-strap
-
angular-dragdrop
-
angular-bindonce
-
spectrum
-
bootstrap-tagsinput
-
jquery.flot
-
jquery.flot.pie
-
jquery.flot.selection
-
jquery.flot.stack
-
jquery.flot.stackpercent
-
jquery.flot.time
-
jquery.flot.crosshair
-
jquery.flot.fillbelow
-
jquery.flot.gauge
-
d3
-
jquery.flot.dashes
是的, 像D3, jquery, moment, lodash這些非常有用的圖表基礎庫都有了, 甚至很貼心的連AngularJS全家桶都有. 美中不足的是AngularJS版本偏低:
1.6.1
.
但這一次練習, 我們要用echarts來建構圖表. 很遺憾, Grafana沒有這項資源. 是以總共需要修改二個地方.
- 将
放入echarts.min.js
目錄public/vendor/
- 修改
檔案, 在public/app/system.conf.js
節點中添加代碼:paths
上下文看起來像這樣:'echarts': 'vendor/echarts.min.js',
-
'tether-drop': 'vendor/npm/tether-drop/dist/js/drop.js',
-
'moment': 'vendor/moment.js',
-
'echarts': 'vendor/echarts.min.js',
-
'jquery': 'vendor/jquery/dist/jquery.js',
-
5. 開始編碼
MetricsPanelCtrl類
準備工作基本完成了, 但是編碼之前, 需要認識一個類:
MetricsPanelCtrl
. 這個類的源碼檔案位于Grafana源碼目錄下
public/app/features/panel/metrics_panel_ctrl.ts
.
MetricsPanelCtrl
類繼承自
PanelCtrl
類, 是我們完成本次Panel類型插件開發必須要用到的. 它主要是解決了以下三個問題:
- 在進入編輯狀态後, 使metrics Tab成為預設面闆.
-
constructor($scope, $injector) {
-
super($scope, $injector);
-
// make metrics tab the default
-
this.editorTabIndex = 1;
-
// ... other code
-
}
-
- 訂閱事件:
-
this.events.on('refresh', this.onMetricsPanelRefresh.bind(this));
-
this.events.on('init-edit-mode', this.onInitMetricsPanelEditMode.bind(this));
-
this.events.on('panel-teardown', this.onPanelTearDown.bind(this));
-
- 提供設定資料源後的響應:
-
setDatasource(datasource) {
-
// ... other code
-
this.panel.datasource = datasource.value;
-
this.datasourceName = datasource.name;
-
this.datasource = null;
-
this.refresh();
-
}
-
插件事件
因為文檔中并無特别描述, 是以以下是已知的插件支援的事件:
-
can be used to response when refresh button was clicked.refresh
-
can be used to add tabs when editing a panelinit-edit-mode
-
can be used for clean uppanel-teardown
-
is an event in that is triggered on data refresh and can be hooked intodata-received
-
is an event triggered to load data when in snapshot mode.data-snapshot-load
-
is used to handle errors on dashboard refresh.data-error
編寫一個controller.js
Controller類必須實作link方法或者init方法. 這是Grafana的插件機制決定的. 與此同時, Controller的執行個體對象也将作為AngularJS元件的上下文對象存在.
-
// app/core/directives/plugin_component.ts
-
function getPluginComponentDirective(options) {
-
// other code
-
return function() {
-
return {
-
// other code
-
link: (scope, elem, attrs, ctrl) => {
-
if (ctrl.link) {
-
ctrl.link(scope, elem, attrs, ctrl);
-
}
-
if (ctrl.init) {
-
ctrl.init();
-
}
-
}
-
}
-
}
-
}
而這個函數是作為AngularJS的指令處理函數存在的. 是以我們的controller.js看起來至少應該是這樣的:
-
import { MetricsPanelCtrl } from 'app/plugins/sdk';
-
export class Controller extends MetricsPanelCtrl {
-
constructor() {
-
// initialize
-
// subscribe events
-
}
-
link(scope, elem, attrs, ctrl) {
-
// impelement linking
-
}
-
}
完整實作細節請參考源碼檔案.
是的, 此時還沒有界面. 在編寫界面之前, 我們先了解一下插件的二種狀态.
6. 插件狀态
插件有二種狀态, 一種是隻讀狀态, 一種是編輯狀态. 隻讀狀态就是打開一個dashboard時各個Panel表現的狀态. 編輯狀态是點選”Edit”之後, 進入的可以影響資料源的狀态. 我們需要分别為這二種狀态編寫頁面.
完整實作細節參考源碼檔案.
7. HTML + CSS
Grafana提供一些布局樣式和外觀樣式. 非常不幸, 我沒有在官方文檔上找到對應的樣式說明. 不過幸運的是, 插件目前隻有三種, 而且三種都提供了Example代碼. 分别是:
https://github.com/grafana/piechart-panel
https://github.com/grafana/simple-json-datasource
https://github.com/grafana/kubernetes-app
這一次練習則是copy的piechart-panel的編輯頁面. 用起來有點類似bootstrap的感覺.
好了, 重點來了: 在編寫完成界面後, 需要将界面和controller關聯在一起.
隻讀狀态的界面關聯方法是在controller.js中聲明完Controller類後, 添加靜态屬性字段:
-
Controller.templateUrl = './partials/module.html';
編輯狀态的界面則需要在
init-edit-mode
事件處理函數中注冊:
-
onInitEditMode() {
-
this.addEditorTab('Options', 'public/plugins/practice-panel/partials/module.editor.html', 2);
-
}
完整實作細節參考源碼檔案.
8. 編譯打包
編譯Grafana官方使用的是Grunt, 實際上隻要按照目錄結構來, 用什麼打包工具并不重要.
grunt
一個練習插件就這麼完成了.
9. 總結
一種資料的四種表現形式. 其中右下角紅色的柱狀圖即本次練習插件.
圖表資料Grafana隻支援二種, 時序圖和非時序圖. 非時序圖僅傳回一個普通的資料結構:
附錄
- 官方開發指南
- 官方插件示例
- 插件描述檔案說明