Browse Source
* integrate exporter with new version of datasource-kit * fix url for getting api key * Add grafana-datasource-kit to depspull/1/head
4 changed files with 9 additions and 164 deletions
@ -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}`; |
|
||||||
} |
|
||||||
} |
|
@ -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]; |
|
||||||
} |
|
Loading…
Reference in new issue