跳至主要内容

场景应用中的下钻页面

下钻页面是构建复杂数据驱动应用程序的强大工具。它们允许您创建用户可以与之交互并逐步探索以揭示底层数据的顶层数据概述。

向场景应用程序添加下钻页面

SceneAppPage 提供了一个 API,允许您创建深度嵌套的下钻页面。

注意

在您开始之前:您必须已经了解 React Router URL 参数、Grafana 字段配置和数据链接,然后才能继续使用本指南。

要创建下钻页面,请使用 SceneAppPage 对象的 drilldown 属性。

步骤 1. 创建场景应用

按照 使用场景构建应用指南 构建您的应用。

步骤 2. 构建顶层下钻页面

使用以下代码构建一个页面,该页面显示 Prometheus API 终结点 HTTP 请求平均持续时间的摘要,使用 Grafana 的表格面板

function getOverviewScene() {
const queryRunner = new SceneQueryRunner({
$timeRange: new SceneTimeRange(),
datasource: {
type: 'prometheus',
uid: '<PROVIDE_GRAFANA_DS_UID>',
},
queries: [
{
refId: 'A',
expr: 'sort_desc(avg by(handler) (rate(prometheus_http_request_duration_seconds_sum {}[5m]) * 1e3))',
format: 'table',
instant: true,
},
],
});

const tablePanel = PanelBuilders.table().setTitle('Average duration of HTTP request').setData(queryRunner).build();

return new EmbeddedScene({
body: new SceneFlexLayout({
direction: 'column',
children: [
new SceneFlexItem({
minHeight: 300,
body: tablePanel,
}),
],
}),
});
}

function getSceneApp() {
return new SceneApp({
pages: [
new SceneAppPage({
title: 'HTTP handlers overview',
url: '/a/<PLUGIN_ID>/my-app',
getScene: getOverviewScene,
}),
],
});
}

步骤 3. 设置下钻导航

要显示下钻页面,您需要提供导航。配置表格面板数据链接(了解 官方 Grafana 文档 中的数据链接)。然后修改表格面板配置以设置 handler 字段的数据链接

import { sceneUtils, PanelBuilders } from '@grafana/scenes';

// ...

const tablePanel = PanelBuilders.table()
.setTitle('Average duration of HTTP request')
.setData(queryRunner)
.setOverrides((b) =>
b.matchFieldsWithName('handler').overrideLinks([
{
title: 'Go to handler overview',
url: '/a/<PLUGIN_ID>/my-app/${__value.text}${__url.params}',
},
])
)
.build();

结果面板将为 handler 字段的所有值提供链接。单击一个值将重定向到特定的终结点下钻 URL,该 URL 将显示“未找到页面”错误。您将在下一步设置此页面。

注意

fieldConfig 选项与您在查看表格面板检查抽屉中的面板 JSON时在典型仪表板面板中看到的选项相同。要访问面板检查抽屉,请单击面板编辑菜单中的检查

步骤 4. 构建下钻页面

修改 getSceneApp 函数以设置下钻场景。使用 SceneAppPage 对象的 drilldowns 属性。drilldowns 属性接受一个 SceneAppDrilldownView 对象数组。它允许渲染下钻 URL 和页面配置

export interface SceneAppDrilldownView {
/** Use to provide parametrized drilldown URL, for example, /app/clusters/:clusterId **/
routePath: string;
/** Function that returns a page object for a given drilldown route match. Use parent to configure drilldown view parent SceneAppPage via getParentPage method. **/
getPage: (routeMatch: SceneRouteMatch<any>, parent: SceneAppPageLike) => SceneAppPageLike;
}

配置 API 终结点下钻视图

function getSceneApp() {
return new SceneApp({
pages: [
new SceneAppPage({
title: 'HTTP handlers overview',
url: '/a/<PLUGIN_ID>/my-app',
getScene: getOverviewScene,
drilldowns: [
{
routePath: '/a/<PLUGIN_ID>/my-app/:handler',
getPage: getHandlerDrilldownPage,
},
],
}),
],
});
}

定义一个函数,返回下钻视图的 SceneAppPage。此函数接收两个参数

  • routeMatch - 包含有关 URL 参数的信息。
  • parentPage - 包含对父 SceneAppPage 的引用,需要正确配置面包屑。
function getHandlerDrilldownPage(routeMatch: SceneRouteMatch<{ handler: string }>, parent: SceneAppPageLike) {
// Retrieve handler from the URL params
const handler = decodeURIComponent(routeMatch.params.handler);

return new SceneAppPage({
// Set up a particular handler drill-down URL
url: `/a/<PLUGIN_ID>/my-app/${encodeURIComponent(handler)}`,
// Important: Set this up for breadcrumbs to be built
getParentPage: () => parent,
title: `${handler} endpoint overview`,
getScene: () => getHandlerDrilldownScene(handler),
});
}

