跳到主要内容

Scenes 应用中的下钻页面

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

向 Scenes 应用添加下钻页面

SceneAppPage 自带 API,允许您创建深层嵌套的下钻页面。

注意

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

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

步骤 1. 创建 Scenes 应用

按照《使用 Scenes 构建应用》指南构建您的应用。

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

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

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),
}),
],
}),
});
}

完整示例

您将在下面找到带有下钻页面的 Scenes 应用的完整代码

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,
},
],
}),
],
});
}