天天看點

Grafana 的插件開發Grafana 插件開發

<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. 目錄結構

先把官網上說明需要的檔案目錄加上.

  1. johnnyb-awesome-datasource

  2. |-- dist # 存放編譯打包後的插件代碼

  3. |-- spec # 測試代碼

  4. | |-- datasource_spec.js

  5. | |-- query_ctrl_spec.js

  6. | |-- test-main.js

  7. |-- src # 插件源碼

  8. | |-- img # 需要的圖檔, 比如LOGO, 可選

  9. | | |-- logo.svg

  10. | |-- partials # 界面檔案, 可選

  11. | | |-- annotations.editor.html

  12. | | |-- config.html

  13. | | |-- query.editor.html

  14. | |-- datasource.js

  15. | |-- module.js # 唯一入口檔案

  16. | |-- plugin.json # 插件描述檔案

  17. | |-- query_ctrl.js

  18. |-- Gruntfile.js # Grunt任務

  19. |-- LICENSE

  20. |-- package.json

  21. |-- 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

. 其他開心就好.

是以實際上, 我的目錄結構是這樣:

  1. practice-panel

  2. |-- dist # 存放編譯打包後的插件代碼

  3. |-- src # 插件源碼

  4. | |-- partials # 界面檔案, 可選

  5. | | |-- module.html

  6. | | |-- module.editor.html

  7. | |-- module.js # 唯一入口檔案

  8. | |-- plugin.json # 插件描述檔案

  9. | |-- controller.js # 分離出來的Controller, 也可全部放在module.js裡面

  10. |-- Gruntfile.js # Grunt任務描述

  11. |-- package.json

  12. |-- README.md

2. 插件描述檔案(.json)

然後來認識一個檔案, 這個檔案是用來描述插件的. 包括插件的唯一辨別, 名字, 作者, 版本等等. 全文是這樣的:

  1. {

  2. "id": "",

  3. "type": "",

  4. "name": "",

  5. "info": {

  6. "description": "",

  7. "author": {

  8. "name": "",

  9. "url": ""

  10. },

  11. "keywords": [],

  12. "logos": [

  13. "small": "",

  14. "large": ""

  15. ],

  16. "version": ""

  17. },

  18. "dependencies": {

  19. "grafanaVersion": "",

  20. "plugins": []

  21. }

  22. }

具體的含義, 參考附錄中的官方說明連結. 本次的内容參考源碼檔案.

3. 入口檔案

這基本上就是約定, Grafana會從插件包的

dist/

第一個讀取的檔案就是

module.js

檔案. 不管用什麼語言編寫, 最終都要保證

dist/module.js

可讀.

4. 可用依賴資源

Grafana的前端子產品化方案采用的Systemjs. 如果熟悉任何一種子產品化方案都行, 這裡的影響并不是很大, 除了需要添加依賴資源. 在插件開發過程中, 開發者是無法添加任何依賴資源的. 因為插件是作為Grafana前端整體的一部分在運作. 所有的資源配置都寫在Grafana源碼目錄的

public/app/system.conf.js

檔案中, 資源都放在

public/vendor/

目錄中.

預設可用資源如下:

  1. virtual-scroll

  2. mousetrap

  3. remarkable

  4. tether

  5. eventemitter3

  6. tether-drop

  7. moment

  8. jquery

  9. lodash-src

  10. lodash

  11. angular

  12. bootstrap

  13. angular-route

  14. angular-sanitize

  15. angular-ui

  16. angular-strap

  17. angular-dragdrop

  18. angular-bindonce

  19. spectrum

  20. bootstrap-tagsinput

  21. jquery.flot

  22. jquery.flot.pie

  23. jquery.flot.selection

  24. jquery.flot.stack

  25. jquery.flot.stackpercent

  26. jquery.flot.time

  27. jquery.flot.crosshair

  28. jquery.flot.fillbelow

  29. jquery.flot.gauge

  30. d3

  31. 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',

     上下文看起來像這樣:
    1. 'tether-drop': 'vendor/npm/tether-drop/dist/js/drop.js',

    2. 'moment': 'vendor/moment.js',

    3. 'echarts': 'vendor/echarts.min.js',

    4. 'jquery': 'vendor/jquery/dist/jquery.js',

