mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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]];
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user