步骤 5. 构建下钻场景

定义将在下钻页面上渲染的场景

function getHandlerDrilldownScene(handler: string) {
const requestsDuration = new SceneQueryRunner({
datasource: {
type: 'prometheus',
uid: '<PROVIDE_GRAFANA_DS_UID>',
},
queries: [
{
refId: 'A',
expr: `avg without(job, instance) (rate(prometheus_http_request_duration_seconds_sum{handler="${handler}"}[5m])) * 1e3`,
},
],
});

const requestsCount = new SceneQueryRunner({
datasource: {
type: 'prometheus',
uid: '<PROVIDE_GRAFANA_DS_UID>',
},
queries: [
{
refId: 'A',
expr: `sum without(job, instance) (rate(prometheus_http_request_duration_seconds_count{handler="${handler}"}[5m])) `,
},
],
});

return new EmbeddedScene({
body: new SceneFlexLayout({
direction: 'column',
children: [
new SceneFlexItem({
minHeight: 300,
body: PanelBuilders.timeseries().setTitle('Requests duration').setData(requestsDuration),
}),
new SceneFlexItem({
minHeight: 300,
body: PanelBuilders.timeseries().setTitle('Requests count').setData(requestsCount),
}),
],
}),
});
}

完整示例

下面是具有下钻页面的场景应用程序的完整代码

function getOverviewScene() {
const queryRunner = new SceneQueryRunner({
$timeRange: new SceneTimeRange(),
datasource: {
type: 'prometheus',
uid: '<PROVIDE_GRAFANA_DS_UID>',
},
queries: [
{
refId: 'A',
expr: 'sort_desc(avg by(handler) (rate(prometheus_http_request_duration_seconds_sum {}[5m]) * 1e3))',
format: 'table',
instant: true,
},
],
});

const tablePanel = PanelBuilders.table()
.setTitle('Average duration of HTTP request')
.setData(queryRunner)
.setOverrides((b) =>
b.matchFieldsWithName('handler').overrideLinks([
{
title: 'Go to handler overview',
url: '/a/<PLUGIN_ID>/my-app/${__value.text}${__url.params}',
},
])
)
.build();

return new EmbeddedScene({
body: new SceneFlexLayout({
direction: 'column',
children: [
new SceneFlexItem({
minHeight: 300,
body: tablePanel,
}),
],
}),
});
}

function getHandlerDrilldownPage(routeMatch: SceneRouteMatch<{ handler: string }>, parent: SceneAppPageLike) {
// Retrieve handler from the URL params.
const handler = decodeURIComponent(routeMatch.params.handler);

return new SceneAppPage({
// Setup particular handler drilldown URL
url: `/a/<PLUGIN_ID>/my-app/${encodeURIComponent(handler)}`,
// Important: setup this for breadcrumbs to be built
getParentPage: () => parent,
title: `${handler} endpoint overview`,
getScene: () => getHandlerDrilldownScene(handler),
});
}

function getHandlerDrilldownScene(handler: string) {
const requestsDuration = new SceneQueryRunner({
datasource: {
type: 'prometheus',
uid: '<PROVIDE_GRAFANA_DS_UID>',
},
queries: [
{
refId: 'A',
expr: `avg without(job, instance) (rate(prometheus_http_request_duration_seconds_sum{handler="${handler}"}[5m])) * 1e3`,
},
],
});

const requestsCount = new SceneQueryRunner({
datasource: {
type: 'prometheus',
uid: '<PROVIDE_GRAFANA_DS_UID>',
},
queries: [
{
refId: 'A',
expr: `sum without(job, instance) (rate(prometheus_http_request_duration_seconds_count{handler="${handler}"}[5m])) `,
},
],
});

return new EmbeddedScene({
body: new SceneFlexLayout({
direction: 'column',
children: [
new SceneFlexItem({
minHeight: 300,
body: PanelBuilders.timeseries().setTitle('Requests duration').setData(requestsDuration),
}),
new SceneFlexItem({
minHeight: 300,
body: PanelBuilders.timeseries().setTitle('Requests count').setData(requestsCount),
}),
],
}),
});
}

function getSceneApp() {
return new SceneApp({
pages: [
new SceneAppPage({
title: 'HTTP handlers overview',
url: '/a/<PLUGIN_ID>/my-app',
getScene: getOverviewScene,
drilldowns: [
{
routePath: '/a/<PLUGIN_ID>/my-app/:handler',
getPage: getHandlerDrilldownPage,
},
],
}),
],
});
}