mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
AzureMonitor: Alerting for Azure Application Insights (#19381)
* Convert Azure Application Insights datasource to Go Allows for alerting of Application Insights data source Closes: #15153 * Fix timeGrainReset * Default time interval for querys for alerts * Fix a few rename related bugs * Update readme to indicate App Insights alerting * Fix typo and add tests to ensure migration is happening * Address code review feedback (mostly typos and unintended changes)
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import AzureMonitorDatasource from '../datasource';
|
||||
import Datasource from '../datasource';
|
||||
import { DataFrame, toUtc } from '@grafana/data';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
// @ts-ignore
|
||||
import Q from 'q';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { toUtc } from '@grafana/data';
|
||||
|
||||
describe('AppInsightsDatasource', () => {
|
||||
const ctx: any = {
|
||||
@@ -17,7 +17,7 @@ describe('AppInsightsDatasource', () => {
|
||||
url: 'http://appinsightsapi',
|
||||
};
|
||||
|
||||
ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings, ctx.backendSrv, ctx.templateSrv, ctx.$q);
|
||||
ctx.ds = new Datasource(ctx.instanceSettings, ctx.backendSrv, ctx.templateSrv, ctx.$q);
|
||||
});
|
||||
|
||||
describe('When performing testDatasource', () => {
|
||||
@@ -108,7 +108,121 @@ describe('AppInsightsDatasource', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('When performing query', () => {
|
||||
describe('When performing raw query', () => {
|
||||
const queryString =
|
||||
'metrics ' +
|
||||
'| where $__timeFilter(timestamp) ' +
|
||||
'| where name == "testMetrics" ' +
|
||||
'| summarize max=max(valueMax) by bin(timestamp, $__interval), partition';
|
||||
|
||||
const options = {
|
||||
range: {
|
||||
from: toUtc('2017-08-22T20:00:00Z'),
|
||||
to: toUtc('2017-08-22T23:59:00Z'),
|
||||
},
|
||||
targets: [
|
||||
{
|
||||
apiVersion: '2016-09-01',
|
||||
refId: 'A',
|
||||
queryType: 'Application Insights',
|
||||
appInsights: {
|
||||
rawQuery: true,
|
||||
rawQueryString: queryString,
|
||||
timeColumn: 'timestamp',
|
||||
valueColumn: 'max',
|
||||
segmentColumn: undefined as string,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
describe('with no grouping', () => {
|
||||
const response: any = {
|
||||
results: {
|
||||
A: {
|
||||
refId: 'A',
|
||||
meta: {},
|
||||
series: [
|
||||
{
|
||||
name: 'PrimaryResult',
|
||||
points: [[2.2075, 1558278660000]],
|
||||
},
|
||||
],
|
||||
tables: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
ctx.backendSrv.datasourceRequest = (options: any) => {
|
||||
expect(options.url).toContain('/api/tsdb/query');
|
||||
expect(options.data.queries.length).toBe(1);
|
||||
expect(options.data.queries[0].refId).toBe('A');
|
||||
expect(options.data.queries[0].appInsights.rawQueryString).toEqual(queryString);
|
||||
expect(options.data.queries[0].appInsights.timeColumn).toEqual('timestamp');
|
||||
expect(options.data.queries[0].appInsights.valueColumn).toEqual('max');
|
||||
expect(options.data.queries[0].appInsights.segmentColumn).toBeUndefined();
|
||||
return ctx.$q.when({ data: response, status: 200 });
|
||||
};
|
||||
});
|
||||
|
||||
it('should return a list of datapoints', () => {
|
||||
return ctx.ds.query(options).then((results: any) => {
|
||||
expect(results.data.length).toBe(1);
|
||||
const data = results.data[0] as DataFrame;
|
||||
expect(data.name).toEqual('PrimaryResult');
|
||||
expect(data.fields[0].values.length).toEqual(1);
|
||||
expect(data.fields[1].values.get(0)).toEqual(1558278660000);
|
||||
expect(data.fields[0].values.get(0)).toEqual(2.2075);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with grouping', () => {
|
||||
const response: any = {
|
||||
results: {
|
||||
A: {
|
||||
refId: 'A',
|
||||
meta: {},
|
||||
series: [
|
||||
{
|
||||
name: 'paritionA',
|
||||
points: [[2.2075, 1558278660000]],
|
||||
},
|
||||
],
|
||||
tables: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
options.targets[0].appInsights.segmentColumn = 'partition';
|
||||
ctx.backendSrv.datasourceRequest = (options: any) => {
|
||||
expect(options.url).toContain('/api/tsdb/query');
|
||||
expect(options.data.queries.length).toBe(1);
|
||||
expect(options.data.queries[0].refId).toBe('A');
|
||||
expect(options.data.queries[0].appInsights.rawQueryString).toEqual(queryString);
|
||||
expect(options.data.queries[0].appInsights.timeColumn).toEqual('timestamp');
|
||||
expect(options.data.queries[0].appInsights.valueColumn).toEqual('max');
|
||||
expect(options.data.queries[0].appInsights.segmentColumn).toEqual('partition');
|
||||
return ctx.$q.when({ data: response, status: 200 });
|
||||
};
|
||||
});
|
||||
|
||||
it('should return a list of datapoints', () => {
|
||||
return ctx.ds.query(options).then((results: any) => {
|
||||
expect(results.data.length).toBe(1);
|
||||
const data = results.data[0] as DataFrame;
|
||||
expect(data.name).toEqual('paritionA');
|
||||
expect(data.fields[0].values.length).toEqual(1);
|
||||
expect(data.fields[1].values.get(0)).toEqual(1558278660000);
|
||||
expect(data.fields[0].values.get(0)).toEqual(2.2075);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('When performing metric query', () => {
|
||||
const options = {
|
||||
range: {
|
||||
from: toUtc('2017-08-22T20:00:00Z'),
|
||||
@@ -121,30 +235,37 @@ describe('AppInsightsDatasource', () => {
|
||||
queryType: 'Application Insights',
|
||||
appInsights: {
|
||||
metricName: 'exceptions/server',
|
||||
groupBy: '',
|
||||
timeGrainType: 'none',
|
||||
timeGrain: '',
|
||||
timeGrainUnit: '',
|
||||
alias: '',
|
||||
dimension: '',
|
||||
timeGrain: 'none',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
describe('and with a single value', () => {
|
||||
const response = {
|
||||
value: {
|
||||
start: '2017-08-30T15:53:58.845Z',
|
||||
end: '2017-09-06T15:53:58.845Z',
|
||||
'exceptions/server': {
|
||||
sum: 100,
|
||||
const response: any = {
|
||||
results: {
|
||||
A: {
|
||||
refId: 'A',
|
||||
meta: {},
|
||||
series: [
|
||||
{
|
||||
name: 'exceptions/server',
|
||||
points: [[2.2075, 1558278660000]],
|
||||
},
|
||||
],
|
||||
tables: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
ctx.backendSrv.datasourceRequest = (options: { url: string }) => {
|
||||
expect(options.url).toContain('/metrics/exceptions/server');
|
||||
ctx.backendSrv.datasourceRequest = (options: any) => {
|
||||
expect(options.url).toContain('/api/tsdb/query');
|
||||
expect(options.data.queries.length).toBe(1);
|
||||
expect(options.data.queries[0].refId).toBe('A');
|
||||
expect(options.data.queries[0].appInsights.rawQueryString).toBeUndefined();
|
||||
expect(options.data.queries[0].appInsights.metricName).toBe('exceptions/server');
|
||||
return ctx.$q.when({ data: response, status: 200 });
|
||||
};
|
||||
});
|
||||
@@ -152,46 +273,39 @@ describe('AppInsightsDatasource', () => {
|
||||
it('should return a single datapoint', () => {
|
||||
return ctx.ds.query(options).then((results: any) => {
|
||||
expect(results.data.length).toBe(1);
|
||||
expect(results.data[0].datapoints.length).toBe(1);
|
||||
expect(results.data[0].target).toEqual('exceptions/server');
|
||||
expect(results.data[0].datapoints[0][1]).toEqual(1504713238845);
|
||||
expect(results.data[0].datapoints[0][0]).toEqual(100);
|
||||
const data = results.data[0] as DataFrame;
|
||||
expect(data.name).toEqual('exceptions/server');
|
||||
expect(data.fields[1].values.get(0)).toEqual(1558278660000);
|
||||
expect(data.fields[0].values.get(0)).toEqual(2.2075);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('and with an interval group and without a segment group by', () => {
|
||||
const response = {
|
||||
value: {
|
||||
start: '2017-08-30T15:53:58.845Z',
|
||||
end: '2017-09-06T15:53:58.845Z',
|
||||
interval: 'PT1H',
|
||||
segments: [
|
||||
{
|
||||
start: '2017-08-30T15:53:58.845Z',
|
||||
end: '2017-08-30T16:00:00.000Z',
|
||||
'exceptions/server': {
|
||||
sum: 3,
|
||||
const response: any = {
|
||||
results: {
|
||||
A: {
|
||||
refId: 'A',
|
||||
meta: {},
|
||||
series: [
|
||||
{
|
||||
name: 'exceptions/server',
|
||||
points: [[3, 1504108800000], [6, 1504112400000]],
|
||||
},
|
||||
},
|
||||
{
|
||||
start: '2017-08-30T16:00:00.000Z',
|
||||
end: '2017-08-30T17:00:00.000Z',
|
||||
'exceptions/server': {
|
||||
sum: 66,
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
tables: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
options.targets[0].appInsights.timeGrainType = 'specific';
|
||||
options.targets[0].appInsights.timeGrain = '30';
|
||||
options.targets[0].appInsights.timeGrainUnit = 'minute';
|
||||
ctx.backendSrv.datasourceRequest = (options: { url: string }) => {
|
||||
expect(options.url).toContain('/metrics/exceptions/server');
|
||||
expect(options.url).toContain('interval=PT30M');
|
||||
options.targets[0].appInsights.timeGrain = 'PT30M';
|
||||
ctx.backendSrv.datasourceRequest = (options: any) => {
|
||||
expect(options.url).toContain('/api/tsdb/query');
|
||||
expect(options.data.queries[0].refId).toBe('A');
|
||||
expect(options.data.queries[0].appInsights.rawQueryString).toBeUndefined();
|
||||
expect(options.data.queries[0].appInsights.metricName).toBe('exceptions/server');
|
||||
expect(options.data.queries[0].appInsights.timeGrain).toBe('PT30M');
|
||||
return ctx.$q.when({ data: response, status: 200 });
|
||||
};
|
||||
});
|
||||
@@ -199,108 +313,68 @@ describe('AppInsightsDatasource', () => {
|
||||
it('should return a list of datapoints', () => {
|
||||
return ctx.ds.query(options).then((results: any) => {
|
||||
expect(results.data.length).toBe(1);
|
||||
expect(results.data[0].datapoints.length).toBe(2);
|
||||
expect(results.data[0].target).toEqual('exceptions/server');
|
||||
expect(results.data[0].datapoints[0][1]).toEqual(1504108800000);
|
||||
expect(results.data[0].datapoints[0][0]).toEqual(3);
|
||||
expect(results.data[0].datapoints[1][1]).toEqual(1504112400000);
|
||||
expect(results.data[0].datapoints[1][0]).toEqual(66);
|
||||
const data = results.data[0] as DataFrame;
|
||||
expect(data.name).toEqual('exceptions/server');
|
||||
expect(data.fields[0].values.length).toEqual(2);
|
||||
expect(data.fields[1].values.get(0)).toEqual(1504108800000);
|
||||
expect(data.fields[0].values.get(0)).toEqual(3);
|
||||
expect(data.fields[1].values.get(1)).toEqual(1504112400000);
|
||||
expect(data.fields[0].values.get(1)).toEqual(6);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('and with a group by', () => {
|
||||
const response = {
|
||||
value: {
|
||||
start: '2017-08-30T15:53:58.845Z',
|
||||
end: '2017-09-06T15:53:58.845Z',
|
||||
interval: 'PT1H',
|
||||
segments: [
|
||||
{
|
||||
start: '2017-08-30T15:53:58.845Z',
|
||||
end: '2017-08-30T16:00:00.000Z',
|
||||
segments: [
|
||||
{
|
||||
'exceptions/server': {
|
||||
sum: 10,
|
||||
},
|
||||
'client/city': 'Miami',
|
||||
},
|
||||
{
|
||||
'exceptions/server': {
|
||||
sum: 1,
|
||||
},
|
||||
'client/city': 'San Jose',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
start: '2017-08-30T16:00:00.000Z',
|
||||
end: '2017-08-30T17:00:00.000Z',
|
||||
segments: [
|
||||
{
|
||||
'exceptions/server': {
|
||||
sum: 20,
|
||||
},
|
||||
'client/city': 'Miami',
|
||||
},
|
||||
{
|
||||
'exceptions/server': {
|
||||
sum: 2,
|
||||
},
|
||||
'client/city': 'San Antonio',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
const response: any = {
|
||||
results: {
|
||||
A: {
|
||||
refId: 'A',
|
||||
meta: {},
|
||||
series: [
|
||||
{
|
||||
name: 'exceptions/server{client/city="Miami"}',
|
||||
points: [[10, 1504108800000], [20, 1504112400000]],
|
||||
},
|
||||
{
|
||||
name: 'exceptions/server{client/city="San Antonio"}',
|
||||
points: [[1, 1504108800000], [2, 1504112400000]],
|
||||
},
|
||||
],
|
||||
tables: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
describe('and with no alias specified', () => {
|
||||
beforeEach(() => {
|
||||
options.targets[0].appInsights.groupBy = 'client/city';
|
||||
options.targets[0].appInsights.dimension = 'client/city';
|
||||
|
||||
ctx.backendSrv.datasourceRequest = (options: { url: string }) => {
|
||||
expect(options.url).toContain('/metrics/exceptions/server');
|
||||
expect(options.url).toContain('segment=client/city');
|
||||
ctx.backendSrv.datasourceRequest = (options: any) => {
|
||||
expect(options.url).toContain('/api/tsdb/query');
|
||||
expect(options.data.queries[0].appInsights.rawQueryString).toBeUndefined();
|
||||
expect(options.data.queries[0].appInsights.metricName).toBe('exceptions/server');
|
||||
expect(options.data.queries[0].appInsights.dimension).toBe('client/city');
|
||||
return ctx.$q.when({ data: response, status: 200 });
|
||||
};
|
||||
});
|
||||
|
||||
it('should return a list of datapoints', () => {
|
||||
return ctx.ds.query(options).then((results: any) => {
|
||||
expect(results.data.length).toBe(3);
|
||||
expect(results.data[0].datapoints.length).toBe(2);
|
||||
expect(results.data[0].target).toEqual('exceptions/server{client/city="Miami"}');
|
||||
expect(results.data[0].datapoints[0][1]).toEqual(1504108800000);
|
||||
expect(results.data[0].datapoints[0][0]).toEqual(10);
|
||||
expect(results.data[0].datapoints[1][1]).toEqual(1504112400000);
|
||||
expect(results.data[0].datapoints[1][0]).toEqual(20);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('and with an alias specified', () => {
|
||||
beforeEach(() => {
|
||||
options.targets[0].appInsights.groupBy = 'client/city';
|
||||
options.targets[0].appInsights.alias = '{{metric}} + {{groupbyname}} + {{groupbyvalue}}';
|
||||
|
||||
ctx.backendSrv.datasourceRequest = (options: { url: string }) => {
|
||||
expect(options.url).toContain('/metrics/exceptions/server');
|
||||
expect(options.url).toContain('segment=client/city');
|
||||
return ctx.$q.when({ data: response, status: 200 });
|
||||
};
|
||||
});
|
||||
|
||||
it('should return a list of datapoints', () => {
|
||||
return ctx.ds.query(options).then((results: any) => {
|
||||
expect(results.data.length).toBe(3);
|
||||
expect(results.data[0].datapoints.length).toBe(2);
|
||||
expect(results.data[0].target).toEqual('exceptions/server + client/city + Miami');
|
||||
expect(results.data[0].datapoints[0][1]).toEqual(1504108800000);
|
||||
expect(results.data[0].datapoints[0][0]).toEqual(10);
|
||||
expect(results.data[0].datapoints[1][1]).toEqual(1504112400000);
|
||||
expect(results.data[0].datapoints[1][0]).toEqual(20);
|
||||
expect(results.data.length).toBe(2);
|
||||
let data = results.data[0] as DataFrame;
|
||||
expect(data.name).toEqual('exceptions/server{client/city="Miami"}');
|
||||
expect(data.fields[0].values.length).toEqual(2);
|
||||
expect(data.fields[1].values.get(0)).toEqual(1504108800000);
|
||||
expect(data.fields[0].values.get(0)).toEqual(10);
|
||||
expect(data.fields[1].values.get(1)).toEqual(1504112400000);
|
||||
expect(data.fields[0].values.get(1)).toEqual(20);
|
||||
data = results.data[1] as DataFrame;
|
||||
expect(data.name).toEqual('exceptions/server{client/city="San Antonio"}');
|
||||
expect(data.fields[0].values.length).toEqual(2);
|
||||
expect(data.fields[1].values.get(0)).toEqual(1504108800000);
|
||||
expect(data.fields[0].values.get(0)).toEqual(1);
|
||||
expect(data.fields[1].values.get(1)).toEqual(1504112400000);
|
||||
expect(data.fields[0].values.get(1)).toEqual(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import _ from 'lodash';
|
||||
import AppInsightsQuerystringBuilder from './app_insights_querystring_builder';
|
||||
import LogAnalyticsQuerystringBuilder from '../log_analytics/querystring_builder';
|
||||
import ResponseParser from './response_parser';
|
||||
import { DataSourceInstanceSettings } from '@grafana/ui';
|
||||
import { AzureDataSourceJsonData } from '../types';
|
||||
import { TimeSeries, toDataFrame } from '@grafana/data';
|
||||
import { DataQueryRequest, DataQueryResponseData, DataSourceInstanceSettings } from '@grafana/ui';
|
||||
import { BackendSrv } from 'app/core/services/backend_srv';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { IQService } from 'angular';
|
||||
import _ from 'lodash';
|
||||
|
||||
import TimegrainConverter from '../time_grain_converter';
|
||||
import { AzureDataSourceJsonData, AzureMonitorQuery } from '../types';
|
||||
import ResponseParser from './response_parser';
|
||||
|
||||
export interface LogAnalyticsColumn {
|
||||
text: string;
|
||||
@@ -24,8 +24,7 @@ export default class AppInsightsDatasource {
|
||||
constructor(
|
||||
instanceSettings: DataSourceInstanceSettings<AzureDataSourceJsonData>,
|
||||
private backendSrv: BackendSrv,
|
||||
private templateSrv: TemplateSrv,
|
||||
private $q: IQService
|
||||
private templateSrv: TemplateSrv
|
||||
) {
|
||||
this.id = instanceSettings.id;
|
||||
this.applicationId = instanceSettings.jsonData.appInsightsAppId;
|
||||
@@ -37,73 +36,82 @@ export default class AppInsightsDatasource {
|
||||
return !!this.applicationId && this.applicationId.length > 0;
|
||||
}
|
||||
|
||||
query(options: any) {
|
||||
createRawQueryRequest(item: any, options: DataQueryRequest<AzureMonitorQuery>, target: AzureMonitorQuery) {
|
||||
if (item.xaxis && !item.timeColumn) {
|
||||
item.timeColumn = item.xaxis;
|
||||
}
|
||||
|
||||
if (item.yaxis && !item.valueColumn) {
|
||||
item.valueColumn = item.yaxis;
|
||||
}
|
||||
|
||||
if (item.spliton && !item.segmentColumn) {
|
||||
item.segmentColumn = item.spliton;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'timeSeriesQuery',
|
||||
raw: false,
|
||||
appInsights: {
|
||||
rawQuery: true,
|
||||
rawQueryString: this.templateSrv.replace(item.rawQueryString, options.scopedVars),
|
||||
timeColumn: item.timeColumn,
|
||||
valueColumn: item.valueColumn,
|
||||
segmentColumn: item.segmentColumn,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
createMetricsRequest(item: any, options: DataQueryRequest<AzureMonitorQuery>, target: AzureMonitorQuery) {
|
||||
// fix for timeGrainUnit which is a deprecated/removed field name
|
||||
if (item.timeGrainCount) {
|
||||
item.timeGrain = TimegrainConverter.createISO8601Duration(item.timeGrainCount, item.timeGrainUnit);
|
||||
} else if (item.timeGrainUnit && item.timeGrain !== 'auto') {
|
||||
item.timeGrain = TimegrainConverter.createISO8601Duration(item.timeGrain, item.timeGrainUnit);
|
||||
}
|
||||
|
||||
// migration for non-standard names
|
||||
if (item.groupBy && !item.dimension) {
|
||||
item.dimension = item.groupBy;
|
||||
}
|
||||
|
||||
if (item.filter && !item.dimensionFilter) {
|
||||
item.dimensionFilter = item.filter;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'timeSeriesQuery',
|
||||
raw: false,
|
||||
appInsights: {
|
||||
rawQuery: false,
|
||||
timeGrain: this.templateSrv.replace((item.timeGrain || '').toString(), options.scopedVars),
|
||||
allowedTimeGrainsMs: item.allowedTimeGrainsMs,
|
||||
metricName: this.templateSrv.replace(item.metricName, options.scopedVars),
|
||||
aggregation: this.templateSrv.replace(item.aggregation, options.scopedVars),
|
||||
dimension: this.templateSrv.replace(item.dimension, options.scopedVars),
|
||||
dimensionFilter: this.templateSrv.replace(item.dimensionFilter, options.scopedVars),
|
||||
alias: item.alias,
|
||||
format: target.format,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async query(options: DataQueryRequest<AzureMonitorQuery>): Promise<DataQueryResponseData[]> {
|
||||
const queries = _.filter(options.targets, item => {
|
||||
return item.hide !== true;
|
||||
}).map(target => {
|
||||
}).map((target: AzureMonitorQuery) => {
|
||||
const item = target.appInsights;
|
||||
let query: any;
|
||||
if (item.rawQuery) {
|
||||
const querystringBuilder = new LogAnalyticsQuerystringBuilder(
|
||||
this.templateSrv.replace(item.rawQueryString, options.scopedVars),
|
||||
options,
|
||||
'timestamp'
|
||||
);
|
||||
const generated = querystringBuilder.generate();
|
||||
|
||||
const url = `${this.baseUrl}/query?${generated.uriString}`;
|
||||
|
||||
return {
|
||||
refId: target.refId,
|
||||
intervalMs: options.intervalMs,
|
||||
maxDataPoints: options.maxDataPoints,
|
||||
datasourceId: this.id,
|
||||
url: url,
|
||||
format: options.format,
|
||||
alias: item.alias,
|
||||
query: generated.rawQuery,
|
||||
xaxis: item.xaxis,
|
||||
yaxis: item.yaxis,
|
||||
spliton: item.spliton,
|
||||
raw: true,
|
||||
};
|
||||
query = this.createRawQueryRequest(item, options, target);
|
||||
} else {
|
||||
const querystringBuilder = new AppInsightsQuerystringBuilder(
|
||||
options.range.from,
|
||||
options.range.to,
|
||||
options.interval
|
||||
);
|
||||
|
||||
if (item.groupBy !== 'none') {
|
||||
querystringBuilder.setGroupBy(this.templateSrv.replace(item.groupBy, options.scopedVars));
|
||||
}
|
||||
querystringBuilder.setAggregation(item.aggregation);
|
||||
querystringBuilder.setInterval(
|
||||
item.timeGrainType,
|
||||
this.templateSrv.replace(item.timeGrain, options.scopedVars),
|
||||
item.timeGrainUnit
|
||||
);
|
||||
|
||||
querystringBuilder.setFilter(this.templateSrv.replace(item.filter || ''));
|
||||
|
||||
const url = `${this.baseUrl}/metrics/${this.templateSrv.replace(
|
||||
encodeURI(item.metricName),
|
||||
options.scopedVars
|
||||
)}?${querystringBuilder.generate()}`;
|
||||
|
||||
return {
|
||||
refId: target.refId,
|
||||
intervalMs: options.intervalMs,
|
||||
maxDataPoints: options.maxDataPoints,
|
||||
datasourceId: this.id,
|
||||
url: url,
|
||||
format: options.format,
|
||||
alias: item.alias,
|
||||
xaxis: '',
|
||||
yaxis: '',
|
||||
spliton: '',
|
||||
raw: false,
|
||||
};
|
||||
query = this.createMetricsRequest(item, options, target);
|
||||
}
|
||||
query.refId = target.refId;
|
||||
query.intervalMs = options.intervalMs;
|
||||
query.datasourceId = this.id;
|
||||
query.queryType = 'Application Insights';
|
||||
return query;
|
||||
});
|
||||
|
||||
if (!queries || queries.length === 0) {
|
||||
@@ -111,25 +119,42 @@ export default class AppInsightsDatasource {
|
||||
return;
|
||||
}
|
||||
|
||||
const promises = this.doQueries(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,
|
||||
},
|
||||
});
|
||||
|
||||
return this.$q
|
||||
.all(promises)
|
||||
.then(results => {
|
||||
return new ResponseParser(results).parseQueryResult();
|
||||
})
|
||||
.then(results => {
|
||||
const flattened: any[] = [];
|
||||
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
if (results[i].columnsForDropdown) {
|
||||
this.logAnalyticsColumns[results[i].refId] = results[i].columnsForDropdown;
|
||||
}
|
||||
flattened.push(results[i]);
|
||||
const result: DataQueryResponseData[] = [];
|
||||
if (data.results) {
|
||||
Object.values(data.results).forEach((queryRes: any) => {
|
||||
if (queryRes.meta && queryRes.meta.columns) {
|
||||
const columnNames = queryRes.meta.columns as string[];
|
||||
this.logAnalyticsColumns[queryRes.refId] = _.map(columnNames, n => ({ text: n, value: n }));
|
||||
}
|
||||
|
||||
return flattened;
|
||||
if (!queryRes.series) {
|
||||
return;
|
||||
}
|
||||
|
||||
queryRes.series.forEach((series: any) => {
|
||||
const timeSerie: TimeSeries = {
|
||||
target: series.name,
|
||||
datapoints: series.points,
|
||||
refId: queryRes.refId,
|
||||
meta: queryRes.meta,
|
||||
};
|
||||
result.push(toDataFrame(timeSerie));
|
||||
});
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
doQueries(queries: any) {
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
import AppInsightsQuerystringBuilder from './app_insights_querystring_builder';
|
||||
import { toUtc } from '@grafana/data';
|
||||
|
||||
describe('AppInsightsQuerystringBuilder', () => {
|
||||
let builder: AppInsightsQuerystringBuilder;
|
||||
|
||||
beforeEach(() => {
|
||||
builder = new AppInsightsQuerystringBuilder(toUtc('2017-08-22 06:00'), toUtc('2017-08-22 07:00'), '1h');
|
||||
});
|
||||
|
||||
describe('with only from/to date range', () => {
|
||||
it('should always add datetime filtering to the querystring', () => {
|
||||
const querystring = `timespan=2017-08-22T06:00:00Z/2017-08-22T07:00:00Z`;
|
||||
expect(builder.generate()).toEqual(querystring);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with from/to date range and aggregation type', () => {
|
||||
beforeEach(() => {
|
||||
builder.setAggregation('avg');
|
||||
});
|
||||
|
||||
it('should add datetime filtering and aggregation to the querystring', () => {
|
||||
const querystring = `timespan=2017-08-22T06:00:00Z/2017-08-22T07:00:00Z&aggregation=avg`;
|
||||
expect(builder.generate()).toEqual(querystring);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with from/to date range and group by segment', () => {
|
||||
beforeEach(() => {
|
||||
builder.setGroupBy('client/city');
|
||||
});
|
||||
|
||||
it('should add datetime filtering and segment to the querystring', () => {
|
||||
const querystring = `timespan=2017-08-22T06:00:00Z/2017-08-22T07:00:00Z&segment=client/city`;
|
||||
expect(builder.generate()).toEqual(querystring);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with from/to date range and specific group by interval', () => {
|
||||
beforeEach(() => {
|
||||
builder.setInterval('specific', 1, 'hour');
|
||||
});
|
||||
|
||||
it('should add datetime filtering and interval to the querystring', () => {
|
||||
const querystring = `timespan=2017-08-22T06:00:00Z/2017-08-22T07:00:00Z&interval=PT1H`;
|
||||
expect(builder.generate()).toEqual(querystring);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with from/to date range and auto group by interval', () => {
|
||||
beforeEach(() => {
|
||||
builder.setInterval('auto', '', '');
|
||||
});
|
||||
|
||||
it('should add datetime filtering and interval to the querystring', () => {
|
||||
const querystring = `timespan=2017-08-22T06:00:00Z/2017-08-22T07:00:00Z&interval=PT1H`;
|
||||
expect(builder.generate()).toEqual(querystring);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with filter', () => {
|
||||
beforeEach(() => {
|
||||
builder.setFilter(`client/city eq 'Boydton'`);
|
||||
});
|
||||
|
||||
it('should add datetime filtering and interval to the querystring', () => {
|
||||
const querystring = `timespan=2017-08-22T06:00:00Z/2017-08-22T07:00:00Z&filter=client/city eq 'Boydton'`;
|
||||
expect(builder.generate()).toEqual(querystring);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,56 +0,0 @@
|
||||
import TimeGrainConverter from '../time_grain_converter';
|
||||
|
||||
export default class AppInsightsQuerystringBuilder {
|
||||
aggregation = '';
|
||||
groupBy = '';
|
||||
timeGrainType = '';
|
||||
timeGrain = '';
|
||||
timeGrainUnit = '';
|
||||
filter = '';
|
||||
|
||||
constructor(private from: any, private to: any, public grafanaInterval: any) {}
|
||||
|
||||
setAggregation(aggregation: string) {
|
||||
this.aggregation = aggregation;
|
||||
}
|
||||
|
||||
setGroupBy(groupBy: string) {
|
||||
this.groupBy = groupBy;
|
||||
}
|
||||
|
||||
setInterval(timeGrainType: string, timeGrain: any, timeGrainUnit: string) {
|
||||
this.timeGrainType = timeGrainType;
|
||||
this.timeGrain = timeGrain;
|
||||
this.timeGrainUnit = timeGrainUnit;
|
||||
}
|
||||
|
||||
setFilter(filter: string) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
generate() {
|
||||
let querystring = `timespan=${this.from.utc().format()}/${this.to.utc().format()}`;
|
||||
|
||||
if (this.aggregation && this.aggregation.length > 0) {
|
||||
querystring += `&aggregation=${this.aggregation}`;
|
||||
}
|
||||
|
||||
if (this.groupBy && this.groupBy.length > 0) {
|
||||
querystring += `&segment=${this.groupBy}`;
|
||||
}
|
||||
|
||||
if (this.timeGrainType === 'specific' && this.timeGrain && this.timeGrainUnit) {
|
||||
querystring += `&interval=${TimeGrainConverter.createISO8601Duration(this.timeGrain, this.timeGrainUnit)}`;
|
||||
}
|
||||
|
||||
if (this.timeGrainType === 'auto') {
|
||||
querystring += `&interval=${TimeGrainConverter.createISO8601DurationFromInterval(this.grafanaInterval)}`;
|
||||
}
|
||||
|
||||
if (this.filter) {
|
||||
querystring += `&filter=${this.filter}`;
|
||||
}
|
||||
|
||||
return querystring;
|
||||
}
|
||||
}
|
||||
@@ -22,12 +22,7 @@ export default class Datasource extends DataSourceApi<AzureMonitorQuery, AzureDa
|
||||
) {
|
||||
super(instanceSettings);
|
||||
this.azureMonitorDatasource = new AzureMonitorDatasource(instanceSettings, this.backendSrv, this.templateSrv);
|
||||
this.appInsightsDatasource = new AppInsightsDatasource(
|
||||
instanceSettings,
|
||||
this.backendSrv,
|
||||
this.templateSrv,
|
||||
this.$q
|
||||
);
|
||||
this.appInsightsDatasource = new AppInsightsDatasource(instanceSettings, this.backendSrv, this.templateSrv);
|
||||
|
||||
this.azureLogAnalyticsDatasource = new AzureLogAnalyticsDatasource(
|
||||
instanceSettings,
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
get-options="ctrl.getMetricNamespaces($query)" on-change="ctrl.onMetricNamespacesChange()" css-class="min-width-12">
|
||||
</gf-form-dropdown>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-9">Metric</label>
|
||||
<gf-form-dropdown model="ctrl.target.azureMonitor.metricName" allow-custom="true" lookup-text="true"
|
||||
get-options="ctrl.getMetricNames($query)" on-change="ctrl.onMetricNameChange()" css-class="min-width-12">
|
||||
@@ -62,7 +62,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-9">Time Grain</label>
|
||||
<div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent timegrainunit-dropdown-wrapper">
|
||||
@@ -72,7 +72,7 @@
|
||||
</div>
|
||||
<div class="gf-form" ng-show="ctrl.target.azureMonitor.timeGrain.trim() === 'auto'">
|
||||
<label class="gf-form-label">Auto Interval</label>
|
||||
<label class="gf-form-label">{{ctrl.getAutoInterval()}}</label>
|
||||
<label class="gf-form-label">{{ctrl.getAzureMonitorAutoInterval()}}</label>
|
||||
</div>
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
@@ -238,19 +238,19 @@
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-9">Group By</label>
|
||||
<gf-form-dropdown allow-custom="true" ng-hide="ctrl.target.appInsights.groupBy !== 'none'" model="ctrl.target.appInsights.groupBy"
|
||||
<gf-form-dropdown allow-custom="true" ng-hide="ctrl.target.appInsights.dimension !== 'none'" model="ctrl.target.appInsights.dimension"
|
||||
lookup-text="true" get-options="ctrl.getAppInsightsGroupBySegments($query)" on-change="ctrl.refresh()"
|
||||
css-class="min-width-20">
|
||||
</gf-form-dropdown>
|
||||
<label class="gf-form-label min-width-20 pointer" ng-hide="ctrl.target.appInsights.groupBy === 'none'"
|
||||
ng-click="ctrl.resetAppInsightsGroupBy()">{{ctrl.target.appInsights.groupBy}}
|
||||
<label class="gf-form-label min-width-20 pointer" ng-hide="ctrl.target.appInsights.dimension === 'none'"
|
||||
ng-click="ctrl.resetAppInsightsGroupBy()">{{ctrl.target.appInsights.dimension}}
|
||||
<i class="fa fa-remove"></i>
|
||||
</label>
|
||||
</div>
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-9">Filter</label>
|
||||
<input type="text" class="gf-form-input width-17" ng-model="ctrl.target.appInsights.filter" spellcheck="false"
|
||||
<input type="text" class="gf-form-input width-17" ng-model="ctrl.target.appInsights.dimensionFilter" spellcheck="false"
|
||||
placeholder="your/groupby eq 'a_value'" ng-blur="ctrl.refresh()">
|
||||
</div>
|
||||
</div>
|
||||
@@ -258,7 +258,6 @@
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-9">Time Grain</label>
|
||||
@@ -268,17 +267,17 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form" ng-hide="ctrl.target.appInsights.timeGrainType === 'auto' || ctrl.target.appInsights.timeGrainType === 'none'">
|
||||
<input type="text" class="gf-form-input width-3" ng-model="ctrl.target.appInsights.timeGrain" spellcheck="false"
|
||||
placeholder="" ng-blur="ctrl.refresh()">
|
||||
<input type="text" class="gf-form-input width-3" ng-model="ctrl.target.appInsights.timeGrainCount" spellcheck="false"
|
||||
placeholder="" ng-blur="ctrl.updateAppInsightsTimeGrain()">
|
||||
</div>
|
||||
<div class="gf-form" ng-hide="ctrl.target.appInsights.timeGrainType === 'auto' || ctrl.target.appInsights.timeGrainType === 'none'">
|
||||
<div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent timegrainunit-dropdown-wrapper">
|
||||
<div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent timegrainunit-dropdown-wrapper">
|
||||
<select class="gf-form-input" ng-model="ctrl.target.appInsights.timeGrainUnit" ng-options="f as f for f in ['minute', 'hour', 'day', 'month', 'year']"
|
||||
ng-change="ctrl.refresh()"></select>
|
||||
ng-change="ctrl.updateAppInsightsTimeGrain()"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form" ng-hide="ctrl.target.appInsights.timeGrainType !== 'auto'">
|
||||
<label class="gf-form-label">Auto Interval</label>
|
||||
<label class="gf-form-label">Auto Interval</label>
|
||||
<label class="gf-form-label">{{ctrl.getAppInsightsAutoInterval()}}</label>
|
||||
</div>
|
||||
<div class="gf-form gf-form--grow">
|
||||
@@ -291,10 +290,9 @@
|
||||
<input type="text" class="gf-form-input width-30" ng-model="ctrl.target.appInsights.alias" spellcheck="false"
|
||||
placeholder="alias patterns (see help for more info)" ng-blur="ctrl.refresh()">
|
||||
</div>
|
||||
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="ctrl.target.appInsights.rawQuery">
|
||||
@@ -316,13 +314,13 @@
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-9">X-axis</label>
|
||||
<gf-form-dropdown model="ctrl.target.appInsights.xaxis" allow-custom="true" placeholder="eg. 'timestamp'"
|
||||
<gf-form-dropdown model="ctrl.target.appInsights.timeColumn" allow-custom="true" placeholder="eg. 'timestamp'"
|
||||
get-options="ctrl.getAppInsightsColumns($query)" on-change="ctrl.onAppInsightsColumnChange()" css-class="min-width-20">
|
||||
</gf-form-dropdown>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-9">Y-axis(es)</label>
|
||||
<gf-form-dropdown model="ctrl.target.appInsights.yaxis" allow-custom="true" get-options="ctrl.getAppInsightsColumns($query)"
|
||||
<label class="gf-form-label query-keyword width-9">Y-axis</label>
|
||||
<gf-form-dropdown model="ctrl.target.appInsights.valueColumn" allow-custom="true" get-options="ctrl.getAppInsightsColumns($query)"
|
||||
on-change="ctrl.onAppInsightsColumnChange()" css-class="min-width-20">
|
||||
</gf-form-dropdown>
|
||||
</div>
|
||||
@@ -333,7 +331,7 @@
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label query-keyword width-9">Split On</label>
|
||||
<gf-form-dropdown model="ctrl.target.appInsights.spliton" allow-custom="true" get-options="ctrl.getAppInsightsColumns($query)"
|
||||
<gf-form-dropdown model="ctrl.target.appInsights.segmentColumn" allow-custom="true" get-options="ctrl.getAppInsightsColumns($query)"
|
||||
on-change="ctrl.onAppInsightsColumnChange()" css-class="min-width-20">
|
||||
</gf-form-dropdown>
|
||||
</div>
|
||||
|
||||
@@ -41,7 +41,7 @@ describe('AzureMonitorQueryCtrl', () => {
|
||||
expect(queryCtrl.target.azureMonitor.resourceName).toBe('select');
|
||||
expect(queryCtrl.target.azureMonitor.metricNamespace).toBe('select');
|
||||
expect(queryCtrl.target.azureMonitor.metricName).toBe('select');
|
||||
expect(queryCtrl.target.appInsights.groupBy).toBe('none');
|
||||
expect(queryCtrl.target.appInsights.dimension).toBe('none');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -239,6 +239,35 @@ describe('AzureMonitorQueryCtrl', () => {
|
||||
});
|
||||
|
||||
describe('and query type is Application Insights', () => {
|
||||
describe('and target is in old format', () => {
|
||||
it('data is migrated', () => {
|
||||
queryCtrl.target.appInsights.xaxis = 'sample-x';
|
||||
queryCtrl.target.appInsights.yaxis = 'sample-y';
|
||||
queryCtrl.target.appInsights.spliton = 'sample-split';
|
||||
queryCtrl.target.appInsights.groupBy = 'sample-group';
|
||||
queryCtrl.target.appInsights.groupByOptions = ['sample-group-1', 'sample-group-2'];
|
||||
queryCtrl.target.appInsights.filter = 'sample-filter';
|
||||
queryCtrl.target.appInsights.metricName = 'sample-metric';
|
||||
|
||||
queryCtrl.migrateApplicationInsightsKeys();
|
||||
|
||||
expect(queryCtrl.target.appInsights.xaxis).toBeUndefined();
|
||||
expect(queryCtrl.target.appInsights.yaxis).toBeUndefined();
|
||||
expect(queryCtrl.target.appInsights.spliton).toBeUndefined();
|
||||
expect(queryCtrl.target.appInsights.groupBy).toBeUndefined();
|
||||
expect(queryCtrl.target.appInsights.groupByOptions).toBeUndefined();
|
||||
expect(queryCtrl.target.appInsights.filter).toBeUndefined();
|
||||
|
||||
expect(queryCtrl.target.appInsights.timeColumn).toBe('sample-x');
|
||||
expect(queryCtrl.target.appInsights.valueColumn).toBe('sample-y');
|
||||
expect(queryCtrl.target.appInsights.segmentColumn).toBe('sample-split');
|
||||
expect(queryCtrl.target.appInsights.dimension).toBe('sample-group');
|
||||
expect(queryCtrl.target.appInsights.dimensions).toEqual(['sample-group-1', 'sample-group-2']);
|
||||
expect(queryCtrl.target.appInsights.dimensionFilter).toBe('sample-filter');
|
||||
expect(queryCtrl.target.appInsights.metricName).toBe('sample-metric');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when getOptions for the Metric Names dropdown is called', () => {
|
||||
const response = [{ text: 'metric1', value: 'metric1' }, { text: 'metric2', value: 'metric2' }];
|
||||
|
||||
@@ -259,7 +288,7 @@ describe('AzureMonitorQueryCtrl', () => {
|
||||
|
||||
describe('when getOptions for the GroupBy segments dropdown is called', () => {
|
||||
beforeEach(() => {
|
||||
queryCtrl.target.appInsights.groupByOptions = ['opt1', 'opt2'];
|
||||
queryCtrl.target.appInsights.dimensions = ['opt1', 'opt2'];
|
||||
});
|
||||
|
||||
it('should return a list of GroupBy segments', () => {
|
||||
@@ -291,8 +320,8 @@ describe('AzureMonitorQueryCtrl', () => {
|
||||
expect(queryCtrl.target.appInsights.aggregation).toBe('avg');
|
||||
expect(queryCtrl.target.appInsights.aggOptions).toContain('avg');
|
||||
expect(queryCtrl.target.appInsights.aggOptions).toContain('sum');
|
||||
expect(queryCtrl.target.appInsights.groupByOptions).toContain('client/os');
|
||||
expect(queryCtrl.target.appInsights.groupByOptions).toContain('client/city');
|
||||
expect(queryCtrl.target.appInsights.dimensions).toContain('client/os');
|
||||
expect(queryCtrl.target.appInsights.dimensions).toContain('client/city');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -32,13 +32,13 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
||||
dimensionFilter: string;
|
||||
timeGrain: string;
|
||||
timeGrainUnit: string;
|
||||
timeGrains: Array<{ text: string; value: string }>;
|
||||
allowedTimeGrainsMs: number[];
|
||||
dimensions: any[];
|
||||
dimension: any;
|
||||
top: string;
|
||||
aggregation: string;
|
||||
aggOptions: string[];
|
||||
timeGrains: Array<{ text: string; value: string }>;
|
||||
};
|
||||
azureLogAnalytics: {
|
||||
query: string;
|
||||
@@ -46,19 +46,28 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
||||
workspace: string;
|
||||
};
|
||||
appInsights: {
|
||||
metricName: string;
|
||||
rawQuery: boolean;
|
||||
rawQueryString: string;
|
||||
groupBy: string;
|
||||
timeGrainType: string;
|
||||
xaxis: string;
|
||||
yaxis: string;
|
||||
spliton: string;
|
||||
// metric style query when rawQuery == false
|
||||
metricName: string;
|
||||
dimension: any;
|
||||
dimensionFilter: string;
|
||||
dimensions: string[];
|
||||
|
||||
aggOptions: string[];
|
||||
aggregation: string;
|
||||
groupByOptions: string[];
|
||||
|
||||
timeGrainType: string;
|
||||
timeGrainCount: string;
|
||||
timeGrainUnit: string;
|
||||
timeGrain: string;
|
||||
timeGrains: Array<{ text: string; value: string }>;
|
||||
allowedTimeGrainsMs: number[];
|
||||
|
||||
// query style query when rawQuery == true
|
||||
rawQueryString: string;
|
||||
timeColumn: string;
|
||||
valueColumn: string;
|
||||
segmentColumn: string;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -73,6 +82,8 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
||||
dimensionFilter: '*',
|
||||
timeGrain: 'auto',
|
||||
top: '10',
|
||||
aggOptions: [] as string[],
|
||||
timeGrains: [] as string[],
|
||||
},
|
||||
azureLogAnalytics: {
|
||||
query: [
|
||||
@@ -96,11 +107,10 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
||||
metricName: this.defaultDropdownValue,
|
||||
rawQuery: false,
|
||||
rawQueryString: '',
|
||||
groupBy: 'none',
|
||||
timeGrainType: 'auto',
|
||||
xaxis: 'timestamp',
|
||||
yaxis: '',
|
||||
spliton: '',
|
||||
dimension: 'none',
|
||||
timeGrain: 'auto',
|
||||
timeColumn: 'timestamp',
|
||||
valueColumn: '',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -124,6 +134,8 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
||||
|
||||
this.migrateToDefaultNamespace();
|
||||
|
||||
this.migrateApplicationInsightsKeys();
|
||||
|
||||
this.panelCtrl.events.on('data-received', this.onDataReceived.bind(this), $scope);
|
||||
this.panelCtrl.events.on('data-error', this.onDataError.bind(this), $scope);
|
||||
this.resultFormats = [{ text: 'Time series', value: 'time_series' }, { text: 'Table', value: 'table' }];
|
||||
@@ -184,6 +196,23 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
||||
this.onMetricNameChange();
|
||||
}
|
||||
|
||||
if (this.target.appInsights.timeGrainUnit) {
|
||||
if (this.target.appInsights.timeGrain !== 'auto') {
|
||||
if (this.target.appInsights.timeGrainCount) {
|
||||
this.target.appInsights.timeGrain = TimegrainConverter.createISO8601Duration(
|
||||
this.target.appInsights.timeGrainCount,
|
||||
this.target.appInsights.timeGrainUnit
|
||||
);
|
||||
} else {
|
||||
this.target.appInsights.timeGrainCount = this.target.appInsights.timeGrain;
|
||||
this.target.appInsights.timeGrain = TimegrainConverter.createISO8601Duration(
|
||||
this.target.appInsights.timeGrain,
|
||||
this.target.appInsights.timeGrainUnit
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
this.target.azureMonitor.timeGrains &&
|
||||
this.target.azureMonitor.timeGrains.length > 0 &&
|
||||
@@ -191,6 +220,14 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
||||
) {
|
||||
this.target.azureMonitor.allowedTimeGrainsMs = this.convertTimeGrainsToMs(this.target.azureMonitor.timeGrains);
|
||||
}
|
||||
|
||||
if (
|
||||
this.target.appInsights.timeGrains &&
|
||||
this.target.appInsights.timeGrains.length > 0 &&
|
||||
(!this.target.appInsights.allowedTimeGrainsMs || this.target.appInsights.allowedTimeGrainsMs.length === 0)
|
||||
) {
|
||||
this.target.appInsights.allowedTimeGrainsMs = this.convertTimeGrainsToMs(this.target.appInsights.timeGrains);
|
||||
}
|
||||
}
|
||||
|
||||
migrateToFromTimes() {
|
||||
@@ -210,6 +247,27 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
||||
this.target.azureMonitor.metricNamespace = this.target.azureMonitor.metricDefinition;
|
||||
}
|
||||
|
||||
migrateApplicationInsightsKeys(): void {
|
||||
const appInsights = this.target.appInsights as any;
|
||||
|
||||
// Migrate old app insights data keys to match other datasources
|
||||
const mappings = {
|
||||
xaxis: 'timeColumn',
|
||||
yaxis: 'valueColumn',
|
||||
spliton: 'segmentColumn',
|
||||
groupBy: 'dimension',
|
||||
groupByOptions: 'dimensions',
|
||||
filter: 'dimensionFilter',
|
||||
} as { [old: string]: string };
|
||||
|
||||
for (const old in mappings) {
|
||||
if (appInsights[old]) {
|
||||
appInsights[mappings[old]] = appInsights[old];
|
||||
delete appInsights[old];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
replace(variable: string) {
|
||||
return this.templateSrv.replace(variable, this.panelCtrl.panel.scopedVars);
|
||||
}
|
||||
@@ -424,6 +482,7 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
||||
if (metadata.dimensions.length > 0) {
|
||||
this.target.azureMonitor.dimension = metadata.dimensions[0].value;
|
||||
}
|
||||
|
||||
return this.refresh();
|
||||
})
|
||||
.catch(this.handleQueryCtrlError.bind(this));
|
||||
@@ -439,19 +498,34 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
||||
return allowedTimeGrainsMs;
|
||||
}
|
||||
|
||||
getAutoInterval() {
|
||||
if (this.target.azureMonitor.timeGrain === 'auto') {
|
||||
generateAutoUnits(timeGrain: string, timeGrains: Array<{ value: string }>) {
|
||||
if (timeGrain === 'auto') {
|
||||
return TimegrainConverter.findClosestTimeGrain(
|
||||
this.templateSrv.getBuiltInIntervalValue(),
|
||||
_.map(this.target.azureMonitor.timeGrains, o =>
|
||||
TimegrainConverter.createKbnUnitFromISO8601Duration(o.value)
|
||||
) || ['1m', '5m', '15m', '30m', '1h', '6h', '12h', '1d']
|
||||
_.map(timeGrains, o => TimegrainConverter.createKbnUnitFromISO8601Duration(o.value)) || [
|
||||
'1m',
|
||||
'5m',
|
||||
'15m',
|
||||
'30m',
|
||||
'1h',
|
||||
'6h',
|
||||
'12h',
|
||||
'1d',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
getAzureMonitorAutoInterval() {
|
||||
return this.generateAutoUnits(this.target.azureMonitor.timeGrain, this.target.azureMonitor.timeGrains);
|
||||
}
|
||||
|
||||
getApplicationInsightAutoInterval() {
|
||||
return this.generateAutoUnits(this.target.appInsights.timeGrain, this.target.appInsights.timeGrains);
|
||||
}
|
||||
|
||||
/* Azure Log Analytics */
|
||||
|
||||
getWorkspaces = () => {
|
||||
@@ -521,7 +595,7 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
||||
.getAppInsightsMetricMetadata(this.replace(this.target.appInsights.metricName))
|
||||
.then((aggData: { supportedAggTypes: string[]; supportedGroupBy: string[]; primaryAggType: string }) => {
|
||||
this.target.appInsights.aggOptions = aggData.supportedAggTypes;
|
||||
this.target.appInsights.groupByOptions = aggData.supportedGroupBy;
|
||||
this.target.appInsights.dimensions = aggData.supportedGroupBy;
|
||||
this.target.appInsights.aggregation = aggData.primaryAggType;
|
||||
return this.refresh();
|
||||
})
|
||||
@@ -541,27 +615,41 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
|
||||
};
|
||||
|
||||
getAppInsightsGroupBySegments(query: any) {
|
||||
return _.map(this.target.appInsights.groupByOptions, option => {
|
||||
return _.map(this.target.appInsights.dimensions, (option: string) => {
|
||||
return { text: option, value: option };
|
||||
});
|
||||
}
|
||||
|
||||
resetAppInsightsGroupBy() {
|
||||
this.target.appInsights.groupBy = 'none';
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
updateTimeGrainType() {
|
||||
if (this.target.appInsights.timeGrainType === 'specific') {
|
||||
this.target.appInsights.timeGrain = '1';
|
||||
this.target.appInsights.timeGrainUnit = 'minute';
|
||||
} else {
|
||||
this.target.appInsights.timeGrain = '';
|
||||
}
|
||||
this.target.appInsights.dimension = 'none';
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
toggleEditorMode() {
|
||||
this.target.appInsights.rawQuery = !this.target.appInsights.rawQuery;
|
||||
}
|
||||
|
||||
updateTimeGrainType() {
|
||||
if (this.target.appInsights.timeGrainType === 'specific') {
|
||||
this.target.appInsights.timeGrainCount = '1';
|
||||
this.target.appInsights.timeGrainUnit = 'minute';
|
||||
this.target.appInsights.timeGrain = TimegrainConverter.createISO8601Duration(
|
||||
this.target.appInsights.timeGrainCount,
|
||||
this.target.appInsights.timeGrainUnit
|
||||
);
|
||||
} else {
|
||||
this.target.appInsights.timeGrainCount = '';
|
||||
this.target.appInsights.timeGrainUnit = '';
|
||||
}
|
||||
}
|
||||
|
||||
updateAppInsightsTimeGrain() {
|
||||
if (this.target.appInsights.timeGrainUnit && this.target.appInsights.timeGrainCount) {
|
||||
this.target.appInsights.timeGrain = TimegrainConverter.createISO8601Duration(
|
||||
this.target.appInsights.timeGrainCount,
|
||||
this.target.appInsights.timeGrainUnit
|
||||
);
|
||||
}
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { DataQuery, DataSourceJsonData } from '@grafana/ui';
|
||||
|
||||
export interface AzureMonitorQuery extends DataQuery {
|
||||
refId: string;
|
||||
format: string;
|
||||
subscription: string;
|
||||
azureMonitor: AzureMetricQuery;
|
||||
azureLogAnalytics: AzureLogsQuery;
|
||||
// appInsights: any;
|
||||
appInsights: ApplicationInsightsQuery;
|
||||
}
|
||||
|
||||
export interface AzureDataSourceJsonData extends DataSourceJsonData {
|
||||
@@ -35,7 +36,6 @@ export interface AzureMetricQuery {
|
||||
metricName: string;
|
||||
timeGrainUnit: string;
|
||||
timeGrain: string;
|
||||
timeGrains: string[];
|
||||
allowedTimeGrainsMs: number[];
|
||||
aggregation: string;
|
||||
dimension: string;
|
||||
@@ -50,6 +50,19 @@ export interface AzureLogsQuery {
|
||||
workspace: string;
|
||||
}
|
||||
|
||||
export interface ApplicationInsightsQuery {
|
||||
rawQuery: boolean;
|
||||
rawQueryString: any;
|
||||
metricName: string;
|
||||
timeGrainUnit: string;
|
||||
timeGrain: string;
|
||||
allowedTimeGrainsMs: number[];
|
||||
aggregation: string;
|
||||
dimension: string;
|
||||
dimensionFilter: string;
|
||||
alias: string;
|
||||
}
|
||||
|
||||
// Azure Monitor API Types
|
||||
|
||||
export interface AzureMonitorMetricDefinitionsResponse {
|
||||
|
||||
Reference in New Issue
Block a user