From 51a5f9f3fdeda19ee19f3724c9ca248521503f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 10 May 2023 14:07:08 +0200 Subject: [PATCH] Scenes: Update to 0.7 and update monitoring demo with new form of in-place drilldown (#68123) * Update * Progress * Update * Update --- package.json | 2 +- .../scenes/apps/GrafanaMonitoringApp.tsx | 7 + .../scenes/apps/{scenes.ts => scenes.tsx} | 90 +++------- public/app/features/scenes/apps/traffic.tsx | 163 ++++++++++++++++++ public/app/features/scenes/apps/utils.ts | 44 +++++ yarn.lock | 10 +- 6 files changed, 246 insertions(+), 70 deletions(-) rename public/app/features/scenes/apps/{scenes.ts => scenes.tsx} (87%) create mode 100644 public/app/features/scenes/apps/traffic.tsx diff --git a/package.json b/package.json index ac78d9eb01e..7d04e89fecb 100644 --- a/package.json +++ b/package.json @@ -261,7 +261,7 @@ "@grafana/lezer-logql": "0.1.3", "@grafana/monaco-logql": "^0.0.7", "@grafana/runtime": "workspace:*", - "@grafana/scenes": "^0.6.0", + "@grafana/scenes": "^0.7.0", "@grafana/schema": "workspace:*", "@grafana/ui": "workspace:*", "@kusto/monaco-kusto": "5.3.6", diff --git a/public/app/features/scenes/apps/GrafanaMonitoringApp.tsx b/public/app/features/scenes/apps/GrafanaMonitoringApp.tsx index b1aa3b7471f..a19e41bbdc7 100644 --- a/public/app/features/scenes/apps/GrafanaMonitoringApp.tsx +++ b/public/app/features/scenes/apps/GrafanaMonitoringApp.tsx @@ -12,6 +12,7 @@ import { getHandlerDetailsScene, getHandlerLogsScene, } from './scenes'; +import { getTrafficScene } from './traffic'; export function GrafanaMonitoringApp() { const appScene = useMemo( @@ -58,6 +59,12 @@ export function getMainPageScene() { }, ], }), + new SceneAppPage({ + title: 'Traffic', + url: '/scenes/grafana-monitoring/traffic', + getScene: getTrafficScene, + preserveUrlKeys: ['from', 'to', 'var-instance'], + }), new SceneAppPage({ title: 'Logs', url: '/scenes/grafana-monitoring/logs', diff --git a/public/app/features/scenes/apps/scenes.ts b/public/app/features/scenes/apps/scenes.tsx similarity index 87% rename from public/app/features/scenes/apps/scenes.ts rename to public/app/features/scenes/apps/scenes.tsx index 1f3fbe47808..9e964bc5d61 100644 --- a/public/app/features/scenes/apps/scenes.ts +++ b/public/app/features/scenes/apps/scenes.tsx @@ -1,3 +1,5 @@ +import React from 'react'; + import { FieldColorModeId, getFrameDisplayName } from '@grafana/data'; import { locationService } from '@grafana/runtime'; import { @@ -10,19 +12,17 @@ import { SceneTimeRange, VariableValueSelectors, SceneQueryRunner, - SceneVariableSet, - QueryVariable, SceneControlsSpacer, SceneDataTransformer, SceneRefreshPicker, SceneFlexItem, } from '@grafana/scenes'; -import { PromQuery } from 'app/plugins/datasource/prometheus/types'; +import { LinkButton } from '@grafana/ui'; import { SceneRadioToggle } from './SceneRadioToggle'; import { SceneSearchBox } from './SceneSearchBox'; import { getTableFilterTransform, getTimeSeriesFilterTransform } from './transforms'; -import { getLinkUrlWithAppUrlState } from './utils'; +import { getInstantQuery, getLinkUrlWithAppUrlState, getTimeSeriesQuery, getVariablesDefinitions } from './utils'; export function getHttpHandlerListScene(): EmbeddedScene { const searchBox = new SceneSearchBox({ value: '' }); @@ -49,7 +49,7 @@ export function getHttpHandlerListScene(): EmbeddedScene { const httpHandlersTable = new VizPanel({ $data: httpHandlerQueriesFiltered, pluginId: 'table', - title: '', + title: 'Handlers', options: { footer: { enablePagination: true, @@ -154,14 +154,23 @@ export function getHttpHandlerListScene(): EmbeddedScene { }), body: new SceneFlexLayout({ direction: 'row', + key: `row-${frameIndex}`, children: [ new SceneFlexItem({ + key: `flex1-${frameIndex}`, body: new VizPanel({ + key: `viz1-${frameIndex}`, pluginId: 'timeseries', - // titleLink: { - // path: `/scenes/grafana-monitoring/handlers/${encodeURIComponent(frame.fields[1].labels.handler)}`, - // queryKeys: ['from', 'to', 'var-instance'], - // }, + headerActions: ( + + Details + + ), title: getFrameDisplayName(frame), options: { legend: { showLegend: false }, @@ -170,26 +179,16 @@ export function getHttpHandlerListScene(): EmbeddedScene { }), new SceneFlexItem({ + key: `flex1-${frameIndex}`, width: 200, body: new VizPanel({ + key: `viz3-${frameIndex}`, title: 'Last', pluginId: 'stat', fieldConfig: { defaults: { displayName: 'Last', - links: [ - { - title: 'Go to handler drilldown view', - url: ``, - onBuildUrl: () => { - const params = locationService.getSearchObject(); - return getLinkUrlWithAppUrlState( - '/scenes/grafana-monitoring/handlers/${__field.labels.handler:percentencode}', - params - ); - }, - }, - ], + links: [], }, overrides: [], }, @@ -242,6 +241,11 @@ export function getHttpHandlerListScene(): EmbeddedScene { return scene; } +function getHandlerDrilldownUrl(handler: string) { + const params = locationService.getSearchObject(); + return getLinkUrlWithAppUrlState(`/scenes/grafana-monitoring/handlers/${encodeURIComponent(handler)}`, params); +} + export function getHandlerDetailsScene(handler: string): EmbeddedScene { const reqDurationTimeSeries = getTimeSeriesQuery({ expr: `avg without(job, instance) (rate(grafana_http_request_duration_seconds_sum{handler="${handler}"}[$__rate_interval])) * 1e3`, @@ -290,36 +294,6 @@ export function getHandlerDetailsScene(handler: string): EmbeddedScene { return scene; } -function getInstantQuery(query: Partial): SceneQueryRunner { - return new SceneQueryRunner({ - datasource: { uid: 'gdev-prometheus' }, - queries: [ - { - refId: 'A', - instant: true, - format: 'table', - maxDataPoints: 500, - ...query, - }, - ], - }); -} - -function getTimeSeriesQuery(query: Partial): SceneQueryRunner { - return new SceneQueryRunner({ - datasource: { uid: 'gdev-prometheus' }, - queries: [ - { - refId: 'A', - range: true, - format: 'time_series', - maxDataPoints: 500, - ...query, - }, - ], - }); -} - export function getOverviewScene(): EmbeddedScene { const scene = new EmbeddedScene({ $variables: getVariablesDefinitions(), @@ -429,18 +403,6 @@ export function getOverviewScene(): EmbeddedScene { return scene; } -function getVariablesDefinitions() { - return new SceneVariableSet({ - variables: [ - new QueryVariable({ - name: 'instance', - datasource: { uid: 'gdev-prometheus' }, - query: { query: 'label_values(grafana_http_request_duration_seconds_sum, instance)' }, - }), - ], - }); -} - function getInstantStatPanel(query: string, title: string) { return new VizPanel({ $data: getInstantQuery({ expr: query }), diff --git a/public/app/features/scenes/apps/traffic.tsx b/public/app/features/scenes/apps/traffic.tsx new file mode 100644 index 00000000000..afbf872147a --- /dev/null +++ b/public/app/features/scenes/apps/traffic.tsx @@ -0,0 +1,163 @@ +import React from 'react'; + +import { + SceneFlexLayout, + SceneTimePicker, + VizPanel, + EmbeddedScene, + SceneTimeRange, + VariableValueSelectors, + SceneControlsSpacer, + SceneRefreshPicker, + SceneFlexItem, + SceneObjectState, + SceneObjectBase, + SceneObjectUrlSyncConfig, + SceneObjectUrlValues, +} from '@grafana/scenes'; +import { Button } from '@grafana/ui'; + +import { getInstantQuery, getTimeSeriesQuery, getVariablesDefinitions } from './utils'; + +export function getTrafficScene(): EmbeddedScene { + const httpHandlersTable = new VizPanel({ + $data: getInstantQuery({ + expr: 'sort_desc(avg without(job, instance) (rate(grafana_http_request_duration_seconds_sum[$__rate_interval]) * 1e3)) ', + }), + pluginId: 'table', + title: 'Handlers', + options: { + footer: { + enablePagination: true, + }, + }, + fieldConfig: { + defaults: {}, + overrides: [ + { + matcher: { + id: 'byRegexp', + options: '.*', + }, + properties: [{ id: 'filterable', value: false }], + }, + { + matcher: { + id: 'byName', + options: 'Time', + }, + properties: [{ id: 'custom.hidden', value: true }], + }, + { + matcher: { + id: 'byName', + options: 'Value', + }, + properties: [{ id: 'displayName', value: 'Duration (Avg)' }], + }, + { + matcher: { + id: 'byName', + options: 'handler', + }, + properties: [ + { + id: 'links', + value: [ + { + title: 'Go to handler drilldown view', + url: '/scenes/grafana-monitoring/traffic?handler=${__value.text:percentencode}', + // onBuildUrl: () => { + // const params = locationService.getSearchObject(); + // return getLinkUrlWithAppUrlState('/scenes/grafana-monitoring/traffic', { + // ...params, + // handler: '${__value.text:percentencode}', + // }); + // }, + }, + ], + }, + ], + }, + ], + }, + }); + + const scene = new EmbeddedScene({ + $variables: getVariablesDefinitions(), + $timeRange: new SceneTimeRange({ from: 'now-1h', to: 'now' }), + controls: [ + new VariableValueSelectors({}), + new SceneControlsSpacer(), + new SceneTimePicker({ isOnCanvas: true }), + new SceneRefreshPicker({ isOnCanvas: true }), + ], + body: new SceneFlexLayout({ + $behaviors: [new HandlerDrilldownViewBehavior()], + children: [new SceneFlexItem({ body: httpHandlersTable })], + }), + }); + + return scene; +} + +export interface HandlerDrilldownViewBehaviorState extends SceneObjectState { + handler?: string; +} + +export class HandlerDrilldownViewBehavior extends SceneObjectBase { + protected _urlSync = new SceneObjectUrlSyncConfig(this, { keys: ['handler'] }); + + public constructor() { + super({}); + + this.addActivationHandler(() => { + this._subs.add(this.subscribeToState((state) => this.onHandlerChanged(state.handler))); + this.onHandlerChanged(this.state.handler); + }); + } + + private onHandlerChanged(handler: string | undefined) { + const layout = this.getLayout(); + + if (handler == null) { + layout.setState({ children: layout.state.children.slice(0, 1) }); + } else { + layout.setState({ children: [layout.state.children[0], this.getDrilldownView(handler)] }); + } + } + + private getDrilldownView(handler: string): SceneFlexItem { + return new SceneFlexItem({ + key: 'drilldown-flex', + body: new VizPanel({ + $data: getTimeSeriesQuery({ + expr: `rate(grafana_http_request_duration_seconds_sum{handler="${handler}"}[$__rate_interval]) * 1e3`, + }), + pluginId: 'timeseries', + title: `Handler: ${handler} details`, + headerActions: ( +