跳至主要内容

添加对变量的支持

变量是值的占位符,您可以使用它们来创建模板化查询以及仪表板或面板链接。有关变量的更多信息,请参阅 模板和变量

在本指南中,您将看到如何将类似于以下的查询字符串转换为

SELECT * FROM services WHERE id = "$service"

成为

SELECT * FROM services WHERE id = "auth-api"

Grafana 提供了一些辅助函数来在字符串模板中插入变量。让我们看看如何在您的插件中使用它们。

向插件添加变量

在面板插件中插入变量

对于面板,replaceVariables 函数在 PanelProps 中可用。

replaceVariables 添加到参数列表,并向其传递用户定义的模板字符串

export function SimplePanel({ options, data, width, height, replaceVariables }: Props) {
const query = replaceVariables('Now displaying $service');

return <div>{query}</div>;
}

在数据源插件中插入变量

对于数据源,您需要使用 getTemplateSrv,它返回 TemplateSrv 的实例。

  1. runtime 包导入 getTemplateSrv

    import { getTemplateSrv } from '@grafana/runtime';
  2. 在您的 query 方法中,使用用户定义的模板字符串调用 replace 方法

    async query(options: DataQueryRequest<MyQuery>): Promise<DataQueryResponse> {
    const query = getTemplateSrv().replace('SELECT * FROM services WHERE id = "$service"', options.scopedVars);

    const data = makeDbQuery(query);

    return { data };
    }

从插件设置变量

您不仅可以读取变量的值,还可以从插件更新变量。使用 locationService.partial(query, replace)

以下示例展示了如何更新名为 service 的变量。

  • query 包含您要更新的查询参数。控制变量的查询参数以 var- 为前缀。
  • replace: true 告诉 Grafana 更新当前 URL 状态,而不是创建新的历史记录条目。
import { locationService } from '@grafana/runtime';
locationService.partial({ 'var-service': 'billing' }, true);
注意

每当您更新变量时,Grafana 都会查询您的数据源。过度更新变量会导致 Grafana 速度变慢,并导致用户体验不佳。

为您的数据源添加对查询变量的支持

一个 查询变量 是一种类型的变量,它允许您查询数据源以获取值。通过为您的数据源插件添加对查询变量的支持,用户可以根据来自您的数据源的数据创建动态仪表板。

让我们从为变量查询定义一个查询模型开始

export interface MyVariableQuery {
namespace: string;
rawQuery: string;
}

为了使数据源支持查询变量,请在您的 DataSourceApi 类中覆盖 metricFindQuerymetricFindQuery 函数返回一个 MetricFindValue 数组,该数组具有一个属性 text

async metricFindQuery(query: MyVariableQuery, options?: any) {
// Retrieve DataQueryResponse based on query.
const response = await this.fetchMetricNames(query.namespace, query.rawQuery);

// Convert query results to a MetricFindValue[]
const values = response.data.map(frame => ({ text: frame.name }));

return values;
}
注意

默认情况下,Grafana 为简单的文本查询提供了一个基本的查询模型和编辑器。如果这就是您所需要的,那么将查询类型保留为 string

async metricFindQuery(query: string, options?: any)

让我们创建一个自定义查询编辑器,以允许用户编辑查询模型。

  1. 创建一个 VariableQueryEditor 组件

    src/VariableQueryEditor.tsx
    import React, { useState } from 'react';
    import { MyVariableQuery } from './types';

    interface VariableQueryProps {
    query: MyVariableQuery;
    onChange: (query: MyVariableQuery, definition: string) => void;
    }

    export const VariableQueryEditor = ({ onChange, query }: VariableQueryProps) => {
    const [state, setState] = useState(query);

    const saveQuery = () => {
    onChange(state, `${state.query} (${state.namespace})`);
    };

    const handleChange = (event: React.FormEvent<HTMLInputElement>) =>
    setState({
    ...state,
    [event.currentTarget.name]: event.currentTarget.value,
    });

    return (
    <>
    <div className="gf-form">
    <span className="gf-form-label width-10">Namespace</span>
    <input
    name="namespace"
    className="gf-form-input"
    onBlur={saveQuery}
    onChange={handleChange}
    value={state.namespace}
    />
    </div>
    <div className="gf-form">
    <span className="gf-form-label width-10">Query</span>
    <input
    name="rawQuery"
    className="gf-form-input"
    onBlur={saveQuery}
    onChange={handleChange}
    value={state.rawQuery}
    />
    </div>
    </>
    );
    };

    Grafana 在任何一个文本字段失去焦点 (onBlur) 时保存查询模型,然后预览 metricFindQuery 返回的值。

    onChange 的第二个参数允许您设置查询的文本表示,该表示将显示在变量列表中变量名称旁边。

  2. 配置您的插件以使用查询编辑器

    import { VariableQueryEditor } from './VariableQueryEditor';

    export const plugin = new DataSourcePlugin<DataSource, MyQuery, MyDataSourceOptions>(DataSource)
    .setQueryEditor(QueryEditor)
    .setVariableQueryEditor(VariableQueryEditor);

