譯 Tim Rose 的kibana外掛教程-消費資料的視覺化外掛

atxiaoxian發表於2019-05-04

kibana官方沒有外掛的開發教程,Tim Rose的教程寫的十分詳盡,也是官方推薦的。由於這個系列的教程是英文版的,且基於kibana4,近日需要做kibana的開發,硬啃下這些教程之後,且根據kibana6.3.2版本做了些改進,記錄下來,留下個爪。由於本人水平有限,錯漏的地方歡迎大家指出。

原文連結:www.timroes.de/writing-kib…

原文標題:Writing Kibana 4 Plugins – Visualizations using Data

簡單視覺化外掛

你需要先閱讀簡單視覺化外掛,才能閱讀本文。

在前面的章節(在閱讀本文前,一定要先閱讀),你已經學會了如何建立一個簡單的視覺化外掛,但並不從elasticsearch獲取 任何資料。在這一章,我們將寫另外一個外掛,像其他外掛一樣,需要消費elasticsearch的資料。

我們將寫一個很簡單的標籤雲外掛,我們會把桶的名稱作為標籤展示出來,度量聚合資料的大小將決定這個標籤字型的大小。如果你對桶和度量還不是很熟悉,可以參考一下我這篇k4v教程。

這篇教程的原始碼可以在GitHub找到

在整個教程當中,我會用tr-k4p-tagcloud這個名字。你需要用一個全域性唯一的名字來替代他。

標籤雲視覺化外掛

首先我們要想想,我們需要以何種方式來視覺化何種資料,這意味著,我們需要考慮,我們的視化外掛,會呈現什麼桶和什麼度量的資料。

我們會盡量保持外掛的簡單性,所以只採用了一個桶和一個度量。桶聚合決定了什麼標籤會被展示出來(一個桶就是一個標籤),維度聚合決定了標籤的大小,即聚合的度量結果越高,所顯示的標籤就會越大。

決定用多少維度和桶的聚合資料是很重要的,如果你能內嵌聚合,你一會還要再外掛中定義。

定義和註冊外掛

跟前面章節的一樣,第一步是建立index.jspackage.json,和註冊一個簡單的視覺化provider。下面我們只展示主要的provider程式碼片段(你可以在public/tagclouc.js中找到):

function TagcloudProvider(Private) {
  var TemplateVisType = Private(require('ui/template_vis_type/TemplateVisType'));

  return new TemplateVisType({
    name: 'trTagcloud', // The internal id of the visualization (must be unique)
    title: 'Tagcloud', // The title of the visualization, shown to the user
    description: 'Tagcloud visualization', // The description of this vis
    icon: 'fa-cloud', // The font awesome icon of this visualization
    template: require('plugins/tr-k4p-tagcloud/tagcloud.html')
  });
}

require('ui/registry/vis_types').register(TagcloudProvider);
複製程式碼

跟前面的教程相比較,這裡又兩個不一樣的地方:

  • 我們在定義模組的時候,丟棄了define()函式(AMD 模組定義)。就像前面說的,這個是非必要的,由於我們使用了webpack作為打包工具,這也意味著你不再需要在模組中返回provider函式。
  • 我們去除了requiresSearch: false選項,我們的tagcloud外掛需要從elasticsearch中獲取資料,所以我們的外掛就可以像其他使用資料的外掛一樣來進行查詢。由於requiresSearch: true是預設選項,所以我們去掉這個選項就行了。

現在我們需要來建立public/tagcloud.html,現在可以讓他置空。第一部分的程式碼可以在在github的tag0.1.0找到。

定義資料結構

一個需要使用聚合資料的外掛,你需要明確的指出,你的外掛需要什麼樣的聚合資料,或者說,什麼樣的資料是被我的外掛接受的。這所謂的資料結構需要在外掛定義的時候給出,我們來修改一下外掛定義:

function TagcloudProvider(Private) {
  var TemplateVisType = /* ... */;

  // Include the Schemas class, which will be used to define schemas
  var Schemas = Private(require('ui/Vis/Schemas'));

  return new TemplateVisType({
    /* every attribute shown above */,
    schemas: new Schemas([
      {
        group: 'metrics',
        name: 'tagsize',
        title: 'Tagsize',
        min: 1,
        max: 1,
        aggFilter: ['count', 'avg', 'sum', 'min', 'max', 'cardinality', 'std_dev']
      },
      {
        group: 'buckets',
        name: 'tags',
        title: 'Tags',
        min: 1,
        max: 1,
        aggFilter: '!geohash_grid'
      }
    ])
  });
}
複製程式碼

