Grafana的Datasource插件开发实践一中介绍了开发datasource需要知道的基本内容。这篇文章中将介绍在项目中具体的开发实践。
Datasource模块
与Grafana的其余部分进行交互,插件文件需要导出以下5个模块:
Datasource // Required
QueryCtrl // Required
ConfigCtrl // Required
QueryOptionsCtrl //
AnnotationsQueryCtrl //
复制代码
constructor函数
datasource插件与数据源通信,并将数据转换为时间序列。 数据源有以下功能:
query(options) // 用于panel查询数据
testDatasource() // 用于自定义数据源的页面,测试当前配置的数据源是可用的
annotationQuery(options) // 用于dashboard获取注释信息
metricFindQuery(options) // used by query editor to get metric suggestions.
复制代码
constructor函数传入的参数有:
constructor(instanceSettings, $q, backendSrv, templateSrv) {}
// instanceSettings对象为:
{
id: 5,
jsonData: {
keepCookies: [],
tlsAuth: false,
tlsAuthWithCACert: false,
tlsSkipVerify: false,
},
meta: {
alerting: false,
annotations: true,
baseUrl: "public/plugins/grafana-server-datasource",
dependencies: {
grafanaVersion: "3.x.x",
plugins: [],
},
id: "grafana-server-datasource",
includes: null,
info: {
author: {
name:"liuchunhui",
url:"https://grafana.com",
},
description: "代理服务端作为数据源",
links: [
{name: "Github", url: ""},
{name: "MIT License", url: ""}
],
logos: {
large:"public/plugins/grafana-server-datasource/img/server-logo.png",
small:"public/plugins/grafana-server-datasource/img/server-logo.png"
},
screenshots:null
updated:"2018-04-23"
version:"1.0.0"
},
metrics: true,
module: "plugins/grafana-server-datasource/module",
name: "代理服务端",
routes: null,
type: "datasource",
}
name:"代理服务端数据源",
type:"grafana-server-datasource",
url:"/api/datasources/proxy/5",
}
// $q 是个函数
// backendSrv对象为:
{
$http: ƒ p(e),
$q: ƒ M(t),
$timeout: ƒ o(o,s,u),
HTTP_REQUEST_CANCELLED: -1,
alertSrv: {
$rootScope: object,
$timeout: ƒ o(o,s,u)
list: []
}
contextSrv: {
isEditor: true,
isGrafanaAdmin: true,
isSignedIn: true,
sidemenu: true,
sidemenuSmallBreakpoint: false,
user: { // 当前登录的用户信息
email:"admin@localhost",
gravatarUrl:"/avatar/46d229b033af06a191ff2267bca9ae56",
helpFlags1:0,
id:1,
isGrafanaAdmin:true,
isSignedIn:true,
lightTheme:true,
locale:"zh-CN",
login:"admin",
name:"admin",
orgCount:1,
orgId:1,
orgName:"Main Org.",
orgRole:"Admin",
timezone:"browser",
},
version:"5.0.1",
},
inFlightRequests:{},
noBackendCache:true,
}
// templateSrv对象为:
{
builtIns: {
__interval:{text: "1s", value: "1s"},
__interval_ms:{text: "100", value: "100"}
},
grafanaVariables: {},
index:{},
regex:/\$(\w+)|\[\[([\s\S]+?)(?::(\w+))?\]\]|\${(\w+)(?::(\w+))?}/g
}
复制代码
query函数
真正去查询数据时要调用的函数。官方提供的数据源有两种不同的结果,time series
和table
,目前grafana官方所有数据源和面板都支持time series
格式,table
格式仅由InfluxDB数据源和表格面板支持。 我们开发插件时可以自定义类型值,但是要做到开发的datasource插件也适配官方自带的panel插件,那么定义datasource返回的数据格式和grafana的一致。 datasource.query的time series
类型响应格式:
[{
"target":"upper_75",
"datapoints":[
[622, 1450754160000],
[365, 1450754220000]
]
}, {
"target":"upper_90",
"datapoints":[
[861, 1450754160000],
[767, 1450754220000]
]
}]
复制代码
datasource.query的table
类型响应格式:
[{
"columns": [{
"text": "Time",
"type": "time",
"sort": true,
"desc": true,
}, {
"text": "mean",
}, {
"text": "sum",
}],
"rows": [[
1457425380000,
null,
null
], [
1457425370000,
1002.76215352,
1002.76215352
]],
"type": "table"
}]
复制代码
传递给datasource.query
函数的请求对象:
{
"range": {
"from": moment, // 全局时间筛选起始日期
"raw": {from: "now-7d", to: "now"},
"to": moment, // 全局时间筛选结束日期
},
"rangeRaw": {
"from": "now-7d",
"to": "now",
},
"interval": "15m",
"intervalMs": 900000,
"targets": [{ // 定义的查询条件们
"refId": "A",
"target": "upper_75"
}, {
"refId": "B",
"target": "upper_90"
}],
"format": "json",
"maxDataPoints": 2495, //decided by the panel
"cacheTimeout": undefined,
"panelId": 2,
"timezone": "browser"
}
复制代码
query函数示例:
query(options) { // 返回的结果数据,panel插件会通过监听'data-received'获取到
const params = { // 封装http请求参数
from: options.range.from.format('x'),
to: options.range.to.format('x'),
targets: options.queries,
};
return this.doRequest({ // 发起http请求并返回结果
url: this.url + '/card-data', // 要制定公共接口规范
method: 'POST',
data: params
});
}
doRequest(options) {
options.withCredentials = this.withCredentials;
options.headers = this.headers;
return this.backendSrv.datasourceRequest(options); // datasourceRequest()是grafana提供的请求datasource函数
}
复制代码
testDatasource函数
当用户在添加新数据源时点击Save&Test
按钮,详细信息首先保存到数据库,然后testDatasource
调用数据源插件中定义的函数。 此函数向数据源发出查询,验证数据源是否配置的正确可用,确保当用户在新仪表板中编写查询时,数据源已正确配置。 函数示例:
/**
* 当保存并测试数据源时会调用该函数
* 测试数据源是否正常工
* return { status: "", message: "", title: "" }
*/
testDatasource() {
return this.doRequest({
url: this.url + '/',
method: 'GET',
}).then(response => {
if (response.status === 200) {
return { status: "success", message: "Data source is working", title: "Success" };
}
});
}
复制代码
annotationQuery函数
注释查询,传递给datasource.annotationQuery函数的请求对象:
{
"range": {
"from": "2016-03-04T04:07:55.144Z",
"to": "2016-03-04T07:07:55.144Z" },
"rangeRaw": {
"from": "now-3h",
"to": "now"
},
"annotation": {
"datasource": "generic datasource",
"enable": true,
"name": "annotation name"
}
}
复制代码
datasource.annotationQuery的预期结果:
[{
"annotation": {
"name": "annotation name", //should match the annotation name in grafana
"enabled": true,
"datasource": "generic datasource",
},
"title": "Cluster outage",
"time": 1457075272576,
"text": "Joe causes brain split",
"tags": "joe, cluster, failure"
}]
复制代码
可以使用grafana平台的数据库存取'注释'
metricFindQuery 函数
ConfigCtrl模块
当用户编辑或创建此类型的新数据源时,该类将被实例化并视为Angular控制器。 该类需要一个静态模板或templateUrl变量,该模板或变量将被渲染为此控制器的视图。 ConfigCtrl类中的this对象内容为:
{
current: {
name: "代理服务端数据源", // 数据源名称
isDefault: true, // 是否是默认
type: "grafana-server-datasource", // 数据源插件的id值
url: "http://localhost:3001", // HTTP:数据源对应的url
access: "proxy", // HTTP:连接数据源类型,有'direct'和'proxy'两种类型可选
basicAuth: true, // Auth:Basic Auth选项
basicAuthUser: "basic auth user", // Basic Auth Details:User选项,当basicAuth为true时有效
basicAuthPassword:"basic auth password", // Basic Auth Details:Password选项,当basicAuth为true时有效
withCredentials: true, // Auth:With Credentials选项
jsonData: {
tlsAuth: true, // Auth:TLS Client Auth选项
tlsAuthWithCACert: true, // Auth: With CA Cert选项
tlsSkipVerify: true, // Auth: Skip TLS Verification (Insecure)选项值
keepCookies: ["Advanced Cookoe"], // Advanced HTTP Settings: cookie的白名单
},
secureJsonData: {
tlsCACert: "TLS Auth CA Cert", // TLS Auth Details:CA Cert选项,当jsonData下的tlsAuthWithCACert值为true时有效
tlsClientCert: "TLS Auth Client Cert", // TLS Auth Details:Client Cert选项,当jsonData下的tlsAuth值为true时有效
tlsClientKey: "TLS Auth Client Key", // TLS Auth Details:Client Key选项,当jsonData下的tlsAuth值为true时有效
},
secureJsonFields: {},
},
meta: {
baseUrl: "public/plugins/grafana-server-datasource",
defaultNavUrl: "",
dependencies: {
grafanaVersion: "3.x.x",
plugins: [],
},
enabled: false,
hasUpdate: false,
id: "grafana-server-datasource",
includes: null,
info: { // plugin.json中配置的信息
author: {
name: "liuchunhui",
url: "https://grafana.com"
},
description: "代理服务端作为数据源",
links: [
{name: "Github", url: ""},
{name: "MIT License", url: ""}
],
logos: {
large:"public/plugins/grafana-server-datasource/img/server-logo.png",
small:"public/plugins/grafana-server-datasource/img/server-logo.png"
},
screenshots:null,
updated:"2018-04-23",
version:"1.0.0"
},
jsonData: null,
latestVersion: "",
module: "plugins/grafana-server-datasource/module",
name: "代理服务端",
pinned: false
state: "",
type: "datasource",
}
复制代码
模板页面中使用grafana封装的angular组件,传入this的current
对象,实现HTTP、Auth两个模块的定义:
<datasource-http-settings current="ctrl.current">datasource-http-settings>
复制代码
QueryCtrl模块
一个JavaScript类,当用户在面板中编辑指标时,它将被实例化并作为Angular控制器处理。 该类必须从app/plugins/sdk.QueryCtrl
类继承。 该类需要一个静态模板或templateUrl变量,该模板或变量将被渲染为此控制器的视图。 当用户在panel面板下,切回到Metrics
模块时,会初始化该类。所以我们可以在构造函数中做一些我们自己的事情。比如获取指标维度列表,作为用户筛选的展示条件。
import { QueryCtrl } from 'app/plugins/sdk';
export default class GenericQueryCtrl extends QueryCtrl {
constructor($scope, $injector) {
super($scope, $injector);
// 获取参数列表请求
this.requestParams().then(response => {
const targets = response.data.target;
this.options = response.data.options;
this.text = response.data.text;
this.keys = Object.keys(targets);
for (let key in targets) {
this.target[key] = this.target[key] || targets[key];
}
});
}
requestParams() { // 请求获取参数列表
const params = {
header: {
'Content-Type': 'application/json'
},
method: 'GET',
retry: 0,
url: this.datasource.url + '/param-list'
};
return this.datasource.backendSrv.datasourceRequest(params); // 使用grafana提供的http请求函数
}
onChangeInternal() { // 刷新面板
this.panelCtrl.refresh(); // grafana自带方法使面板更新数据
}
toggleEditorMode() { // 是否开启编辑模式
this.target.rawQuery = !this.target.rawQuery;
}
}
GenericQueryCtrl.templateUrl = './page/query.html';
复制代码
QueryCtrl控制器的query.html模板:
<query-editor-row query-ctrl="ctrl" has-text-edit-mode="true">
<div class="gf-form"
ng-if="!ctrl.target.rawQuery"
ng-repeat="key in ctrl.keys">
<span class="gf-form-label width-7">
{{ctrl.text[key]}}
span>
<select class="gf-form-input width-25"
ng-model="ctrl.target[key]"
ng-change="ctrl.onChangeInternal()">
<option ng-repeat="option in ctrl.options[key]"
value="{{option.name}}">
{{option.desc}}
option>
select>
div>
<div ng-if="ctrl.target.rawQuery">
<textarea class="gf-form-input" rows="5" spellcheck="false" ng-blur="ctrl.onChangeInternal()" />
div>
query-editor-row>
复制代码
标签的内容会加入到Add Query
模板中,标签中的has-text-edit-mode="true"属性,能开启Toggle Edit Mode
功能。 QueryCtrl控制器中的target.rawQuery参数,标记着两种编辑模式的切换,但是这两种模式需要写代码定义。
AnnotationsQueryCtrl模块
当用户在datasource的模板菜单中选择这种类型的数据源时,将被实例化并作为Angular控制器处理的JavaScript类。 此类需要一个静态模板或templateUrl变量,该模板或变量将被渲染为此控制器的视图。 绑定到此控制器的字段随后会发送到数据库对象annotationQuery函数。 在开发插件时能自定义dashboard的Built in query
的条件。 AnnotationQueryCtrl代码:
export default class GenericAnnotationsQueryCtrl {}
GenericAnnotationsQueryCtrl.templateUrl = './page/annotationsQuery.html';
复制代码
annotationsQuery.html代码:
<h5 class="section-heading">注解查询条件设定h5>
<div class="gf-form-group">
<div class="gf-form">
<input type="text" class="gf-form-input" ng-model='ctrl.annotation.query' placeholder="" />
div>
div>
复制代码
QueryOptionsCtrl模块
一个JavaScript类,当用户在面板中编辑指标时,它将被实例化并作为Angular控制器处理。 此控制器负责处理数据源的面板范围设置,如间隔,速率和聚合(如果需要)。 此类需要一个静态模板或templateUrl变量,该模板或变量将被渲染为此控制器的视图。
QueryOptionsCtrl代码:
export default class GenericQueryOptionsCtrl {}
GenericQueryOptionsCtrl.templateUrl = './page/queryOptions.html';
复制代码
queryOptions.html代码:
<section class="grafana-metric-options" >
<div class="gf-form">div>
section>
复制代码