写Kibana插件——自定义应用程序

Writing Kibana Plugins – Custom applications

  在阅读本教程之前,您需要阅读第1部分 - 基础知识

  本部分教程系列介绍了Kibana中定制应用程序的创建。一个应用程序是插件中的一个可能的组件,它在Kibana平台内部有自己完整的部分,可以放置任何你想要的东西。Kibana只是提供你一个链接到这个部分,你可以随你的感觉设计。其中一个很好的例子是Elastic的timelion插件。

  在本教程中,我们将构建一个非常简单的Elasticsearch状态页面应用程序。它将只列出所有索引,单击一个将带您到有关该索引的统计信息的页面。您可以在下面的动画中看到最终结果。

   在本教程中,我们将学习:

  • 如何创建应用程序插件的基本结构
  • 如何从elasticsearch 正确地与插件进行通信
  • 如何在应用程序中创建多个子页面并在它们之间进行导航

   完整插件的源代码可以在GitHub上找到。这个插件我用了很多ECMAScript 2015 Syntax。Kibana使用Webpack将您的插件文件捆绑在一起,所以您可以安全地使用ECMAScript 2015,它将被Kibana折叠到ES 5 JavaScript。

  GitHub源码:https://github.com/timroes/elasticsearch_status

New Kibana 5 design

  你可能已经注意到,上面Kibana的设计可能看起来很少见(但是)。这是Kibana 5的新UI,尚未发布。教程中的所有内容也适用于Kibana 4,我将在本教程中展示与插件开发相关的旧设计与新设计之间的差异。

Creating the basic structure

  我们已经看到了本系列前几部分的index.js。要注册一个新的应用程序组件,请使用uiExport对象中的app键,如下所示:

