Browse Source

Integration with grafana datasource kit #2 (#3)

* integrate exporter with new version of datasource-kit

* fix url for getting api key

* Add grafana-datasource-kit to deps
pull/1/head
Evgeny Smyshlyaev 6 years ago committed by rozetko
parent
commit
31326a767f
  1. 1
      package.json
  2. 88
      src/grafana-datasource-kit/grafana_metric_model.ts
  3. 70
      src/grafana-datasource-kit/grafana_service.ts
  4. 14
      src/target.ts

1
package.json

@ -15,6 +15,7 @@
"axios": "^0.18.0",
"express": "^4.16.3",
"fast-csv": "^2.4.1",
"grafana-datasource-kit": "^0.0.4",
"moment": "^2.22.1",
"nodemon": "^1.17.3",
"ts-loader": "^3.5.0",

88
src/grafana-datasource-kit/grafana_metric_model.ts

@ -1,88 +0,0 @@
export type GrafanaDatasource = {
url: string,
type: string,
params: {
db: string,
q: string,
epoch: string
}
}
export type GrafanaMetricId = string;
export class GrafanaMetric {
private _metricQuery: MetricQuery = undefined;
constructor(
public datasource: GrafanaDatasource,
public targets: any[],
public id?: GrafanaMetricId
) {
if(datasource === undefined) {
throw new Error('datasource is undefined');
}
if(targets === undefined) {
throw new Error('targets is undefined');
}
if(targets.length === 0) {
throw new Error('targets is empty');
}
}
public get metricQuery() {
if(this._metricQuery === undefined) {
this._metricQuery = new MetricQuery(this);
}
return this._metricQuery;
}
public toObject() {
return {
datasource: this.datasource,
targets: this.targets,
_id: this.id
};
}
static fromObject(obj: any): GrafanaMetric {
if(obj === undefined) {
throw new Error('obj is undefined');
}
return new GrafanaMetric(
obj.datasource,
obj.targets,
obj._id
);
}
}
export class MetricQuery {
private static INFLUX_QUERY_TIME_REGEX = /time >[^A-Z]+/;
private _queryParts: string[];
private _type: string;
constructor(metric: GrafanaMetric) {
this._type = metric.datasource.type;
if (this._type !== 'influxdb') {
throw new Error(`Queries of type "${metric.datasource.type}" are not supported yet.`);
}
var queryStr = metric.datasource.params.q;
this._queryParts = queryStr.split(MetricQuery.INFLUX_QUERY_TIME_REGEX);
if(this._queryParts.length == 1) {
throw new Error(
`Query "${queryStr}" is not replaced with LIMIT/OFFSET oeprators. Missing time clause.`
);
}
if(this._queryParts.length > 2) {
throw new Error(`Query "${queryStr}" has multiple time clauses. Can't parse.`);
}
}
getQuery(from: number, to: number, limit: number, offset: number): string {
let timeClause = `time >= ${from}ms AND time <= ${to}ms`;
return `${this._queryParts[0]} ${timeClause} ${this._queryParts[1]} LIMIT ${limit} OFFSET ${offset}`;
}
}

70
src/grafana-datasource-kit/grafana_service.ts

@ -1,70 +0,0 @@
import { GrafanaMetric } from './grafana_metric_model';
import { getApiKey } from '../config';
import { URL } from 'url';
import axios from 'axios';
const CHUNK_SIZE = 50000;
/**
* @param metric to query to Grafana
* @returns [time, value][] array
*/
export async function queryByMetric(
metric: GrafanaMetric, panelUrl: string, from: number, to: number
) {
let datasource = metric.datasource;
let origin = new URL(panelUrl).origin;
let url = `${origin}/${datasource.url}`;
let params = datasource.params
let data = {
values: [],
columns: []
};
let chunkParams = Object.assign({}, params);
while(true) {
chunkParams.q = metric.metricQuery.getQuery(from, to, CHUNK_SIZE, data.values.length);
var chunk = await queryGrafana(url, chunkParams);
let values = chunk.values;
data.values = data.values.concat(values);
data.columns = chunk.columns;
if(values.length < CHUNK_SIZE) {
// because if we get less that we could, then there is nothing more
break;
}
}
return data;
}
async function queryGrafana(url: string, params: any) {
let origin = new URL(url).origin;
let headers = { Authorization: `Bearer ${getApiKey(origin)}` };
try {
var res = await axios.get(url, { params, headers });
} catch (e) {
if(e.response.status === 401) {
throw new Error('Unauthorized. Check the $HASTIC_API_KEY.');
}
throw new Error(e.message);
}
if (res.data.results === undefined) {
throw new Error('results field is undefined in response.');
}
// TODO: support more than 1 metric (each res.data.results item is a metric)
let results = res.data.results[0];
if (results.series === undefined) {
return [];
}
return results.series[0];
}

14
src/target.ts

@ -1,10 +1,11 @@
import { queryByMetric } from './grafana-datasource-kit/grafana_service';
import { GrafanaDatasource, GrafanaMetric } from './grafana-datasource-kit/grafana_metric_model';
import { queryByMetric, Datasource, Metric } from 'grafana-datasource-kit';
import { getApiKey } from './config';
import * as csv from 'fast-csv';
import * as path from 'path';
import * as fs from 'fs';
import * as moment from 'moment';
import { URL } from 'url';
const MS_IN_DAY = 24 * 60 * 60 * 1000;
@ -14,17 +15,17 @@ export class Target {
private days: number;
private day: number;
private csvStream: any;
private metric: GrafanaMetric;
private metric: Metric;
constructor(
private panelUrl: string,
private user: string,
datasource: GrafanaDatasource,
datasource: Datasource,
targets: Array<Object>,
private from: number,
private to: number
) {
this.metric = new GrafanaMetric(datasource, targets);
this.metric = new Metric(datasource, targets);
}
public updateStatus(status) {
@ -64,7 +65,8 @@ export class Target {
console.log(`${this.day} day: ${from}ms -> ${to}ms`);
let metrics = await queryByMetric(this.metric, this.panelUrl, from, to);
let apiKey = getApiKey(new URL(this.panelUrl).origin);
let metrics = await queryByMetric(this.metric, this.panelUrl, from, to, apiKey);
if(metrics.values.length > 0) {
if(metrics !== undefined) {

Loading…
Cancel
Save