為了定義資料結構,我們需要例項化一個Schemas物件,我們需要傳一個陣列作為建構函式的引數,陣列的每個物件定義了一個你所需要的聚合資料,每個聚合物件有如下屬性:

  • group metrics或者buckets二選一,定義了這個物件所屬的聚合資料型別
  • name 這個聚合資料的名字(id)。以後你可以用這個屬性來引用不同的聚合資料
  • title 這個是展示給使用者看的,當他新增一個單項聚合的時候,這個會告訴他,這個資料將會被怎麼使用,再本例中,桶資料會被用作標籤,維度資料則影響的是標籤的字型大小
  • min/max 使用者能夠新增的此聚合的範圍,例如,使用者新增一個直方圖,一個桶聚合的名稱叫“圖列”。這個是沒有限制的,即沒有最大值限制,就像使用者想要有多少個直方圖的條數,就能有多少個。在本例中,我們各種資料只允許一個聚合資料,由於我們的外掛的工作方式。
  • 過濾器 決定了哪種聚合資料是合法的。他的值是一個這裡所允許的聚合型別的陣列(下面可以看見),或者是一個所不允許的聚合型別的陣列(每一個一定要以一個下劃線為字首),在接下來的場景中,其他的聚合型別是允許的。如果這個陣列只有一個值,那你也可以把他宣告稱一個字串(正如桶聚合所示)。

這裡也還有其他的一些欄位你可以使用的,但是在本例中不會被用到。

你可以宣告的維度聚合的aggFilter型別有:svg, cardinality, count, max, median, min, percentileranks, percentiles, stddev, sum

桶聚合的aggFilter的型別有:** datehistogram, daterange, filters, geohashgrid, histogram, iprange, range, significant_terms, terms**

在github的tag0.2.0上可以找到這一步的程式碼。

編輯控制器controller

為了給我們的視覺化外掛天價一些邏輯,我們會再次需要一個angular的controller。與之前不同的是,我們會把這個controller放在一個單獨的檔案下(因為他會變得大一點),因此,我們需要在tancloud.js中新增如下程式碼:

require('plugins/tr-k4p-tagcloud/tagcloudController')

複製程式碼

現在我們需要建立一個新檔案public/tagcloudController.js,先寫上一個空白的controller:

var module = require('ui/modules').get('tr-k4p-tagcloud');
module.controller('TagcloudController', function($scope) {
  // Your logic will go here
});
複製程式碼

在模板中載入控制器(public/tagcloud.html)

<div ng-controller="TagcloudController"></div>
複製程式碼

現在外掛的模板就可以載入控制器了。

獲取資料

我們需要從angular的scope中繼承兩個變數,一個是從之前章節就知道的vis,這個變數包含了外掛的資訊,和使用者的設定資訊。另外一個變數就是esResponse,這變數儲存了Elasticsearch 的返回資料,kibana'會自動請求elasticsearch,帶上當前的query和filters,以及使用者的聚合資訊。

我們的外掛需要把返回資料和使用者的設定,通過視覺化的方式展示出來,我們可以訪問$scope.esResponse.aggregations變數來獲取我們的查詢所返回資料。我們需要獲取聚合資料中的id,為了獲取指定聚合資料的id,我們需要$scope.vis.aggs中的一些方法來找到is。

在我們的場景中,首先我們需要獲取全部的桶資料(就是所有的tag名稱),我們可以通過如下方式,來獲取tags聚合的id

$scope.vis.aggs.bySchemaName['tags'][0].id
複製程式碼

bySchemaName 物件包含一個聚合資料設定的名字(在schema中指定的)的對映,tags屬性是一個使用者輸入的全部聚合設定的陣列,由於我們已經把這個屬性的最大值和最小值都設定成了1,我們現在也只有一個物件可以獲取他的id了。我們用這個id在esResponse中查詢我們需要的資料。

我們一般是設定一個watch來監聽esResponse的變化來更新資料,現在我們來把這個tag當成一個list展示出來:

$scope.$watch('esResponse', function(resp) {
  if (!resp) {
    $scope.tags = null;
    return;
  }

  // Retrieve the id of the configured tags aggregation
  var tagsAggId = $scope.vis.aggs.bySchemaName['tags'][0].id;
  // Get the buckets of that aggregation
  var buckets = resp.aggregations[tagsAggId].buckets;
  // Transform all buckets into tag objects
  $scope.tags = buckets.map(function(bucket) {
    return {
      label: bucket.key
    };
   });
});

