概要 †Grafanaのバックエンドデータソースプラグインの開発手順を記載する。 目次 †
開発環境の構築 †https://grafana.com/tutorials/build-a-data-source-backend-plugin/ を参考に開発環境を構築する。 ビルドには以下が必要との事 (2021/10 現在)
セットアップ cd /tmp echo "## apt update ##" apt update echo "## apt install (initial) ##" apt install -y wget curl git curl nodejs npm echo "## go install ##" curl -L -o go1.16.9.linux-amd64.tar.gz https://golang.org/dl/go1.16.9.linux-amd64.tar.gz rm -rf /usr/local/go && tar -C /usr/local -xzf go1.16.9.linux-amd64.tar.gz echo "export PATH=\$PATH:/usr/local/go/bin" >> ~/.bashrc echo "## npm install n ##" npm install -g n echo "## n install 16.11.0 ##" n install 16.11.0 echo "## apt remove nodejs npm ##" apt remove -y nodejs npm echo "## apt remove yarn ##" apt remove -y cmdtest yarn echo "## re install yarn ##" curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list apt update && apt install -y yarn echo "## install mage ##" mkdir -p ~/go/bin git clone https://github.com/magefile/mage cd mage GOPATH=~/go go run bootstrap.go echo "export GOPATH=\$HOME/go" >>~/.bashrc echo "export PATH=\$PATH:\$GOPATH/bin" >>~/.bashrc source ~/.bashrc echo "## npm install @grafana/toolkit ##" npm install -g @grafana/toolkit プラグインの雛形の作成 †npx @grafana/toolkit plugin:create my-backend-datasource-test
Need to install the following packages:
Ok to proceed? (y) y
? Select plugin type Backend Datasource Plugin
✔ Fetching plugin template...
? Plugin name my-backend-datasource-test
? Organization (used as part of plugin ID) sample
? Description
? Keywords (separated by comma)
? Author myname
? Your URL (i.e. organisation url)
Your plugin details
---
Name: my-backend-datasource-test
ID: sample-my-backend-datasource-test
Description:
Keywords: []
Author: myname
Organisation: sample
Website:
? Is that ok? Yes
✔ Saving package.json and plugin.json files
✔ Cleaning
Congrats! You have just created Grafana Backend Datasource Plugin.
Learn more about Grafana Plugins at https://grafana.com/docs/grafana/latest/plugins/developing/development/
ビルド確認 cd my-backend-datasource-test go get -u github.com/grafana/grafana-plugin-sdk-go go mod tidy mage -v データソースプラグインの作成 †バックエンドのデータソースプラグインは設定画面とバックエンド処理の2つで成り立っており、フロント部分は TypeScrypt 、バックエンド部分は Go で作成する必要がある。 データソース 及び クエリの設定画面 †各設定画面は、データソース自体の設定画面と各パネルで使用するクエリ編集画面用の2つある。 データソース設定画面( ConfigEditor.tsx ) の作成 †中身は react コンポーネントだが、各入力フィールドは Grafana の UIコンポーネントを利用して作成する。 以下、雛形生成された ConfigEditor.tsx より抜粋。 :
return (
<div className="gf-form-group">
<div className="gf-form">
<FormField
label="Path"
labelWidth={6}
inputWidth={20}
onChange={this.onPathChange}
value={jsonData.path || ''}
placeholder="json field returned to frontend"
/>
</div>
<div className="gf-form-inline">
<div className="gf-form">
<SecretFormField
isConfigured={(secureJsonFields && secureJsonFields.apiKey) as boolean}
value={secureJsonData.apiKey || ''}
label="API Key"
placeholder="secure json field (backend only)"
labelWidth={6}
inputWidth={20}
onReset={this.onResetAPIKey}
onChange={this.onAPIKeyChange}
/>
</div>
</div>
</div>
);
}
}
値変更時の処理は、項目毎に onChange メソッドを実装する必要がある。 以下、雛形生成された ConfigEditor.tsx より抜粋。 :
export class ConfigEditor extends PureComponent<Props, State> {
onPathChange = (event: ChangeEvent<HTMLInputElement>) => {
const { onOptionsChange, options } = this.props;
const jsonData = {
...options.jsonData,
path: event.target.value,
};
onOptionsChange({ ...options, jsonData });
};
// Secure field (only sent to the backend)
onAPIKeyChange = (event: ChangeEvent<HTMLInputElement>) => {
const { onOptionsChange, options } = this.props;
onOptionsChange({
...options,
secureJsonData: {
apiKey: event.target.value,
},
});
};
onResetAPIKey = () => {
const { onOptionsChange, options } = this.props;
onOptionsChange({
...options,
secureJsonFields: {
...options.secureJsonFields,
apiKey: false,
},
secureJsonData: {
...options.secureJsonData,
apiKey: '',
},
});
};
:
項目を追加する場合は、 types.ts に追加する。 types.ts import { DataQuery, DataSourceJsonData } from '@grafana/data';
export interface MyQuery extends DataQuery {
// ここはクエリ画面の項目定義
queryText?: string;
constant: number;
withStreaming: boolean;
}
export const defaultQuery: Partial<MyQuery> = {
// ここはクエリ画面の項目のデフォルト値
constant: 6.5,
withStreaming: false,
};
/**
* These are options configured for each DataSource instance.
*/
export interface MyDataSourceOptions extends DataSourceJsonData {
// ここに通常の設定項目
path?: string;
}
/**
* Value that is used in the backend, but never sent over HTTP to the frontend
*/
export interface MySecureJsonData {
// ここにセキュアな設定項目
apiKey?: string;
}
クエリの設定画面 ( QueryEditor.tsx ) の作成 †クエリ画面側も設定画面と同じく react コンポーネントとして作成する。 以下、雛形生成された QueryEditor.tsx をそのまま記載。 import { LegacyForms } from '@grafana/ui';
import { QueryEditorProps } from '@grafana/data';
import { DataSource } from './datasource';
import { defaultQuery, MyDataSourceOptions, MyQuery } from './types';
const { FormField, Switch } = LegacyForms;
type Props = QueryEditorProps<DataSource, MyQuery, MyDataSourceOptions>;
export class QueryEditor extends PureComponent<Props> {
onQueryTextChange = (event: ChangeEvent<HTMLInputElement>) => {
const { onChange, query } = this.props;
onChange({ ...query, queryText: event.target.value });
};
onConstantChange = (event: ChangeEvent<HTMLInputElement>) => {
const { onChange, query, onRunQuery } = this.props;
onChange({ ...query, constant: parseFloat(event.target.value) });
// executes the query
onRunQuery();
};
onWithStreamingChange = (event: SyntheticEvent<HTMLInputElement>) => {
const { onChange, query, onRunQuery } = this.props;
onChange({ ...query, withStreaming: event.currentTarget.checked });
// executes the query
onRunQuery();
};
render() {
const query = defaults(this.props.query, defaultQuery);
const { queryText, constant, withStreaming } = query;
return (
<div className="gf-form">
<FormField
width={4}
value={constant}
onChange={this.onConstantChange}
label="Constant"
type="number"
step="0.1"
/>
<FormField
labelWidth={8}
value={queryText || ''}
onChange={this.onQueryTextChange}
label="Query Text"
tooltip="Not used yet"
/>
<Switch checked={withStreaming || false} label="Enable streaming (v8+)" onChange={this.onWithStreamingChange} />
</div>
);
}
}
ビルド等は yarn を使用して行う事ができる。( package.json を参照 ) 依存モジュールのインストール yarn install ビルド yarn build バックエンド処理の作成 †バックエンド部分は pkg/plugin/plugin.go に記述する。 クエリに対する処理は query メソッドに記述する。 :
func (d *SampleDatasource) query(_ context.Context, pCtx backend.PluginContext, query backend.DataQuery) backend.DataResponse {
response := backend.DataResponse{}
// Unmarshal the JSON into our queryModel.
var qm queryModel
response.Error = json.Unmarshal(query.JSON, &qm)
if response.Error != nil {
return response
}
log.DefaultLogger.Info("### pCtx ###", "pCtx", pCtx)
log.DefaultLogger.Info("### query ###", "query", query)
// create data frame response.
frame := data.NewFrame("response")
// add fields.
frame.Fields = append(frame.Fields,
data.NewField("time", nil, []time.Time{query.TimeRange.From, query.TimeRange.To}),
data.NewField("values", nil, []int64{10, 20}),
)
// add the frames to the response.
response.Frames = append(response.Frames, frame)
return response
}
:
設定画面でデータソース作成時行うヘルスチェックの内容は CheckHealth メソッドに記述する。 :
func (d *SampleDatasource) CheckHealth(_ context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
log.DefaultLogger.Info("CheckHealth called", "request", req)
var status = backend.HealthStatusOk
var message = "Data source is working"
// ランダムでエラーになるように記述されている為、コメントアウトしておく
//if rand.Int()%2 == 0 {
// status = backend.HealthStatusError
// message = "randomized error"
//}
return &backend.CheckHealthResult{
Status: status,
Message: message,
}, nil
}
:
Grafanaへの配備 †grafana の plugins ディレクトリに dist フォルダ事コピーする。 https://grafana.com/docs/grafana/latest/developers/plugins/sign-a-plugin/ サンプル作成 †サンプルとして他サーバで動作する WebAPI をデータソースとする、バックエンドデータソースを作成してみる。 TODO:
|