跳到主要内容

构建数据源插件

简介

Grafana 支持广泛的数据源,包括 Prometheus、MySQL 和 Datadog。 然而,在某些情况下,您可能已经拥有希望添加到 Grafana 仪表板的内部指标解决方案。 本教程教您构建一个新的数据源插件来查询数据。

在本教程中,您将

  • 构建一个数据源以可视化正弦波
  • 使用查询编辑器构建查询
  • 使用配置编辑器配置您的数据源

先决条件

  • Grafana v10.0 或更高版本
  • LTS 版本的 Node.js

创建一个新插件

Grafana create-plugin 工具 是一个 CLI 应用程序,可以简化 Grafana 插件开发,以便您可以专注于代码。 该工具为您搭建了一个入门插件、所有必需的配置以及使用 Docker Compose 的开发环境。

  1. 在一个新目录中,使用 create-plugin 工具从模板创建一个插件。 当提示插件类型时,选择datasource:

    npx @grafana/create-plugin@latest
  2. 转到您新创建的插件目录

    cd <your-plugin>
  3. 安装依赖项

    npm install
  4. 构建插件

    npm run dev
  5. 启动 Grafana

    docker compose up
  6. 打开 Grafana,默认地址为 https://127.0.0.1:3000,然后转到 管理 > 插件。 确保您的datasource插件在那里。

您还可以通过检查日志来验证 Grafana 是否已发现您的插件

INFO[01-01|12:00:00] Plugin registered       logger=plugin.loader pluginID=<your-plugin>

要了解如何创建后端数据源插件,请参阅 构建数据源后端插件

插件的结构

您创建的每个插件至少需要两个文件:plugin.jsonsrc/module.ts

plugin.json

当 Grafana 启动时,它会扫描插件目录中任何包含 plugin.json 文件的子目录。 plugin.json 文件包含有关您的插件的信息,并告知 Grafana 您的插件需要哪些功能和依赖项。

虽然某些插件类型可能具有特定的配置选项,但让我们看看强制性的选项

  • type 告诉 Grafana 要期望的插件类型。 Grafana 支持三种类型的插件:paneldatasourceapp
  • name 是用户将在插件列表中看到的名称。 如果您正在创建数据源,这通常是它连接的数据库的名称,例如 Prometheus、PostgreSQL 或 Stackdriver。
  • id 唯一标识您的插件,应遵循以下命名约定:<$组织名称>-<$插件名称>-<$插件类型>。 create-plugin 工具会根据您对其提示的响应正确配置此项。

要查看 plugin.json 的所有可用配置设置,请参阅 plugin.json 架构

module.ts

在发现您的插件后,Grafana 会加载 module.js 文件,即您插件的入口点。 module.js 公开了您插件的实现,这取决于您正在构建的插件类型。

具体来说,src/module.ts 需要导出一个扩展 GrafanaPlugin 的类,并且可以是以下任何一种

数据源插件

Grafana 中的数据源必须扩展 DataSourceApi 接口,这需要您定义两个方法:querytestDatasource

query 方法

query 方法是任何数据源插件的核心。 它接受来自用户的查询,从外部数据库检索数据,并以 Grafana 可识别的格式返回数据。

async query(options: DataQueryRequest<MyQuery>): Promise<DataQueryResponse>

options 对象包含用户发出的查询或目标,以及上下文信息,例如当前时间间隔。 使用此信息来查询外部数据库。

测试您的数据源

testDatasource 为您的数据源实现健康检查。 例如,当用户在更改连接设置后单击 保存 & 测试 按钮时,Grafana 会调用此方法。

async testDatasource()

有关前端数据源中健康检查的示例,请参阅我们的 datasource-http 插件。

返回数据帧

有无数个不同的数据库,每个数据库都有自己查询数据的方式。 为了能够支持所有不同的数据格式,Grafana 将数据整合到称为数据帧的统一数据结构中。