5. 開始編碼

MetricsPanelCtrl類

準備工作基本完成了, 但是編碼之前, 需要認識一個類: 

MetricsPanelCtrl

. 這個類的源碼檔案位于Grafana源碼目錄下

public/app/features/panel/metrics_panel_ctrl.ts

MetricsPanelCtrl

類繼承自

PanelCtrl

類, 是我們完成本次Panel類型插件開發必須要用到的. 它主要是解決了以下三個問題:

  1. 在進入編輯狀态後, 使metrics Tab成為預設面闆.
    1. constructor($scope, $injector) {

    2. super($scope, $injector);

    3. // make metrics tab the default

    4. this.editorTabIndex = 1;

    5. // ... other code

    6. }

  2. 訂閱事件:
    1. this.events.on('refresh', this.onMetricsPanelRefresh.bind(this));

    2. this.events.on('init-edit-mode', this.onInitMetricsPanelEditMode.bind(this));

    3. this.events.on('panel-teardown', this.onPanelTearDown.bind(this));

  3. 提供設定資料源後的響應:
    1. setDatasource(datasource) {

    2. // ... other code

    3. this.panel.datasource = datasource.value;

    4. this.datasourceName = datasource.name;

    5. this.datasource = null;

    6. this.refresh();

    7. }

插件事件

因為文檔中并無特别描述, 是以以下是已知的插件支援的事件:

  • refresh

     can be used to response when refresh button was clicked.
  • init-edit-mode

     can be used to add tabs when editing a panel
  • panel-teardown

     can be used for clean up
  • data-received

     is an event in that is triggered on data refresh and can be hooked into
  • data-snapshot-load

     is an event triggered to load data when in snapshot mode.
  • data-error

     is used to handle errors on dashboard refresh.

編寫一個controller.js

Controller類必須實作link方法或者init方法. 這是Grafana的插件機制決定的. 與此同時, Controller的執行個體對象也将作為AngularJS元件的上下文對象存在.

  1. // app/core/directives/plugin_component.ts

  2. function getPluginComponentDirective(options) {

  3. // other code

  4. return function() {

  5. return {

  6. // other code

  7. link: (scope, elem, attrs, ctrl) => {

  8. if (ctrl.link) {

  9. ctrl.link(scope, elem, attrs, ctrl);

  10. }

  11. if (ctrl.init) {

  12. ctrl.init();

  13. }

  14. }

  15. }

  16. }

  17. }

而這個函數是作為AngularJS的指令處理函數存在的. 是以我們的controller.js看起來至少應該是這樣的:

  1. import { MetricsPanelCtrl } from 'app/plugins/sdk';

  2. export class Controller extends MetricsPanelCtrl {

  3. constructor() {

  4. // initialize

  5. // subscribe events

  6. }

  7. link(scope, elem, attrs, ctrl) {

  8. // impelement linking

  9. }

  10. }

完整實作細節請參考源碼檔案.

是的, 此時還沒有界面. 在編寫界面之前, 我們先了解一下插件的二種狀态.

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類後, 添加靜态屬性字段:

  1. Controller.templateUrl = './partials/module.html';

編輯狀态的界面則需要在

init-edit-mode

事件處理函數中注冊:

  1. onInitEditMode() {

  2. this.addEditorTab('Options', 'public/plugins/practice-panel/partials/module.editor.html', 2);

  3. }

完整實作細節參考源碼檔案.

8. 編譯打包

編譯Grafana官方使用的是Grunt, 實際上隻要按照目錄結構來, 用什麼打包工具并不重要.

grunt

一個練習插件就這麼完成了.

9. 總結

Grafana 的插件開發Grafana 插件開發

一種資料的四種表現形式. 其中右下角紅色的柱狀圖即本次練習插件.

圖表資料Grafana隻支援二種, 時序圖和非時序圖. 非時序圖僅傳回一個普通的資料結構:

Grafana 的插件開發Grafana 插件開發
Grafana 的插件開發Grafana 插件開發

附錄

  • 官方開發指南
  • 官方插件示例
  • 插件描述檔案說明