stackdriver: better error handling and show query metadata

If the Stackdriver returns an error, show that error in the query
editor. Also, allow the user to see the raw querystring that was sent
to google (for troubleshooting).
This commit is contained in:
Daniel Lee
2018-09-11 22:41:24 +02:00
parent 2683699ab4
commit 0b5783563e
7 changed files with 218 additions and 117 deletions

View File

@ -20,26 +20,27 @@ export default class StackdriverDatasource {
const result = [];
try {
const { data } = await this.backendSrv.datasourceRequest({
url: '/api/tsdb/query',
method: 'POST',
data: {
from: options.range.from.valueOf().toString(),
to: options.range.to.valueOf().toString(),
queries,
},
});
const { data } = await this.backendSrv.datasourceRequest({
url: '/api/tsdb/query',
method: 'POST',
data: {
from: options.range.from.valueOf().toString(),
to: options.range.to.valueOf().toString(),
queries,
},
});
if (data.results) {
Object['values'](data.results).forEach(queryRes => {
queryRes.series.forEach(series => {
result.push({ target: series.name, datapoints: series.points });
if (data.results) {
Object['values'](data.results).forEach(queryRes => {
queryRes.series.forEach(series => {
result.push({
target: series.name,
datapoints: series.points,
refId: queryRes.refId,
meta: queryRes.meta,
});
});
}
} catch (error) {
console.log(error);
});
}
return { data: result };

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -2,15 +2,49 @@
<div class="gf-form-inline">
<div class="gf-form">
<span class="gf-form-label width-9">Project</span>
<input class="gf-form-input" disabled type="text" ng-model='ctrl.target.project.name' get-options="ctrl.getProjects()" css-class="min-width-12"
/>
<input class="gf-form-input" disabled type="text" ng-model='ctrl.target.project.name' get-options="ctrl.getProjects()"
css-class="min-width-12" />
</div>
</div>
<div class="gf-form-inline">
<div class="gf-form">
<span class="gf-form-label width-9">Metric Type</span>
<gf-form-dropdown model="ctrl.target.metricType" get-options="ctrl.getMetricTypes($query)" class="gf-form-input" disabled
type="text" allow-custom="true" lookup-text="true" css-class="min-width-12" on-change="ctrl.refresh()"></gf-form-dropdown>
<gf-form-dropdown model="ctrl.target.metricType" get-options="ctrl.getMetricTypes($query)" class="min-width-20"
disabled type="text" allow-custom="true" lookup-text="true" css-class="min-width-12" on-change="ctrl.refresh()"></gf-form-dropdown>
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
</query-editor-row>
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label query-keyword" ng-click="ctrl.showHelp = !ctrl.showHelp">
Show Help
<i class="fa fa-caret-down" ng-show="ctrl.showHelp"></i>
<i class="fa fa-caret-right" ng-hide="ctrl.showHelp"></i>
</label>
</div>
<div class="gf-form" ng-show="ctrl.lastQueryMeta">
<label class="gf-form-label query-keyword" ng-click="ctrl.showLastQuery = !ctrl.showLastQuery">
Raw Query
<i class="fa fa-caret-down" ng-show="ctrl.showLastQuery"></i>
<i class="fa fa-caret-right" ng-hide="ctrl.showLastQuery"></i>
</label>
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
<div class="gf-form" ng-show="ctrl.showLastQuery">
<pre class="gf-form-pre">{{ctrl.lastQueryMeta.rawQueryString}}</pre>
</div>
<div class="gf-form" ng-show="ctrl.showHelp">
<pre class="gf-form-pre alert alert-info">
Help text for aliasing
</pre>
</div>
<div class="gf-form" ng-show="ctrl.lastQueryError">
<pre class="gf-form-pre alert alert-error">{{ctrl.lastQueryError}}</pre>
</div>
</query-editor-row>

View File

@ -3,15 +3,23 @@
"type": "datasource",
"id": "stackdriver",
"metrics": true,
"alerting": false,
"alerting": true,
"annotations": false,
"queryOptions": {
"maxDataPoints": true,
"cacheTimeout": true
},
"info": {
"description": "Data Source for Stackdriver",
"version": "1.0.0"
"description": "Google Stackdriver Datasource for Grafana",
"version": "1.0.0",
"logos": {
"small": "img/stackdriver_logo.png",
"large": "img/stackdriver_logo.png"
},
"author": {
"name": "Grafana Project",
"url": "https://grafana.com"
}
},
"routes": [
{

View File

@ -2,6 +2,10 @@ import _ from 'lodash';
import { QueryCtrl } from 'app/plugins/sdk';
import appEvents from 'app/core/app_events';
export interface QueryMeta {
rawQuery: string;
rawQueryString: string;
}
export class StackdriverQueryCtrl extends QueryCtrl {
static templateUrl = 'partials/query.editor.html';
target: {
@ -10,21 +14,31 @@ export class StackdriverQueryCtrl extends QueryCtrl {
name: string;
};
metricType: string;
refId: string;
};
defaultDropdownValue = 'select';
defaultDropdownValue = 'Select metric';
defaults = {
project: {
id: 'default',
name: 'loading project...',
},
metricType: this.defaultDropdownValue,
// metricType: this.defaultDropdownValue,
};
showHelp: boolean;
showLastQuery: boolean;
lastQueryMeta: QueryMeta;
lastQueryError?: string;
/** @ngInject */
constructor($scope, $injector) {
super($scope, $injector);
_.defaultsDeep(this.target, this.defaults);
this.panelCtrl.events.on('data-received', this.onDataReceived.bind(this), $scope);
this.panelCtrl.events.on('data-error', this.onDataError.bind(this), $scope);
this.getCurrentProject().then(this.getMetricTypes.bind(this));
}
@ -67,4 +81,34 @@ export class StackdriverQueryCtrl extends QueryCtrl {
return [];
}
}
onDataReceived(dataList) {
this.lastQueryError = null;
this.lastQueryMeta = null;
const anySeriesFromQuery: any = _.find(dataList, { refId: this.target.refId });
if (anySeriesFromQuery) {
this.lastQueryMeta = anySeriesFromQuery.meta;
this.lastQueryMeta.rawQueryString = decodeURIComponent(this.lastQueryMeta.rawQuery);
}
}
onDataError(err) {
if (err.data && err.data.results) {
const queryRes = err.data.results[this.target.refId];
if (queryRes) {
this.lastQueryMeta = queryRes.meta;
this.lastQueryMeta.rawQueryString = decodeURIComponent(this.lastQueryMeta.rawQuery);
let jsonBody;
try {
jsonBody = JSON.parse(queryRes.error);
} catch {
this.lastQueryError = queryRes.error;
}
this.lastQueryError = jsonBody.error.message;
}
}
}
}