Azure: Multiple dimension support for Azure Monitor Service (#25947)

Azure Monitor (metrics) support multiple dimensions instead of just one.

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
Kyle Brandt
2020-06-30 16:26:46 -04:00
committed by GitHub
parent 72fa5ccb7b
commit 4be56cde0d
11 changed files with 403 additions and 142 deletions

View File

@@ -74,11 +74,18 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
const aggregation = templateSrv.replace(item.aggregation, scopedVars);
const top = templateSrv.replace(item.top || '', scopedVars);
const dimensionsFilters = item.dimensionFilters.map(f => {
return {
dimension: templateSrv.replace(f.dimension, scopedVars),
operator: f.operator || 'eq',
filter: templateSrv.replace(f.filter, scopedVars),
};
});
return {
refId: target.refId,
subscription: subscriptionId,
queryType: AzureQueryType.AzureMonitor,
type: 'timeSeriesQuery',
azureMonitor: {
resourceGroup,
resourceName,
@@ -89,9 +96,8 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
metricNamespace:
metricNamespace && metricNamespace !== defaultDropdownValue ? metricNamespace : metricDefinition,
aggregation: aggregation,
dimension: templateSrv.replace(item.dimension, scopedVars),
dimensionsFilters,
top: top || '10',
dimensionFilter: templateSrv.replace(item.dimensionFilter, scopedVars),
alias: item.alias,
format: target.format,
},

View File

@@ -13,6 +13,7 @@ import {
import { Observable, of, from } from 'rxjs';
import { DataSourceWithBackend } from '@grafana/runtime';
import InsightsAnalyticsDatasource from './insights_analytics/insights_analytics_datasource';
import { migrateMetricsDimensionFilters } from './query_ctrl';
export default class Datasource extends DataSourceApi<AzureMonitorQuery, AzureDataSourceJsonData> {
azureMonitorDatasource: AzureMonitorDatasource;
@@ -64,6 +65,10 @@ export default class Datasource extends DataSourceApi<AzureMonitorQuery, AzureDa
target.queryType = AzureQueryType.AzureMonitor;
}
if (target.queryType === AzureQueryType.AzureMonitor) {
migrateMetricsDimensionFilters(target.azureMonitor);
}
// Check that we have options
const opts = (target as any)[this.optionsKey[target.queryType]];

View File

@@ -132,44 +132,73 @@
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
<div class="gf-form-inline" ng-show="ctrl.target.azureMonitor.dimensions.length > 0">
<div class="gf-form">
<label class="gf-form-label query-keyword width-9">Dimension</label>
<div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
<select
class="gf-form-input min-width-12"
ng-model="ctrl.target.azureMonitor.dimension"
ng-options="f.value as f.text for f in ctrl.target.azureMonitor.dimensions"
ng-change="ctrl.refresh()"
></select>
<!-- NO Filters-->
<ng-container ng-if="ctrl.target.azureMonitor.dimensionFilters.length < 1">
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label query-keyword width-9">Dimension</label>
</div>
<div class="gf-form">
<a ng-click="ctrl.azureMonitorAddDimensionFilter()" class="gf-form-label query-part"><icon name="'plus'"></icon></a>
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
<div class="gf-form">
<label class="gf-form-label query-keyword width-3">eq</label>
<input
type="text"
class="gf-form-input width-17"
ng-model="ctrl.target.azureMonitor.dimensionFilter"
spellcheck="false"
placeholder="auto"
ng-blur="ctrl.refresh()"
/>
</ng-container>
<!-- YES Filters-->
<ng-container ng-if="ctrl.target.azureMonitor.dimensionFilters.length > 0">
<div ng-repeat="dim in ctrl.target.azureMonitor.dimensionFilters track by $index" class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label query-keyword width-9">Dimension</label>
<div class="gf-form-select-wrapper gf-form-select-wrapper--caret-indent">
<select
class="gf-form-input min-width-12"
ng-model="dim.dimension"
ng-options="f.value as f.text for f in ctrl.target.azureMonitor.dimensions"
ng-change="ctrl.refresh()"
></select>
</div>
<label class="gf-form-label query-keyword width-3">eq</label>
<input
type="text"
class="gf-form-input width-17"
ng-model="dim.filter"
spellcheck="false"
placeholder="Anything (*)"
ng-blur="ctrl.refresh()"
/>
</div>
<div class="gf-form">
<a ng-click="ctrl.azureMonitorRemoveDimensionFilter($index)" class="gf-form-label query-part"><icon name="'minus'"></icon></a>
</div>
<div class="gf-form" ng-if="$last">
<a ng-click="ctrl.azureMonitorAddDimensionFilter()" class="gf-form-label query-part"><icon name="'plus'"></icon></a>
</div>
<div class="gf-form gf-form--grow">
<div class="gf-form-label gf-form-label--grow"></div>
</div>
</div>
<div class="gf-form">
<label class="gf-form-label query-keyword width-9">Top</label>
<input
type="text"
class="gf-form-input width-3"
ng-model="ctrl.target.azureMonitor.top"
spellcheck="false"
placeholder="10"
ng-blur="ctrl.refresh()"
/>
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label query-keyword width-9">Top</label>
<input
type="text"
class="gf-form-input width-3"
ng-model="ctrl.target.azureMonitor.top"
spellcheck="false"
placeholder="10"
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>
</ng-container>
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label query-keyword width-9">Legend Format</label>

View File

@@ -8,7 +8,7 @@ import kbn from 'app/core/utils/kbn';
import { TemplateSrv } from 'app/features/templating/template_srv';
import { auto, IPromise } from 'angular';
import { DataFrame, PanelEvents } from '@grafana/data';
import { AzureQueryType } from './types';
import { AzureQueryType, AzureMetricQuery } from './types';
export interface ResultFormat {
text: string;
@@ -27,23 +27,7 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
refId: string;
queryType: AzureQueryType;
subscription: string;
azureMonitor: {
resourceGroup: string;
resourceName: string;
metricDefinition: string;
metricNamespace: string;
metricName: string;
dimensionFilter: string;
timeGrain: string;
timeGrainUnit: string;
allowedTimeGrainsMs: number[];
dimensions: any[];
dimension: any;
top: string;
aggregation: string;
aggOptions: string[];
timeGrains: Array<{ text: string; value: string }>;
};
azureMonitor: AzureMetricQuery;
azureLogAnalytics: {
query: string;
resultFormat: string;
@@ -139,6 +123,8 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
this.migrateApplicationInsightsDimensions();
migrateMetricsDimensionFilters(this.target.azureMonitor);
this.panelCtrl.events.on(PanelEvents.dataReceived, this.onDataReceived.bind(this), $scope);
this.panelCtrl.events.on(PanelEvents.dataError, this.onDataError.bind(this), $scope);
this.resultFormats = [
@@ -219,12 +205,13 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
}
}
const oldAzureTimeGrains = (this.target.azureMonitor as any).timeGrains;
if (
this.target.azureMonitor.timeGrains &&
this.target.azureMonitor.timeGrains.length > 0 &&
oldAzureTimeGrains &&
oldAzureTimeGrains.length > 0 &&
(!this.target.azureMonitor.allowedTimeGrainsMs || this.target.azureMonitor.allowedTimeGrainsMs.length === 0)
) {
this.target.azureMonitor.allowedTimeGrainsMs = this.convertTimeGrainsToMs(this.target.azureMonitor.timeGrains);
this.target.azureMonitor.allowedTimeGrainsMs = this.convertTimeGrainsToMs(oldAzureTimeGrains);
}
if (
@@ -328,10 +315,8 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
this.target.azureMonitor.resourceName = this.defaultDropdownValue;
this.target.azureMonitor.metricName = this.defaultDropdownValue;
this.target.azureMonitor.aggregation = '';
this.target.azureMonitor.timeGrains = [];
this.target.azureMonitor.timeGrain = '';
this.target.azureMonitor.dimensions = [];
this.target.azureMonitor.dimension = '';
this.target.azureMonitor.dimensionFilters = [];
}
}
@@ -439,10 +424,8 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
this.target.azureMonitor.metricNamespace = this.defaultDropdownValue;
this.target.azureMonitor.metricName = this.defaultDropdownValue;
this.target.azureMonitor.aggregation = '';
this.target.azureMonitor.timeGrains = [];
this.target.azureMonitor.timeGrain = '';
this.target.azureMonitor.dimensions = [];
this.target.azureMonitor.dimension = '';
this.target.azureMonitor.dimensionFilters = [];
this.refresh();
}
@@ -451,27 +434,22 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
this.target.azureMonitor.metricNamespace = this.defaultDropdownValue;
this.target.azureMonitor.metricName = this.defaultDropdownValue;
this.target.azureMonitor.aggregation = '';
this.target.azureMonitor.timeGrains = [];
this.target.azureMonitor.timeGrain = '';
this.target.azureMonitor.dimensions = [];
this.target.azureMonitor.dimension = '';
this.target.azureMonitor.dimensionFilters = [];
}
onResourceNameChange() {
this.target.azureMonitor.metricNamespace = this.defaultDropdownValue;
this.target.azureMonitor.metricName = this.defaultDropdownValue;
this.target.azureMonitor.aggregation = '';
this.target.azureMonitor.timeGrains = [];
this.target.azureMonitor.timeGrain = '';
this.target.azureMonitor.dimensions = [];
this.target.azureMonitor.dimension = '';
this.target.azureMonitor.dimensionFilters = [];
this.refresh();
}
onMetricNamespacesChange() {
this.target.azureMonitor.metricName = this.defaultDropdownValue;
this.target.azureMonitor.dimensions = [];
this.target.azureMonitor.dimension = '';
this.target.azureMonitor.dimensionFilters = [];
}
onMetricNameChange(): IPromise<void> {
@@ -489,16 +467,20 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
this.replace(this.target.azureMonitor.metricName)
)
.then((metadata: any) => {
this.target.azureMonitor.aggOptions = metadata.supportedAggTypes || [metadata.primaryAggType];
this.target.azureMonitor.aggregation = metadata.primaryAggType;
this.target.azureMonitor.timeGrains = [{ text: 'auto', value: 'auto' }].concat(metadata.supportedTimeGrains);
this.target.azureMonitor.timeGrain = 'auto';
console.log('Update metadata', metadata);
this.target.azureMonitor.aggregation = metadata.primaryAggType;
this.target.azureMonitor.timeGrain = 'auto';
this.target.azureMonitor.allowedTimeGrainsMs = this.convertTimeGrainsToMs(metadata.supportedTimeGrains || []);
this.target.azureMonitor.dimensions = metadata.dimensions;
// HACK: this saves the last metadata values in the panel json ¯\_(ツ)_/¯
const hackState = this.target.azureMonitor as any;
hackState.aggOptions = metadata.supportedAggTypes || [metadata.primaryAggType];
hackState.timeGrains = [{ text: 'auto', value: 'auto' }].concat(metadata.supportedTimeGrains);
hackState.dimensions = metadata.dimensions;
if (metadata.dimensions.length > 0) {
this.target.azureMonitor.dimension = metadata.dimensions[0].value;
// this.target.azureMonitor.dimension = metadata.dimensions[0].value;
}
return this.refresh();
@@ -537,13 +519,29 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
}
getAzureMonitorAutoInterval() {
return this.generateAutoUnits(this.target.azureMonitor.timeGrain, this.target.azureMonitor.timeGrains);
return this.generateAutoUnits(this.target.azureMonitor.timeGrain, (this.target.azureMonitor as any).timeGrains);
}
getApplicationInsightAutoInterval() {
return this.generateAutoUnits(this.target.appInsights.timeGrain, this.target.appInsights.timeGrains);
}
azureMonitorAddDimensionFilter() {
console.log('Add dimension', this.target.azureMonitor);
this.target.azureMonitor.dimensionFilters.push({
dimension: '',
operator: 'eq',
filter: '',
});
this.refresh();
}
azureMonitorRemoveDimensionFilter(index: number) {
this.target.azureMonitor.dimensionFilters.splice(index, 1);
this.refresh();
console.log('Remove dimension', index, this.target.azureMonitor);
}
/* Azure Log Analytics */
getWorkspaces = () => {
@@ -695,3 +693,20 @@ export class AzureMonitorQueryCtrl extends QueryCtrl {
this.refresh();
}
}
// Modifies the actual query object
export function migrateMetricsDimensionFilters(item: AzureMetricQuery) {
if (!item.dimensionFilters) {
item.dimensionFilters = [];
}
const oldDimension = (item as any).dimension;
if (oldDimension && oldDimension !== 'None') {
item.dimensionFilters.push({
dimension: oldDimension,
operator: 'eq',
filter: (item as any).dimensionFilter,
});
delete (item as any).dimension;
delete (item as any).dimensionFilter;
}
}

View File

@@ -45,6 +45,12 @@ export interface AzureDataSourceSecureJsonData {
appInsightsApiKey?: string;
}
export interface AzureMetricDimension {
dimension: string;
operator: 'eq'; // future proof
filter?: string; // *
}
export interface AzureMetricQuery {
resourceGroup: string;
resourceName: string;
@@ -55,8 +61,7 @@ export interface AzureMetricQuery {
timeGrain: string;
allowedTimeGrainsMs: number[];
aggregation: string;
dimension: string;
dimensionFilter: string;
dimensionFilters: AzureMetricDimension[];
alias: string;
top: string;
}