跳到主要内容

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