query_ctrl.ts 17 KB

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