Kibana 插件 API
编辑Kibana 插件 API编辑
此功能处于技术预览阶段,可能会在未来版本中更改或删除。Elastic 将努力解决任何问题,但技术预览版中的功能不受官方 GA 功能支持 SLA 的约束。
Kibana 平台插件是朝着为所有开发者稳定 Kibana 架构迈出的重要一步。我们确保插件可以继续使用当今使用的大多数相同技术,至少从技术角度来看是这样。
插件剖析编辑
插件被定义为类,并通过一个简单的包装函数呈现给 Kibana。插件可以包含浏览器端代码、服务器端代码或两者兼而有之。浏览器中的插件和服务器上的插件之间没有架构差异。在这两个地方,您都以类似的方式描述您的插件,并且您以相同的方式与核心和其他插件交互。
名为 demo
的 Kibana 插件的基本文件结构,该插件同时具有客户端代码和服务器端代码,如下所示
plugins/ demo kibana.json [1] public index.ts [2] plugin.ts [3] server index.ts [4] plugin.ts [5]
[1] kibana.json
是一个静态清单文件,用于标识插件并指定此插件是具有服务器端代码、浏览器端代码还是两者兼而有之
{ "id": "demo", "version": "kibana", "server": true, "ui": true }
了解有关 清单文件格式 的信息。
package.json
文件与 Kibana 无关,并且 Kibana 会忽略这些文件以发现和加载插件。
[2] public/index.ts
是此插件客户端代码的入口点。它必须导出一个名为 plugin
的函数,该函数将接收 一组标准的核心功能 作为参数。它应该返回其插件类的实例以供 Kibana 加载。
import type { PluginInitializerContext } from '@kbn/core/server'; import { MyPlugin } from './plugin'; export function plugin(initializerContext: PluginInitializerContext) { return new MyPlugin(initializerContext); }
[3] public/plugin.ts
是客户端插件定义本身。从技术上讲,它不需要是一个类,甚至不需要是与入口点分离的文件,但 *Elastic 的所有插件* 都应该以这种方式保持一致。查看 第一方 Elastic 插件的所有约定。
import type { Plugin, PluginInitializerContext, CoreSetup, CoreStart } from '@kbn/core/server'; export class MyPlugin implements Plugin { constructor(initializerContext: PluginInitializerContext) {} public setup(core: CoreSetup) { // called when plugin is setting up during Kibana's startup sequence } public start(core: CoreStart) { // called after all plugins are set up } public stop() { // called when plugin is torn down during Kibana's shutdown sequence } }
[4] server/index.ts
是此插件服务器端代码的入口点。它与 客户端入口点几乎完全相同
import type { PluginInitializerContext } from '@kbn/core/server'; export async function plugin(initializerContext: PluginInitializerContext) { const { MyPlugin } = await import('./plugin'); return new MyPlugin(initializerContext); }
[5] server/plugin.ts
是服务器端插件定义。此插件的形状与其客户端对应部分相同
import type { Plugin, PluginInitializerContext, CoreSetup, CoreStart } from '@kbn/core/server'; export class MyPlugin implements Plugin { constructor(initializerContext: PluginInitializerContext) {} public setup(core: CoreSetup) { // called when plugin is setting up during Kibana's startup sequence } public start(core: CoreStart) { // called after all plugins are set up } public stop() { // called when plugin is torn down during Kibana's shutdown sequence } }
Kibana 没有对插件内部架构的方式施加任何技术限制,尽管有一些考虑因素与插件如何与核心 API 和其他插件公开的 API 集成有关,这些因素可能会极大地影响它们的构建方式。
生命周期和核心服务编辑
构成 核心
的各种独立域由一系列服务表示,其中许多服务公开了提供给所有插件的公共接口。服务在其生命周期的不同部分公开不同的功能。我们使用服务定义上的特定命名函数来描述核心服务和插件的生命周期。
Kibana 有三个生命周期:setup
、start
和 stop
。当 Kibana 在服务器上设置或在浏览器中加载时,将依次调用每个插件的 setup
函数。在所有插件的 setup
完成后,将依次调用 start
函数。当 Kibana 优雅地关闭服务器或关闭浏览器选项卡或窗口时,将依次调用 stop
函数。
下表说明了每个生命周期与 Kibana 状态的关系。
生命周期 | 目的 | 服务器 | 浏览器 |
---|---|---|---|
setup |
执行“注册”工作以设置运行时环境 |
配置 REST API 端点、注册已保存的对象类型等。 |
在 SPA 中配置应用程序路由、在扩展点注册自定义 UI 元素等。 |
start |
引导运行时逻辑 |
响应传入请求、请求 Elasticsearch 服务器等。 |
开始轮询 Kibana 服务器、更新 DOM 树以响应用户交互等。 |
stop |
清理运行时 |
在服务器关闭之前释放活动句柄。 |
当用户离开 Kibana 时,将会话数据存储在 LocalStorage 中,等等。 |
相反,Kibana 平台插件中没有与 uiExports
等效的内容。根据一般的经验法则,通过 uiExports
注册的功能现在在 setup
阶段注册。大多数其他所有内容都应移至 start
阶段。
核心服务公开的特定于生命周期的协定始终作为第一个参数传递给插件中的等效生命周期函数。例如,核心 http
服务向所有插件 setup
函数公开一个函数 createRouter
。要使用此函数注册 HTTP 路由处理程序,插件只需从第一个参数访问它
import type { CoreSetup } from '@kbn/core/server'; export class MyPlugin { public setup(core: CoreSetup) { const router = core.http.createRouter(); // handler is called when '/path' resource is requested with `GET` method router.get({ path: '/path', validate: false }, (context, req, res) => res.ok({ content: 'ok' })); } }
不同的服务接口可以并且将会传递给 setup
、start
和 stop
,因为某些功能在正在运行的插件的上下文中才有意义,而其他类型的功能可能有限制,或者可能只在正在停止的插件的上下文中才有意义。
例如,浏览器中的 stop
函数作为 window.onbeforeunload
事件的一部分被调用,这意味着您不一定能够在这里可靠地执行异步代码。因此,core
可能不会向浏览器中的插件 stop
函数提供任何异步函数。
所有插件的当前生命周期函数将在下一个生命周期开始之前执行。也就是说,所有 setup
函数都在执行任何 start
函数之前执行。
这些是核心服务为每个生命周期公开的协定
生命周期 | 服务器协定 | 浏览器协定 |
---|---|---|
构造函数 |
||
setup |
||
start |
与其他插件集成编辑
插件可以公开公共接口供其他插件使用。与 core
一样,这些接口绑定到生命周期函数 setup
和/或 start
。
从 setup
或 start
返回的任何内容都将充当接口,虽然这不是技术要求,但所有第一方 Elastic 插件也将公开该接口的类型。强烈建议希望允许其他插件与其集成的第三方插件也公开其插件接口的类型。
foobar plugin.ts
import type { Plugin } from '@kbn/core/server'; export interface FoobarPluginSetup { getFoo(): string; } export interface FoobarPluginStart { getBar(): string; } export class MyPlugin implements Plugin<FoobarPluginSetup, FoobarPluginStart> { public setup(): FoobarPluginSetup { return { getFoo() { return 'foo'; }, }; } public start(): FoobarPluginStart { return { getBar() { return 'bar'; }, }; } }
与核心不同,插件公开的功能 *不会* 自动注入到所有插件中。相反,如果插件希望使用另一个插件提供的公共接口,则它必须首先在其 kibana.json
清单文件中将该插件声明为依赖项。
demo kibana.json
{ "id": "demo", "requiredPlugins": ["foobar"], "server": true, "ui": true }
在插件清单中指定后,就可以通过 setup
和/或 start
的第二个参数使用相应的接口
demo plugin.ts
import type { CoreSetup, CoreStart } from '@kbn/core/server'; import type { FoobarPluginSetup, FoobarPluginStart } from '../../foobar/server'; interface DemoSetupPlugins { foobar: FoobarPluginSetup; } interface DemoStartPlugins { foobar: FoobarPluginStart; } export class AnotherPlugin { public setup(core: CoreSetup, plugins: DemoSetupPlugins) { const { foobar } = plugins; foobar.getFoo(); // 'foo' foobar.getBar(); // throws because getBar does not exist } public start(core: CoreStart, plugins: DemoStartPlugins) { const { foobar } = plugins; foobar.getFoo(); // throws because getFoo does not exist foobar.getBar(); // 'bar' } public stop() {} }