让我们看看如何从 query 方法创建和返回数据帧。 在此步骤中,您将更改入门插件中的代码以返回正弦波

  1. 在当前的 query 方法中,删除 map 函数内部的代码。

    现在的 query 方法看起来像这样

    src/datasource.ts
    async query(options: DataQueryRequest<MyQuery>): Promise<DataQueryResponse> {
    const { range } = options;
    const from = range!.from.valueOf();
    const to = range!.to.valueOf();

    const data = options.targets.map(target => {
    // Your code goes here.
    });

    return { data };
    }
  2. map 函数中,使用 lodash/defaults 包为尚未设置的查询属性设置默认值

    src/datasource.ts
    import defaults from 'lodash/defaults';

    const query = defaults(target, defaultQuery);
  3. 在 datasource.ts 的顶部创建一个默认查询

    src/datasource.ts
    export const defaultQuery: Partial<MyQuery> = {
    constant: 6.5,
    };
  4. 创建一个包含时间字段和数字字段的数据帧

    src/datasource.ts
    const frame = createDataFrame({
    refId: query.refId,
    fields: [
    { name: 'time', type: FieldType.time },
    { name: 'value', type: FieldType.number },
    ],
    });

    需要设置 refId 以告知 Grafana 生成此数据帧的查询。

接下来,我们将实际值添加到数据帧。 不用担心用于计算值的数学公式。

  1. 创建几个辅助变量

    src/datasource.ts
    // duration of the time range, in milliseconds.
    const duration = to - from;

    // step determines how close in time (ms) the points will be to each other.
    const step = duration / 1000;
  2. 将值添加到数据帧

    src/datasource.ts
    for (let t = 0; t < duration; t += step) {
    frame.add({ time: from + t, value: Math.sin((2 * Math.PI * t) / duration) });
    }

    frame.add() 接受一个对象,其中键对应于数据帧中每个字段的名称。

  3. 返回数据帧

    src/datasource.ts
    return frame;
  4. 通过创建新的数据源实例并构建仪表板来试用它。

您的数据源现在正在发送 Grafana 可以可视化的数据帧。 接下来,我们将了解如何通过定义查询来控制正弦波的频率。

信息

在此示例中,我们正在从当前时间范围生成时间戳。 这意味着无论您使用什么时间范围,您都将获得相同的图形。 在实践中,您应该使用数据库返回的时间戳。

定义查询

大多数数据源都提供查询特定数据的方法。 MySQL 和 PostgreSQL 使用 SQL,而 Prometheus 有自己的查询语言,称为 PromQL。 无论您的数据库使用哪种查询语言,Grafana 都允许您构建对其的支持。

通过实现您自己的查询编辑器(一个 React 组件,使用户能够通过用户友好的图形界面构建自己的查询),为您的数据源添加对自定义查询的支持。

查询编辑器可以像用户编辑原始查询文本的文本字段一样简单,也可以提供更用户友好的表单,其中包含下拉菜单和开关,这些菜单和开关稍后会在发送到数据库之前转换为原始查询文本。

定义查询模型

设计查询编辑器的第一步是定义其查询模型。 查询模型定义了数据源的用户输入。

我们希望能够控制正弦波的频率,因此让我们添加另一个属性。

  1. 向查询模型添加一个新的数字属性,名为 frequency

    src/types.ts
    export interface MyQuery extends DataQuery {
    queryText?: string;
    constant: number;
    frequency: number;
    }
  2. 为新的 frequency 属性设置默认值

    src/types.ts
    export const defaultQuery: Partial<MyQuery> = {
    constant: 6.5,
    frequency: 1.0,
    };

将模型绑定到表单

现在您已经定义了希望支持的查询模型,下一步是将模型绑定到表单。 FormFieldgrafana/ui 中的一个文本字段组件,允许您注册一个侦听器,每当表单字段值更改时,该侦听器都会被调用。

  1. query 对象定义 frequency,并在查询编辑器中添加一个新的表单字段,以控制 render 方法中的新 frequency 属性。

    src/components/QueryEditor.tsx
    const { queryText, constant, frequency } = query;

    <InlineField label="Frequency" labelWidth={16}>
    <Input onChange={onFrequencyChange} value={frequency || ''} />
    </InlineField>;
  2. 为新属性添加事件侦听器。

    src/components/QueryEditor.tsx
    const onFrequencyChange = (event: ChangeEvent<HTMLInputElement>) => {
    onChange({ ...query, frequency: parseFloat(event.target.value) });
    // executes the query
    onRunQuery();
    };

    注册的侦听器 onFrequencyChange 调用 onChange 以使用表单字段中的值更新当前查询。

    onRunQuery(); 告诉 Grafana 在每次更改后运行查询。 对于快速查询,建议这样做以提供更快的响应体验。