export default function (kibana) {
  return new kibana.Plugin({
   require: ['elasticsearch'],

    uiExports: {
      app: {
        title: 'Indices',
        description: 'An awesome Kibana plugin',
        main: 'plugins/elasticsearch_status/app',
        icon: 'plugins/elasticsearch_status/icon.svg'
      }
    }
});

  在require数组中,我们可以列出我们在模块中使用的其他模块。在这里通常的值是kibana和/或者elasticsearch。指定这些将导致Kibana在这些模块之后始终加载我们,并确保它们已成功加载。我们已经在这里指定了elasticsearch模块,因为我们稍后将在本教程中使用它来访问elasticsearch的数据。

  在uiExports对象内,您可以指定应用程序键,该键本身也是描述应用程序的对象。

  title键是此应用程序的可读名称。这是在Kibana的侧栏中显示的(或在Kibana 5之前的应用程序菜单中的图标下方)。description是对应用程序的描述。这个目前不在任何地方使用。

  main键是您的应用程序的主要javascript的必须字符串。它将始终以plugins/<your plugin id>/开头,后跟插件中public文件夹中JavaScript的名称。例如在上面的示例中,您的插件中应该有一个public / app.js文件,而package.json中的plugin id应该是elasticsearch_status。

  icon键是指此应用程序的图标。这将显示在侧面导航中的名称旁边(或应用程序菜单中的Kibana 5之前)。它又是一个将被解决的字符串,与上述相同。

  您可以为应用程序指定几个其他键,上面没有显示。你可以将hidden设置为true,如果您不希望应用程序显示在导航中(例如,Kibana状态页面是这样的应用程序)。

Creating a new Server API

  如果要从应用程序查询Elasticsearch,那么干净的解决方案将是给Kibana服务器一个新的API。你从应用程序中调用这个API,它会为你查询Elasticsearch。

  为什么不直接从您的应用程序中查询Elasticsearch?当然,您也可以使用Elasticsearch JavaScript客户端直接从您的前端查询ES。但这些调用将在用户的浏览器中执行,从而导致CORS(交叉原始资源共享)问题。干净的解决方案是使用Kibana服务器。

  因此,如上所述,我们的应用程序将需要获取所有索引的列表,并且需要检索特定索引的统计信息。我们来看看第二个界面。

  要向Kibana添加新的服务器API,有一个可以指定的init方法:

// ...
return new kibana.Plugin({
  // ...
  init(server, options) {
    // Initialization goes here
  }
});

  假如你现在还不太熟悉现代JavaScript语法,这只是编写init:function(server,options){...}的一个快捷方式。

  传递给该方法的server对象实际上是一个hapiJS服务器对象。您可以按如下方式创建新界面:

// inside your init method:
server.route({
  path: '/api/elasticsearch_status/index/{name}',
  method: 'GET',
  handler(req, reply) {
    // more to come here in the next step
    reply("Hello World");
  }
});

  这样,您可以在Kibana服务器上创建一个新的GET API。你现在可以调用/ api / elasticsearch_status / index / <some index name here>接口(也没有做任何事情)。handler 程序方法将获得两个参数:第一个是已经创建的请求。您可以从此处的请求中访问很多(例如,使用req.params.name,您将获得在URL中传递的索引的名称)。第二个参数是回复函数。您必须调用此函数并将其应该返回的数据传递给调用此API的客户端。

  有关完整的文档,请查看路由方法的官方hapi文档

Querying Elasticsearch

  现在我们需要某种方式实际上从处理程序方法中查询Elasticsearch以检索关于索引的数据。有一个实用方法来调用Elasticsearch,我们可以使用。这个方法也是我们在index.js中对elasticsearch 模块的要求的原因。以下代码将进入我们API的处理函数:

server.plugins.elasticsearch.callWithRequest(req, 'cluster.state', {
  metric: 'metadata',
  index: req.params.name
}).then(function (response) {
  reply(response.metadata.indices[req.params.name]);
});

  我们需要将API中的请求作为第一个参数传递给callWithRequest方法。该方法,例如负责在调用Kibana服务器和调用Elasticsearch之间传递身份验证。第二个参数是从我们想要调用的Elasticsearch JavaScript客户端函数的名称 - 在我们的例子中,我们要调用cluster.state()方法,并且我们要将索引(从请求参数读出)传递给方法。

  该方法返回一个将通过Elasticsearch的响应解决的承诺。在解析函数中,我们将从响应(在我们的例子中为索引统计)提取我们需要的数据,并返回它(通过回复方法)。

  如果您正在开发Kibana 5.2,在本博客中概述的callWithRequest的使用会有轻微的变化。

  这样我们创建了我们的第一个Kibana服务器API,现在可以调用它了。如果您注意GitHub的源代码,您将注意到,我提取API以生成另一个模块,并且从init方法中调用此方法。我建议这样做,以保持你的代码可读 - 如果你有很多创建的API,你甚至可能想要在多个模块中使用它们。

  第二个服务器API(用于获取所有索引的列表)可以在源代码中找到。我不会在这篇博文中详细介绍,因为我们已经涵盖了所有主题,你可以自己写。

Creating the frontend

  最后但并非最不重要的是,我们应该为应用程序创建实际的前端。我们之前已经将index.js中的特定app.js注册为主文件。现在是创建它的时间了,并填写一些内容。

import 'ui/autoload/styles';
import './less/main.less';

  如果您使用Kibana 5,则第一行很重要,您应该始终将其放在应用程序插件中。这将使Kibana加载其通常具有的所有样式。如果您不导入(或要求,如果您喜欢ES5语法)此模块,您的应用程序周围的Kibana框架将在用户进入应用程序时看起来像坏的。如果您使用Kibana 4,此文件不存在,您无法导入(这让我们回到第一篇文章中的巨大警告,关于缺少稳定的公共API)。

  第二行是可选的,并显示如何为您的应用程序插入自己的LESS样式。您只需导入您的LESS文件。您也可以使用SASS替代。我建议使用相对路径,这些文件也在您的公共文件夹,所以你不需要一遍又一遍重复你的插件ID。

Creating routing options

  Kibana使用AngularJS'ngRouter在页面之间进行路由。如果您的应用程序想要使用路由,则必须明确启用它,并在app.js文件中配置一些路由:

import uiRoutes from 'ui/routes';

import overviewTemplate from './templates/index.html';
import detailTemplate from './templates/detail.html';

uiRoutes.enable();
uiRoutes
.when('/', {
  template: overviewTemplate,
  controller: 'elasticsearchStatusController',
  controllerAs: 'ctrl'
})
.when('/index/:name', {
  template: detailTemplate,
  controller: 'elasticsearchDetailController',
  controllerAs: 'ctrl'
});

  如果需要使用路由,则调用uiRoutes.enable()是必需的。之后,你可以使用when和其它的调用,就像你从$ routeProvider中使用的一样。在这种情况下,我们要配置两条路由:一个是基本路径,一个是/ index /:name路径,该名称是索引名称的占位符。可以通过使用上面的import语句将实际的html文件(在我们的例子中都放置在模板文件中)设置为两个路由的模板。我们还使用两个控制器,我们还没有写。

  所以我们仅仅使用全局Angular模块注册正在运行的Kibana来编写它们:

import uiModules from 'ui/modules';

uiModules
.get('app/elasticsearch_status')
.controller('elasticsearchStatusController', function ($http) {
  $http.get('../api/elasticsearch_status/indices').then((response) => {
    this.indices = response.data;
  });
});

  uiModules是Kibana的核心服务,负责应用中的所有Angular模块。如果要获取或创建一个,请使用其get方法。第一个参数是要获取或创建的模块的名称。该服务将负责返回模块,如果已经存在或者如果没有则创建它。第二个参数可以是一个模块数组,我们的模块依赖。如果模块已经存在,那么这些模块在返回之前将被添加到模块的依赖关系列表中。这些模块不存在,只需将它们添加到新创建的模块中即可。

  正如你所看,此行为与angular.module方法不同,您可以在指定依赖关系时创建模块,并在不传递第二个参数时获取一个模块。使用这种服务,Kibana也负责加载我们的Angular模块。

  上面的控制器本身就是使用$ http服务从我们的接口获取索引列表,并将其存储在控制器中。

  最后一个缺失的部分是在templates / index.html中的模板:

<div class="container">
  <div class="row">
    <div class="col-12-sm">
      <h1>Elasticsearch Status</h1>
      <ul class="indexList">
        <li ng-repeat="index in ctrl.indices">
          <a href="#/index/{{index}}">{{ index }}</a>
        </li>
      </ul>
    </div>
  </div>
</div>

  我们的教程中的HTML保持相当简单。我们只是使用ng-repeat来迭代所有的索引,我们从API中检索并链接到它们。此外,我们使用一些Bootstrap的CSS类来设计我们的内容。详细页面的HTML可以在GitHub的源代码中找到。

  在Kibana 5开始,Kibana将只给你旁边的导航,如上图所示。一旦你切换到你的插件,没有标题栏或任何东西。如果你想要这种样式,你将不得不在你的应用程序中创建它。在Kibana 5之前,您仍然可以获得标题栏,并可以通过服务进行修改:

import chrome from 'ui/chrome';

chrome
.setNavBackground('#FF00FF') // Turns the navbar in beautiful pink
.setBrand({
  logo: '<CSS background property value for the logo>',
  smallLogo: '<CSS background property value a smaller version>'
});

  有更多的方法来创建制表符等。但是当您开发应用程序组件时,请记住,这些内容在Kibana 5(截至今天)中已经消失,您的应用程序可能无法使用,因为您缺少依赖的选项卡导航。

What’s next?

   通过这个非常简单的应用程序组件,我们覆盖了您构建自己的awesome应用程序组件插件所需的大量API。所以现在由你来构建下一个timelion。

 

原文地址:https://www.timroes.de/2016/02/21/writing-kibana-plugins-custom-applications/

posted @ 2017-08-08 08:36 流浪三毛 阅读(...) 评论(...) 编辑 收藏