query_ctrl.ts 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. import _ from 'lodash';
  2. import { QueryCtrl } from 'app/plugins/sdk';
  3. // import './css/query_editor.css';
  4. import TimegrainConverter from './time_grain_converter';
  5. import './editor/editor_component';
  6. import kbn from 'app/core/utils/kbn';
  7. import { TemplateSrv } from 'app/features/templating/template_srv';
  8. import { auto } from 'angular';
  9. import { DataFrame } from '@grafana/data';
  10. export interface ResultFormat {
  11. text: string;
  12. value: string;
  13. }
  14. export class AzureMonitorQueryCtrl extends QueryCtrl {
  15. static templateUrl = 'partials/query.editor.html';
  16. defaultDropdownValue = 'select';
  17. target: {
  18. refId: string;
  19. queryType: string;
  20. subscription: string;
  21. azureMonitor: {
  22. resourceGroup: string;
  23. resourceName: string;
  24. metricDefinition: string;
  25. metricNamespace: string;
  26. metricName: string;
  27. dimensionFilter: string;
  28. timeGrain: string;
  29. timeGrainUnit: string;
  30. timeGrains: Array<{ text: string; value: string }>;
  31. allowedTimeGrainsMs: number[];
  32. dimensions: any[];
  33. dimension: any;
  34. aggregation: string;
  35. aggOptions: string[];
  36. };
  37. azureLogAnalytics: {
  38. query: string;
  39. resultFormat: string;
  40. workspace: string;
  41. };
  42. appInsights: {
  43. metricName: string;
  44. rawQuery: boolean;
  45. rawQueryString: string;
  46. groupBy: string;
  47. timeGrainType: string;
  48. xaxis: string;
  49. yaxis: string;
  50. spliton: string;
  51. aggOptions: string[];
  52. aggregation: string;
  53. groupByOptions: string[];
  54. timeGrainUnit: string;
  55. timeGrain: string;
  56. };
  57. };
  58. defaults = {
  59. queryType: 'Azure Monitor',
  60. azureMonitor: {
  61. resourceGroup: this.defaultDropdownValue,
  62. metricDefinition: this.defaultDropdownValue,
  63. resourceName: this.defaultDropdownValue,
  64. metricNamespace: this.defaultDropdownValue,
  65. metricName: this.defaultDropdownValue,
  66. dimensionFilter: '*',
  67. timeGrain: 'auto',
  68. },
  69. azureLogAnalytics: {
  70. query: [
  71. '//change this example to create your own time series query',
  72. '<table name> ' +
  73. '//the table to query (e.g. Usage, Heartbeat, Perf)',
  74. '| where $__timeFilter(TimeGenerated) ' +
  75. '//this is a macro used to show the full chart’s time range, choose the datetime column here',
  76. '| summarize count() by <group by column>, bin(TimeGenerated, $__interval) ' +
  77. '//change “group by column” to a column in your table, such as “Computer”. ' +
  78. 'The $__interval macro is used to auto-select the time grain. Can also use 1h, 5m etc.',
  79. '| order by TimeGenerated asc',
  80. ].join('\n'),
  81. resultFormat: 'time_series',
  82. workspace:
  83. this.datasource && this.datasource.azureLogAnalyticsDatasource
  84. ? this.datasource.azureLogAnalyticsDatasource.defaultOrFirstWorkspace
  85. : '',
  86. },
  87. appInsights: {
  88. metricName: this.defaultDropdownValue,
  89. rawQuery: false,
  90. rawQueryString: '',
  91. groupBy: 'none',
  92. timeGrainType: 'auto',
  93. xaxis: 'timestamp',
  94. yaxis: '',
  95. spliton: '',
  96. },
  97. };
  98. resultFormats: ResultFormat[];
  99. workspaces: any[];
  100. showHelp: boolean;
  101. showLastQuery: boolean;
  102. lastQuery: string;
  103. lastQueryError?: string;
  104. subscriptions: Array<{ text: string; value: string }>;
  105. /** @ngInject */
  106. constructor($scope: any, $injector: auto.IInjectorService, private templateSrv: TemplateSrv) {
  107. super($scope, $injector);
  108. _.defaultsDeep(this.target, this.defaults);
  109. this.migrateTimeGrains();
  110. this.migrateToFromTimes();
  111. this.migrateToDefaultNamespace();
  112. this.panelCtrl.events.on('data-received', this.onDataReceived.bind(this), $scope);
  113. this.panelCtrl.events.on('data-error', this.onDataError.bind(this), $scope);
  114. this.resultFormats = [{ text: 'Time series', value: 'time_series' }, { text: 'Table', value: 'table' }];
  115. this.getSubscriptions();
  116. if (this.target.queryType === 'Azure Log Analytics') {
  117. this.getWorkspaces();
  118. }
  119. }
  120. onDataReceived(dataList: DataFrame[]) {
  121. this.lastQueryError = undefined;
  122. this.lastQuery = '';
  123. const anySeriesFromQuery: any = _.find(dataList, { refId: this.target.refId });
  124. if (anySeriesFromQuery && anySeriesFromQuery.meta) {
  125. this.lastQuery = anySeriesFromQuery.meta.query;
  126. }
  127. }
  128. onDataError(err: any) {
  129. this.handleQueryCtrlError(err);
  130. }
  131. handleQueryCtrlError(err: any) {
  132. if (err.query && err.query.refId && err.query.refId !== this.target.refId) {
  133. return;
  134. }
  135. if (err.error && err.error.data && err.error.data.error && err.error.data.error.innererror) {
  136. if (err.error.data.error.innererror.innererror) {
  137. this.lastQueryError = err.error.data.error.innererror.innererror.message;
  138. } else {
  139. this.lastQueryError = err.error.data.error.innererror.message;
  140. }
  141. } else if (err.error && err.error.data && err.error.data.error) {
  142. this.lastQueryError = err.error.data.error.message;
  143. } else if (err.error && err.error.data) {
  144. this.lastQueryError = err.error.data.message;
  145. } else if (err.data && err.data.error) {
  146. this.lastQueryError = err.data.error.message;
  147. } else if (err.data && err.data.message) {
  148. this.lastQueryError = err.data.message;
  149. } else {
  150. this.lastQueryError = err;
  151. }
  152. }
  153. migrateTimeGrains() {
  154. if (this.target.azureMonitor.timeGrainUnit) {
  155. if (this.target.azureMonitor.timeGrain !== 'auto') {
  156. this.target.azureMonitor.timeGrain = TimegrainConverter.createISO8601Duration(
  157. this.target.azureMonitor.timeGrain,
  158. this.target.azureMonitor.timeGrainUnit
  159. );
  160. }
  161. delete this.target.azureMonitor.timeGrainUnit;
  162. this.onMetricNameChange();
  163. }
  164. if (
  165. this.target.azureMonitor.timeGrains &&
  166. this.target.azureMonitor.timeGrains.length > 0 &&
  167. (!this.target.azureMonitor.allowedTimeGrainsMs || this.target.azureMonitor.allowedTimeGrainsMs.length === 0)
  168. ) {
  169. this.target.azureMonitor.allowedTimeGrainsMs = this.convertTimeGrainsToMs(this.target.azureMonitor.timeGrains);
  170. }
  171. }
  172. migrateToFromTimes() {
  173. this.target.azureLogAnalytics.query = this.target.azureLogAnalytics.query.replace(/\$__from\s/gi, '$__timeFrom() ');
  174. this.target.azureLogAnalytics.query = this.target.azureLogAnalytics.query.replace(/\$__to\s/gi, '$__timeTo() ');
  175. }
  176. async migrateToDefaultNamespace() {
  177. if (
  178. this.target.azureMonitor.metricNamespace &&
  179. this.target.azureMonitor.metricNamespace !== this.defaultDropdownValue &&
  180. this.target.azureMonitor.metricDefinition
  181. ) {
  182. return;
  183. }
  184. this.target.azureMonitor.metricNamespace = this.target.azureMonitor.metricDefinition;
  185. }
  186. replace(variable: string) {
  187. return this.templateSrv.replace(variable, this.panelCtrl.panel.scopedVars);
  188. }
  189. onQueryTypeChange() {
  190. if (this.target.queryType === 'Azure Log Analytics') {
  191. return this.getWorkspaces();
  192. }
  193. }
  194. getSubscriptions() {
  195. if (!this.datasource.azureMonitorDatasource.isConfigured()) {
  196. return;
  197. }
  198. return this.datasource.azureMonitorDatasource.getSubscriptions().then((subs: any) => {
  199. this.subscriptions = subs;
  200. if (!this.target.subscription && this.target.queryType === 'Azure Monitor') {
  201. this.target.subscription = this.datasource.azureMonitorDatasource.subscriptionId;
  202. } else if (!this.target.subscription && this.target.queryType === 'Azure Log Analytics') {
  203. this.target.subscription = this.datasource.azureLogAnalyticsDatasource.logAnalyticsSubscriptionId;
  204. }
  205. if (!this.target.subscription && this.subscriptions.length > 0) {
  206. this.target.subscription = this.subscriptions[0].value;
  207. }
  208. return this.subscriptions;
  209. });
  210. }
  211. onSubscriptionChange() {
  212. if (this.target.queryType === 'Azure Log Analytics') {
  213. return this.getWorkspaces();
  214. }
  215. if (this.target.queryType === 'Azure Monitor') {
  216. this.target.azureMonitor.resourceGroup = this.defaultDropdownValue;
  217. this.target.azureMonitor.metricDefinition = this.defaultDropdownValue;
  218. this.target.azureMonitor.resourceName = this.defaultDropdownValue;
  219. this.target.azureMonitor.metricName = this.defaultDropdownValue;
  220. this.target.azureMonitor.aggregation = '';
  221. this.target.azureMonitor.timeGrains = [];
  222. this.target.azureMonitor.timeGrain = '';
  223. this.target.azureMonitor.dimensions = [];
  224. this.target.azureMonitor.dimension = '';
  225. }
  226. }
  227. /* Azure Monitor Section */
  228. getResourceGroups(query: any) {
  229. if (this.target.queryType !== 'Azure Monitor' || !this.datasource.azureMonitorDatasource.isConfigured()) {
  230. return;
  231. }
  232. return this.datasource
  233. .getResourceGroups(
  234. this.replace(this.target.subscription || this.datasource.azureMonitorDatasource.subscriptionId)
  235. )
  236. .catch(this.handleQueryCtrlError.bind(this));
  237. }
  238. getMetricDefinitions(query: any) {
  239. if (
  240. this.target.queryType !== 'Azure Monitor' ||
  241. !this.target.azureMonitor.resourceGroup ||
  242. this.target.azureMonitor.resourceGroup === this.defaultDropdownValue
  243. ) {
  244. return;
  245. }
  246. return this.datasource
  247. .getMetricDefinitions(
  248. this.replace(this.target.subscription || this.datasource.azureMonitorDatasource.subscriptionId),
  249. this.replace(this.target.azureMonitor.resourceGroup)
  250. )
  251. .catch(this.handleQueryCtrlError.bind(this));
  252. }
  253. getResourceNames(query: any) {
  254. if (
  255. this.target.queryType !== 'Azure Monitor' ||
  256. !this.target.azureMonitor.resourceGroup ||
  257. this.target.azureMonitor.resourceGroup === this.defaultDropdownValue ||
  258. !this.target.azureMonitor.metricDefinition ||
  259. this.target.azureMonitor.metricDefinition === this.defaultDropdownValue
  260. ) {
  261. return;
  262. }
  263. return this.datasource
  264. .getResourceNames(
  265. this.replace(this.target.subscription || this.datasource.azureMonitorDatasource.subscriptionId),
  266. this.replace(this.target.azureMonitor.resourceGroup),
  267. this.replace(this.target.azureMonitor.metricDefinition)
  268. )
  269. .catch(this.handleQueryCtrlError.bind(this));
  270. }
  271. getMetricNamespaces() {
  272. if (
  273. this.target.queryType !== 'Azure Monitor' ||
  274. !this.target.azureMonitor.resourceGroup ||
  275. this.target.azureMonitor.resourceGroup === this.defaultDropdownValue ||
  276. !this.target.azureMonitor.metricDefinition ||
  277. this.target.azureMonitor.metricDefinition === this.defaultDropdownValue ||
  278. !this.target.azureMonitor.resourceName ||
  279. this.target.azureMonitor.resourceName === this.defaultDropdownValue
  280. ) {
  281. return;
  282. }
  283. return this.datasource
  284. .getMetricNamespaces(
  285. this.replace(this.target.subscription || this.datasource.azureMonitorDatasource.subscriptionId),
  286. this.replace(this.target.azureMonitor.resourceGroup),
  287. this.replace(this.target.azureMonitor.metricDefinition),
  288. this.replace(this.target.azureMonitor.resourceName)
  289. )
  290. .catch(this.handleQueryCtrlError.bind(this));
  291. }
  292. getMetricNames() {
  293. if (
  294. this.target.queryType !== 'Azure Monitor' ||
  295. !this.target.azureMonitor.resourceGroup ||
  296. this.target.azureMonitor.resourceGroup === this.defaultDropdownValue ||
  297. !this.target.azureMonitor.metricDefinition ||
  298. this.target.azureMonitor.metricDefinition === this.defaultDropdownValue ||
  299. !this.target.azureMonitor.resourceName ||
  300. this.target.azureMonitor.resourceName === this.defaultDropdownValue ||
  301. !this.target.azureMonitor.metricNamespace ||
  302. this.target.azureMonitor.metricNamespace === this.defaultDropdownValue
  303. ) {
  304. return;
  305. }
  306. return this.datasource
  307. .getMetricNames(
  308. this.replace(this.target.subscription || this.datasource.azureMonitorDatasource.subscriptionId),
  309. this.replace(this.target.azureMonitor.resourceGroup),
  310. this.replace(this.target.azureMonitor.metricDefinition),
  311. this.replace(this.target.azureMonitor.resourceName),
  312. this.replace(this.target.azureMonitor.metricNamespace)
  313. )
  314. .catch(this.handleQueryCtrlError.bind(this));
  315. }
  316. onResourceGroupChange() {
  317. this.target.azureMonitor.metricDefinition = this.defaultDropdownValue;
  318. this.target.azureMonitor.resourceName = this.defaultDropdownValue;
  319. this.target.azureMonitor.metricNamespace = this.defaultDropdownValue;
  320. this.target.azureMonitor.metricName = this.defaultDropdownValue;
  321. this.target.azureMonitor.aggregation = '';
  322. this.target.azureMonitor.timeGrains = [];
  323. this.target.azureMonitor.timeGrain = '';
  324. this.target.azureMonitor.dimensions = [];
  325. this.target.azureMonitor.dimension = '';
  326. this.refresh();
  327. }
  328. onMetricDefinitionChange() {
  329. this.target.azureMonitor.resourceName = this.defaultDropdownValue;
  330. this.target.azureMonitor.metricNamespace = this.defaultDropdownValue;
  331. this.target.azureMonitor.metricName = this.defaultDropdownValue;
  332. this.target.azureMonitor.aggregation = '';
  333. this.target.azureMonitor.timeGrains = [];
  334. this.target.azureMonitor.timeGrain = '';
  335. this.target.azureMonitor.dimensions = [];
  336. this.target.azureMonitor.dimension = '';
  337. }
  338. onResourceNameChange() {
  339. this.target.azureMonitor.metricNamespace = this.defaultDropdownValue;
  340. this.target.azureMonitor.metricName = this.defaultDropdownValue;
  341. this.target.azureMonitor.aggregation = '';
  342. this.target.azureMonitor.timeGrains = [];
  343. this.target.azureMonitor.timeGrain = '';
  344. this.target.azureMonitor.dimensions = [];
  345. this.target.azureMonitor.dimension = '';
  346. this.refresh();
  347. }
  348. onMetricNamespacesChange() {
  349. this.target.azureMonitor.metricName = this.defaultDropdownValue;
  350. this.target.azureMonitor.dimensions = [];
  351. this.target.azureMonitor.dimension = '';
  352. }
  353. onMetricNameChange() {
  354. if (!this.target.azureMonitor.metricName || this.target.azureMonitor.metricName === this.defaultDropdownValue) {
  355. return;
  356. }
  357. return this.datasource
  358. .getMetricMetadata(
  359. this.replace(this.target.subscription),
  360. this.replace(this.target.azureMonitor.resourceGroup),
  361. this.replace(this.target.azureMonitor.metricDefinition),
  362. this.replace(this.target.azureMonitor.resourceName),
  363. this.replace(this.target.azureMonitor.metricNamespace),
  364. this.replace(this.target.azureMonitor.metricName)
  365. )
  366. .then((metadata: any) => {
  367. this.target.azureMonitor.aggOptions = metadata.supportedAggTypes || [metadata.primaryAggType];
  368. this.target.azureMonitor.aggregation = metadata.primaryAggType;
  369. this.target.azureMonitor.timeGrains = [{ text: 'auto', value: 'auto' }].concat(metadata.supportedTimeGrains);
  370. this.target.azureMonitor.timeGrain = 'auto';
  371. this.target.azureMonitor.allowedTimeGrainsMs = this.convertTimeGrainsToMs(metadata.supportedTimeGrains || []);
  372. this.target.azureMonitor.dimensions = metadata.dimensions;
  373. if (metadata.dimensions.length > 0) {
  374. this.target.azureMonitor.dimension = metadata.dimensions[0].value;
  375. }
  376. return this.refresh();
  377. })
  378. .catch(this.handleQueryCtrlError.bind(this));
  379. }
  380. convertTimeGrainsToMs(timeGrains: Array<{ text: string; value: string }>) {
  381. const allowedTimeGrainsMs: number[] = [];
  382. timeGrains.forEach((tg: any) => {
  383. if (tg.value !== 'auto') {
  384. allowedTimeGrainsMs.push(kbn.interval_to_ms(TimegrainConverter.createKbnUnitFromISO8601Duration(tg.value)));
  385. }
  386. });
  387. return allowedTimeGrainsMs;
  388. }
  389. getAutoInterval() {
  390. if (this.target.azureMonitor.timeGrain === 'auto') {
  391. return TimegrainConverter.findClosestTimeGrain(
  392. this.templateSrv.getBuiltInIntervalValue(),
  393. _.map(this.target.azureMonitor.timeGrains, o =>
  394. TimegrainConverter.createKbnUnitFromISO8601Duration(o.value)
  395. ) || ['1m', '5m', '15m', '30m', '1h', '6h', '12h', '1d']
  396. );
  397. }
  398. return '';
  399. }
  400. /* Azure Log Analytics */
  401. getWorkspaces = () => {
  402. return this.datasource.azureLogAnalyticsDatasource
  403. .getWorkspaces(this.target.subscription)
  404. .then((list: any[]) => {
  405. this.workspaces = list;
  406. if (list.length > 0 && !this.target.azureLogAnalytics.workspace) {
  407. this.target.azureLogAnalytics.workspace = list[0].value;
  408. }
  409. })
  410. .catch(this.handleQueryCtrlError.bind(this));
  411. };
  412. getAzureLogAnalyticsSchema = () => {
  413. return this.getWorkspaces()
  414. .then(() => {
  415. return this.datasource.azureLogAnalyticsDatasource.getSchema(this.target.azureLogAnalytics.workspace);
  416. })
  417. .catch(this.handleQueryCtrlError.bind(this));
  418. };
  419. onLogAnalyticsQueryChange = (nextQuery: string) => {
  420. this.target.azureLogAnalytics.query = nextQuery;
  421. };
  422. onLogAnalyticsQueryExecute = () => {
  423. this.panelCtrl.refresh();
  424. };
  425. get templateVariables() {
  426. return this.templateSrv.variables.map(t => '$' + t.name);
  427. }
  428. /* Application Insights Section */
  429. getAppInsightsAutoInterval() {
  430. const interval = this.templateSrv.getBuiltInIntervalValue();
  431. if (interval[interval.length - 1] === 's') {
  432. return '1m';
  433. }
  434. return interval;
  435. }
  436. getAppInsightsMetricNames() {
  437. if (!this.datasource.appInsightsDatasource.isConfigured()) {
  438. return;
  439. }
  440. return this.datasource.getAppInsightsMetricNames().catch(this.handleQueryCtrlError.bind(this));
  441. }
  442. getAppInsightsColumns() {
  443. return this.datasource.getAppInsightsColumns(this.target.refId);
  444. }
  445. onAppInsightsColumnChange() {
  446. return this.refresh();
  447. }
  448. onAppInsightsMetricNameChange() {
  449. if (!this.target.appInsights.metricName || this.target.appInsights.metricName === this.defaultDropdownValue) {
  450. return;
  451. }
  452. return this.datasource
  453. .getAppInsightsMetricMetadata(this.replace(this.target.appInsights.metricName))
  454. .then((aggData: { supportedAggTypes: string[]; supportedGroupBy: string[]; primaryAggType: string }) => {
  455. this.target.appInsights.aggOptions = aggData.supportedAggTypes;
  456. this.target.appInsights.groupByOptions = aggData.supportedGroupBy;
  457. this.target.appInsights.aggregation = aggData.primaryAggType;
  458. return this.refresh();
  459. })
  460. .catch(this.handleQueryCtrlError.bind(this));
  461. }
  462. onAppInsightsQueryChange = (nextQuery: string) => {
  463. this.target.appInsights.rawQueryString = nextQuery;
  464. };
  465. onAppInsightsQueryExecute = () => {
  466. return this.refresh();
  467. };
  468. getAppInsightsQuerySchema = () => {
  469. return this.datasource.appInsightsDatasource.getQuerySchema().catch(this.handleQueryCtrlError.bind(this));
  470. };
  471. getAppInsightsGroupBySegments(query: any) {
  472. return _.map(this.target.appInsights.groupByOptions, option => {
  473. return { text: option, value: option };
  474. });
  475. }
  476. resetAppInsightsGroupBy() {
  477. this.target.appInsights.groupBy = 'none';
  478. this.refresh();
  479. }
  480. updateTimeGrainType() {
  481. if (this.target.appInsights.timeGrainType === 'specific') {
  482. this.target.appInsights.timeGrain = '1';
  483. this.target.appInsights.timeGrainUnit = 'minute';
  484. } else {
  485. this.target.appInsights.timeGrain = '';
  486. }
  487. this.refresh();
  488. }
  489. toggleEditorMode() {
  490. this.target.appInsights.rawQuery = !this.target.appInsights.rawQuery;
  491. }
  492. }