使用该属性

新的查询模型现在可以在我们的 query 方法中使用了。

  1. query 方法中,使用 frequency 属性来调整我们的公式。

    src/datasource.ts
    frame.add({ time: from + t, value: Math.sin((2 * Math.PI * query.frequency * t) / duration) });
  2. 通过更改面板查询中的频率来试用它。

为您的数据源启用配置

要访问特定的数据源,您通常需要配置主机名、凭据或身份验证方法等内容。 配置编辑器允许您的用户配置您的数据源插件以满足他们的需求。

配置编辑器看起来类似于查询编辑器,因为它定义了一个模型并将其绑定到表单。

由于在我们的正弦波示例中我们实际上并未连接到外部数据库,因此我们实际上不需要太多选项。 但是,为了向您展示如何添加选项,我们将添加波分辨率作为一个选项。

分辨率控制数据点之间在时间上的接近程度。 较高的分辨率意味着更多的点更接近,但代价是处理更多的数据。

定义选项模型

  1. 向选项模型添加一个新的数字属性,名为 resolution

    src/types.ts
    export interface MyDataSourceOptions extends DataSourceJsonData {
    path?: string;
    resolution?: number;
    }

将模型绑定到表单

就像查询编辑器一样,配置编辑器中的表单字段在值更改时会调用注册的侦听器。

  1. 在查询编辑器中添加一个新的表单字段,以控制新的分辨率选项。

    src/components/ConfigEditor.tsx
    <InlineField label="Resolution" labelWidth={12}>
    <Input onChange={onResolutionChange} value={jsonData.resolution || ''} placeholder="Enter a number" width={40} />
    </InlineField>
  2. 为新选项添加事件侦听器。

    src/components/ConfigEditor.tsx
    const onResolutionChange = (event: ChangeEvent<HTMLInputElement>) => {
    const jsonData = {
    ...options.jsonData,
    resolution: parseFloat(event.target.value),
    };
    onOptionsChange({ ...options, jsonData });
    };

    onResolutionChange 侦听器调用 onOptionsChange 以使用表单字段中的值更新当前选项。

使用该选项

  1. DataSource 类中创建一个名为 resolution 的属性。

    src/datasource.ts
    export class DataSource extends DataSourceApi<MyQuery, MyDataSourceOptions> {
    resolution: number;

    constructor(instanceSettings: DataSourceInstanceSettings<MyDataSourceOptions>) {
    super(instanceSettings);

    this.resolution = instanceSettings.jsonData.resolution || 1000.0;
    }

    // ...
  2. query 方法中,使用 resolution 属性来更改我们计算步长的方式。

    src/datasource.ts
    const step = duration / this.resolution;
  3. 通过配置新的数据源并更改分辨率的值来试用它。

总结

在本教程中,您构建了一个完整用于 Grafana 的数据源插件,该插件使用查询编辑器来控制要可视化哪些数据。 您添加了一个数据源选项,通常用于设置连接选项等。

了解更多

从外部 API 获取数据

Grafana 中的大多数数据源将从外部 API 返回数据。 本教程试图保持简单,不需要额外的服务。 要了解如何实现这一点,请使用 datasource-http 示例。

此示例显示了如何使用来自 grafana-runtimegetBackendSrv 函数

虽然您可以使用诸如 axiosFetch API 之类的东西来发出请求,但我们建议使用 getBackendSrv,因为它通过 Grafana 服务器代理请求,而不是从浏览器发出请求。 当向外部 API 发出经过身份验证的请求时,我们强烈建议这样做。 有关验证外部请求的更多信息,请参阅 为数据源插件添加身份验证

提高插件的质量

要了解有关高级插件开发主题的更多信息,请参阅以下内容