就这样!您现在可以通过将 查询变量 添加到您的仪表板来试用该插件。

使用模板变量

模板变量 使用户能够创建根据其输入动态更改的仪表板。由于变量在 Grafana 中已经存在很长时间,因此许多用户期望它们得到他们安装的任何数据源的支持。

插入模板变量

要插入模板变量,您需要从 @grafana/runtime 包中导入 getTemplateSrv() 函数

import { getTemplateSrv } from '@grafana/runtime';

getTemplateSrv() 函数返回一个 TemplateSrv 的实例,它提供了用于处理模板变量的方法。其中最重要的一个 replace() 接受包含变量的字符串作为输入,并返回一个插入的字符串,其中变量已替换为用户选择的相应值。

例如,如果您有一个名为 instance 的变量,则以下代码将使用其相应的值替换该变量

getTemplateSrv().replace("I'd like $instance, please!");

// I'd like server-1, please!

replace() 甚至处理内置变量,例如 $__from$__to

就是这样!对于大多数用例,这就是您需要做到的,以在您的数据源中添加对模板变量的支持。请注意,您需要决定哪些字段支持模板变量。例如,要在查询中插入单个属性 rawQuery,请添加以下内容

const interpolatedQuery: MyQuery = {
...query,
rawQuery: getTemplateSrv().replace(query.rawQuery),
};

格式化多值变量

在前面的示例中,变量只有一个值 server-1。但是,如果用户改而创建一个多值变量,它可以同时保存多个值。多值变量带来了一个新的挑战:如何决定如何格式化值集合?

例如,以下哪种不同的格式适合您的用例?

{server-1, server-2, server-3} (Graphite)
["server-1", "server-2", "server-3"] (JSON)
("server-1" OR "server-2" OR "server-3") (Lucene)

幸运的是,replace() 方法允许您传递第三个参数,以允许您从一组预定义的格式中进行选择,例如 CSV 格式

getTemplateSrv().replace("I'd like $instance, please!", {}, "csv");

// I'd like server-1, server-2, server-3, please!
注意

replace() 方法的第二个参数允许您配置要在插入字符串时包含的自定义变量集或作用域变量集。除非您对此感兴趣,否则可以随意传递一个空对象 {}

Grafana 支持各种格式选项。要浏览可用的格式,请查看 高级变量格式选项

使用插值函数格式化变量

在查看了高级变量格式选项后,您可能会发现您想支持一个不可用的格式选项。幸运的是,Grafana 通过使用插值函数,让您完全控制 replace() 如何格式化变量。

您可以将插值函数传递给 replace(),而不是将字符串作为第三个参数传递。以下示例使用自定义格式化程序函数在最后一个元素之前添加一个 and

const formatter = (value: string | string[]): string => {
if (typeof value == 'string') {
return value;
}

// Add 'and' before the last element.
if (value.length > 1) {
return value.slice(0, -1).join(', ') + ' and ' + value[value.length - 1];
}

return value[0];
};

getTemplateSrv().replace("I'd like $instance, please!", {}, formatter);

// I'd like server-1, server-2, and server-3, please!

函数的参数可以是字符串或字符串数组,例如 (string | string[]),具体取决于变量是否支持多个值,因此请确保在使用值之前检查其类型。

在模板之外使用变量

在某些情况下,您可能希望在模板之外使用变量。例如,如果您想验证所选值的数量或将它们添加到下拉菜单中。

此辅助函数使用 replace() 方法将值作为数组返回

function getValuesForVariable(name: string): string[] {
const values: string[] = [];

// Collects the values in an array.
getTemplateSrv().replace(`$${name}`, {}, (value: string | string[]) => {
if (Array.isArray(value)) {
values.push(...value);
} else {
values.push(value);
}

// We don't really care about the string here.
return '';
});

return values;
}
const instances = getValuesForVariable("instance");

for (var instance of instances) {
console.log(instance);
}

// server-1
// server-2
// server-3

您甚至可以更进一步,创建一个对象,其中整齐地包含所有变量及其值

function getAllVariables(): Record<string, string[]> {
const entries = getTemplateSrv()
.getVariables()
.map((v) => [v.name, getValuesForVariable(v.name)]);

return Object.fromEntries(entries);
}
const vars = getAllVariables();

console.log(vars.instance);

// ["server-1", "server-2", "server-3"]

在此示例中,使用 getTemplateSrv().getVariables() 列出当前仪表板的所有配置变量。

注意

您也可以根据可预测的分隔符拆分插值字符串。 随意根据您的需要调整这些代码段。