mirror of
https://github.com/grafana/grafana.git
synced 2025-08-02 02:22:38 +08:00
Scenes: Update to 0.7 and update monitoring demo with new form of in-place drilldown (#68123)
* Update * Progress * Update * Update
This commit is contained in:
@ -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",
|
||||
|
@ -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',
|
||||
|
@ -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: (
|
||||
<LinkButton
|
||||
fill="text"
|
||||
size="sm"
|
||||
icon="arrow-right"
|
||||
href={getHandlerDrilldownUrl(frame.fields[1]!.labels!.handler)}
|
||||
>
|
||||
Details
|
||||
</LinkButton>
|
||||
),
|
||||
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<PromQuery>): SceneQueryRunner {
|
||||
return new SceneQueryRunner({
|
||||
datasource: { uid: 'gdev-prometheus' },
|
||||
queries: [
|
||||
{
|
||||
refId: 'A',
|
||||
instant: true,
|
||||
format: 'table',
|
||||
maxDataPoints: 500,
|
||||
...query,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
function getTimeSeriesQuery(query: Partial<PromQuery>): 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 }),
|
163
public/app/features/scenes/apps/traffic.tsx
Normal file
163
public/app/features/scenes/apps/traffic.tsx
Normal file
@ -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<HandlerDrilldownViewBehaviorState> {
|
||||
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: (
|
||||
<Button size="sm" variant="secondary" icon="times" onClick={() => this.setState({ handler: undefined })} />
|
||||
),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
public getUrlState() {
|
||||
return { handler: this.state.handler };
|
||||
}
|
||||
|
||||
public updateFromUrl(values: SceneObjectUrlValues) {
|
||||
if (typeof values.handler === 'string' || values.handler === undefined) {
|
||||
this.setState({ handler: values.handler });
|
||||
}
|
||||
}
|
||||
|
||||
private getLayout() {
|
||||
if (this.parent instanceof SceneFlexLayout) {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
throw new Error('Invalid parent');
|
||||
}
|
||||
}
|
@ -2,6 +2,8 @@ import { useLocation } from 'react-router-dom';
|
||||
|
||||
import { UrlQueryMap, urlUtil } from '@grafana/data';
|
||||
import { locationSearchToObject } from '@grafana/runtime';
|
||||
import { QueryVariable, SceneQueryRunner, SceneVariableSet } from '@grafana/scenes';
|
||||
import { PromQuery } from 'app/plugins/datasource/prometheus/types';
|
||||
|
||||
export function useAppQueryParams() {
|
||||
const location = useLocation();
|
||||
@ -11,3 +13,45 @@ export function useAppQueryParams() {
|
||||
export function getLinkUrlWithAppUrlState(path: string, params: UrlQueryMap): string {
|
||||
return urlUtil.renderUrl(path, params);
|
||||
}
|
||||
|
||||
export function getInstantQuery(query: Partial<PromQuery>): SceneQueryRunner {
|
||||
return new SceneQueryRunner({
|
||||
datasource: { uid: 'gdev-prometheus' },
|
||||
queries: [
|
||||
{
|
||||
refId: 'A',
|
||||
instant: true,
|
||||
format: 'table',
|
||||
maxDataPoints: 500,
|
||||
...query,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
export function getTimeSeriesQuery(query: Partial<PromQuery>): SceneQueryRunner {
|
||||
return new SceneQueryRunner({
|
||||
datasource: { uid: 'gdev-prometheus' },
|
||||
queries: [
|
||||
{
|
||||
refId: 'A',
|
||||
range: true,
|
||||
format: 'time_series',
|
||||
maxDataPoints: 500,
|
||||
...query,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
export 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)' },
|
||||
}),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
10
yarn.lock
10
yarn.lock
@ -3060,9 +3060,9 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@grafana/scenes@npm:^0.6.0":
|
||||
version: 0.6.0
|
||||
resolution: "@grafana/scenes@npm:0.6.0"
|
||||
"@grafana/scenes@npm:^0.7.0":
|
||||
version: 0.7.0
|
||||
resolution: "@grafana/scenes@npm:0.7.0"
|
||||
dependencies:
|
||||
"@grafana/e2e-selectors": canary
|
||||
"@grafana/experimental": 1.0.1
|
||||
@ -3070,7 +3070,7 @@ __metadata:
|
||||
react-use: 17.4.0
|
||||
react-virtualized-auto-sizer: 1.0.7
|
||||
uuid: ^9.0.0
|
||||
checksum: 7197abac93ba84711900b526f0caa648b0b9f0c0e2edea2fbc125c1f192d6a3dae52389007cf82cbdf06a7b8019e5c03b5efa635f12ebc51eab5852cddf6427e
|
||||
checksum: 78cff0b04cc1237d32b5d3fae816cc2814e6aeed11cbe443814347d73df7ec97e1ce204733ea7f4e6b39c39dd56f37c15829c530cf0ca9bba646a8b78aa7a74e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -18604,7 +18604,7 @@ __metadata:
|
||||
"@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/toolkit": "workspace:*"
|
||||
"@grafana/tsconfig": ^1.2.0-rc1
|
||||
|
Reference in New Issue
Block a user