複製程式碼

這樣我們的$scope.tags就是一個陣列,陣列裡的物件是有一個屬性為label,值是bucket.key。相應的,我們也要修改tagcloud.html

<div ng-controller="TagcloudController">
  <span ng-repeat="tag in tags">{{tag.label}}</span>
</div>
複製程式碼

完整的程式碼(包括一些css)可以在GitHub倉庫的0.3.0

獲取維度聚合資料相對來說要簡單一點,但也遵循這同樣的步驟。首先,我們需要要拿到的桶數的維度聚合資料的一個引用:

var metricsAgg = $scope.vis.aggs.bySchemaName['tagsize'][0];
複製程式碼

注意:我們並沒有讀取id,反而讀取了整個聚合物件,同樣的我們也只是讀取了陣列中的第一個元素,因為我們只允許配置一個維度聚合。我們可以來完善剛剛的桶資料,給桶資料加上維度資料:

$scope.tags = buckets.map(function(bucket) {
  return {
    label: bucket.key,
    value: metricsAgg.getValue(bucket)
  };
});
複製程式碼

我們可以呼叫維度聚合物件上的getValue方法,引數是桶bucket,返回值就是這個桶資料想對應的維度資料了。之後我們就可以拿到一個tags的陣列,裡面的物件都是一個label和一個值。現在剩下的事情就是為我們每個tag計算一個font-size,首選需要統計tag的最大值和最小值,在收集tags陣列的時候,我們會設定一個最大的font-size和最小的font-size,然後計算出每個tag相應的字型大小,由於這個跟kibana沒有直接關聯,只是angular的controller的一些東西,就不在這裡展示了,我們會放在GitHub的0.4.0中的tagcloudController.jstagcloud.html中。

資料累加

tag雲是是一個很簡單的讀取資料的外掛。他只有一個維度資料和一個桶資料,沒有多維度,也沒有內嵌的聚合,等等。在一個更復雜的外掛中你會遇到上述的全部狀況,那我們現在就來試試處理這些複雜的資料:

  • 可以通過$scope.vis.aggs物件來獲取配置的視覺化物件,下面的bySchemaNamebySchemaGroupbyTypeName(例如,count,terms等等),這些屬性去獲取,scheme裡配置的不同name的聚合資料
  • 聚合物件的getValue方法可以獲取bucket物件的資料。

一般來說,還有很多其他的方法,來適應更加複雜的視覺化場景,(例如你可以使用聚合物件的getKey方法來獲取key或者bucket)。這些方法需要等一等,在官方的外掛開發文件中找到。閱讀原始碼,經常是最好的方法,在開發的過程中打個斷點,然後在瀏覽器的dev-tool上面觀察這些物件。

點選過濾

最後一個我們想新增的功能就是tags過濾,當使用者點選tag的時候,儀表盤上應該新增一個該tag的值的過濾器。

第一步,我們要建立一個過濾器來實現過濾服務,我們會使用Private服務(這個服務是負責為需要的模組例項化angular服務,在前面章節已經說過了),來例項化一個filter服務,controller中需要做如下修改:

module.controller('TagcloudController', function($scope, Private) {
  var filterManager = Private(require('ui/filter_manager'));
  // ...
});
複製程式碼

filter manger有一個add方法,可以呼叫來實現新增過濾器,首先我們修改HTML來當使用者點選一個標籤的時候,呼叫一個方法:

<span ng-click="filter(tag)" ng-repeat="tag in tags" ...>
複製程式碼

完整的filter方法:

$scope.filter = function(tag) {
  // Add a new filter via the filter manager
  filterManager.add(
    // The field to filter for, we can get it from the config
    $scope.vis.aggs.bySchemaName['tags'][0].params.field,
    // The value to filter for, we will read out the bucket key from the tag
    tag.label,
    // Whether the filter is negated. If you want to create a negated filter pass '-' here
    null,
    // The index pattern for the filter
    $scope.vis.indexPattern.title
  );
};
複製程式碼

這一步的完整程式碼可以在GitHub的0.5.0找到。

接下來

現在你已經可以寫一個展示來自elasticsearch資料的視覺化外掛了。寫外掛的時候,要時刻注意,什麼樣的scheme是你想展示的,要保證只允許配置你程式碼中處理了的scheme。所以,如果你允許多個bucket聚合,那你就要把每一個bucket都要在程式碼中進行處理,這樣的話,你就需要從$scope.vis.aggs.bySchemaName['foobar']獲取資料,而不是僅僅獲取第一個。

相關文章