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: (
+