Backstage 部署监控指南:使用 OpenTelemetry 与 Analytics API 打造可观测的开发门户
【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage
本文是一份面向 Backstage 运维管理员的部署监控实战指南。生产环境中的 Backstage 需要可观测性来跟踪系统健康、诊断问题和理解使用模式,为此 Backstage 在后端提供了内建的 OpenTelemetry 支持(指标与追踪),在前端提供了事件驱动的 Analytics API。读完本文,你将掌握:如何为后端接入 OpenTelemetry 导出器(Prometheus 指标、OTLP 追踪)、如何利用内置健康检查端点配置 Kubernetes 探针、如何为前端接入第三方分析工具并理解其事件模型,以及如何利用结构化 JSON 日志接入日志聚合系统。
监控总览:两条可观测性主线
Backstage 的可观测性设计围绕两条主线展开:
- 后端:通过 OpenTelemetry 上报指标(metrics)与追踪(traces),结合内置的健康检查端点(health check endpoints)与结构化 JSON 日志,覆盖系统的健康、性能与运行状态。
- 前端:通过事件驱动的 Analytics API 追踪用户行为,帮助理解哪些插件使用最频繁,量化 Backstage 投资的回报(ROI),同时可配合 Sentry、CloudWatch RUM、Cloudflare RUM 等服务做客户端错误上报。
两条主线互补:后端指标回答"系统是否健康、是否高效",前端分析回答"用户如何使用系统"。下面分别展开。
后端监控:OpenTelemetry 接入
Backstage 使用 OpenTelemetry 上报指标与追踪。接入流程分为三步:安装 OpenTelemetry 依赖包、创建插桩(instrumentation)文件、在后端启动前加载该文件。完整的分步教程见 Setup OpenTelemetry 教程,下文直接给出可操作的完整流程。
安装依赖
在packages/backend中安装 OpenTelemetry Node SDK 与自动插桩包:
yarn --cwd packages/backend add \ @opentelemetry/sdk-node \ @opentelemetry/auto-instrumentations-node \ @opentelemetry/exporter-prometheus \ @opentelemetry/exporter-trace-otlp-http各包职责如下:
@opentelemetry/sdk-node:OpenTelemetry Node.js SDK,负责装配指标读取器(metric reader)与追踪导出器(trace exporter)。@opentelemetry/auto-instrumentations-node:自动插桩库,为 Express 等被调用库中的代码自动创建 spans。@opentelemetry/exporter-prometheus:Prometheus 指标导出器,默认在localhost:9464/metrics暴露指标。@opentelemetry/exporter-trace-otlp-http:OTLP HTTP 追踪导出器,将 traces 通过 HTTP 发送到如 Jaeger 等目标。
从源码看,Backstage 的插件(如 catalog)本身就在使用 OpenTelemetry API 发送自定义 traces 和 metrics,例如 catalog-backend 的数据库指标实现 通过metrics.createObservableGauge(...)注册catalog_entities_count、catalog_registered_locations_count、catalog_relations_count等观测指标。auto-instrumentations-node则负责捕获框架层的自动插桩数据。
教程中使用 Prometheus 导出器做演示,你可以按需替换为其他导出器(例如参考 OTLP exporters 相关文档);追踪部分使用 JSON/HTTP 导出器、以 Jaeger 为理想目标,同样可替换为你需要的工具。
创建 instrumentation 文件
在packages/backend/src目录下创建instrumentation.js:
// 防止因 worker 线程导致重复运行 const { isMainThread } = require('node:worker_threads'); if (isMainThread) { const { NodeSDK } = require('@opentelemetry/sdk-node'); const { getNodeAutoInstrumentations, } = require('@opentelemetry/auto-instrumentations-node'); const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus'); const { OTLPTraceExporter, } = require('@opentelemetry/exporter-trace-otlp-http'); // 默认在 localhost:9464/metrics 导出指标 const prometheusExporter = new PrometheusExporter(); // 将 traces 发送到 localhost:4318/v1/traces const otlpTraceExporter = new OTLPTraceExporter({ // 默认 Jaeger URL 追踪端点 url: 'http://localhost:4318/v1/traces', }); const sdk = new NodeSDK({ metricReader: prometheusExporter, traceExporter: otlpTraceExporter, instrumentations: [getNodeAutoInstrumentations()], }); sdk.start(); }几点说明:
- 使用
isMainThread检查可以避免因 worker 线程(Backstage 后台任务可能使用)而多次初始化 SDK。 getNodeAutoInstrumentations()返回全部自动插桩项,实际部署时你很可能不需要全部,建议按需精简,只保留你真正用到的插桩。- 指标默认端口为
9464,路径为/metrics;OTLP traces 默认端点为http://localhost:4318/v1/traces,可按你的 Jaeger/Collector 部署位置调整。
配置 Views 调整直方图桶
OpenTelemetry 默认的直方图桶以毫秒为单位,而 Catalog 处理流程产生的直方图指标以秒为单位。你可以通过 OpenTelemetry 的 Views 功能调整桶边界,使其更贴合实际数据分布。
统一调整所有直方图桶:
const prometheus = new PrometheusExporter(); const sdk = new NodeSDK({ metricReader: prometheus, views: [ new View({ instrumentName: 'catalog.test', aggregation: new ExplicitBucketHistogramAggregation([ 0.01, 0.1, 0.5, 1, 5, 10, 25, 50, 100, 500, 1000, ]), }), ], });更精细的定向配置:
const prometheus = new PrometheusExporter(); const sdk = new NodeSDK({ metricReader: prometheus, views: [ new View({ instrumentName: 'catalog.test', aggregation: new ExplicitBucketHistogramAggregation([ 0, 0.01, 0.05, 0.1, 0.25, 0.5, 1, 2, 5, 10, 30, 60, 120, 300, 1000, ]), }), ], });上述代码中的instrumentName: 'catalog.test'为演示名称,实际使用时应替换为你关心的具体指标名(如catalog.processing.duration)。桶边界的选择应覆盖你预期的处理时长分布,太小会导致高值全部落入最后一个桶,失去区分度。
本地开发配置
关键在于 NodeSDK 与自动插桩必须在导入任何库之前完成初始化,因此需要使用 Node.js 的--require标志(详见 Node.js CLI 文档)在应用启动前预加载插桩文件。
在packages/backend/package.json的scripts中加入--require标志:
"scripts": { "start": "backstage-cli package start --require ./src/instrumentation.js", ...随后正常执行yarn start启动 Backstage,即可在http://localhost:9464/metrics看到指标输出。
常见问题排查
如果指标或追踪无法工作,OpenTelemetry 提供了诊断工具。先安装@opentelemetry/api:
yarn --cwd packages/backend add @opentelemetry/api然后在sdk.start()调用之前加入如下片段,开启调试日志:
const { diag, DiagConsoleLogger, DiagLogLevel } = require('@opentelemetry/api'); diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);这会输出 OpenTelemetry 的调试日志,帮助你定位问题。不建议在生产环境启用——DEBUG 级别的日志量非常大。
此外,OpenTelemetry 提供了大量 SDK 环境变量,可用于禁用或配置特定功能(如采样率、导出间隔等),部署时可按需调整。
生产环境配置(Docker)
生产环境使用 Docker 部署时,需要确保 instrumentation 文件被正确打入镜像并预先加载。
第一步,在.dockerignore中加入下面一行(在推荐的标准.dockerignore配置下,确保 Docker 构建不会忽略插桩文件):
!packages/backend/src/instrumentation.js第二步,在Dockerfile中把instrumentation.js复制到工作目录根:
COPY --chown=${NOT_ROOT_USER}:${NOT_ROOT_USER} packages/backend/src/instrumentation.js ./第三步,在 CMD 数组中加入指向该文件的--require标志:
# 修改前 CMD ["node", "packages/backend", "--config", "app-config.yaml"] # 修改后 CMD ["node", "--require", "./instrumentation.js", "packages/backend", "--config", "app-config.yaml"]可用指标清单
以下是 Backstage 当前暴露的可用指标(实际可用指标取决于你安装的插件及其版本,下表来自本仓库的教程文档与插件源码):
| 指标名 | 含义 |
|---|---|
catalog_entities_count | Catalog 中的实体总数(带kind标签) |
catalog_registered_locations_count | Catalog 中已注册 location 的总数 |
catalog_relations_count | 实体间关系(relations)的总数 |
catalog.processed.entities.count | 已处理的实体数量 |
catalog.processing.duration | 执行完整处理流程所花时间 |
catalog.processors.duration | 执行 catalog processors 所花时间 |
catalog.processing.queue.delay | 从被调度处理到实际开始处理之间的延迟 |
catalog.stitched.entities.count | 已缝合(stitched)的实体数量 |
catalog.stitching.duration | 执行完整缝合流程所花时间 |
catalog.stitching.queue.length | 当前缝合队列中的实体数 |
catalog.stitching.queue.delay | 从被调度缝合到实际开始缝合之间的延迟 |
scaffolder.task.count | 任务运行次数(计数器) |
scaffolder.task.duration | 一次任务运行的耗时(直方图) |
scaffolder.step.count | 步骤运行次数(计数器) |
scaffolder.step.duration | 单次步骤运行耗时(直方图) |
backend_tasks.task.runs.count | 后台任务累计运行次数 |
backend_tasks.task.runs.duration | 后台任务运行耗时的直方图 |
backend_tasks.task.runs.started | 每个任务(taskId标签)最近一次启动的 Unix 时间戳(秒)Gauge |
backend_tasks.task.runs.completed | 每个任务最近一次完成的 Unix 时间戳(秒)Gauge |
这些指标是设置告警的重要依据,例如可以用catalog.processing.queue.delay监控处理积压,用scaffolder.task.duration监控异常的慢任务。
指标背后的源码实现
- Catalog 指标定义在 catalog-backend 数据库指标模块:
catalog_entities_count、catalog_registered_locations_count、catalog_relations_count同时以"Prometheus Gauge(已标记 DEPRECATED,建议改用 OpenTelemetry 指标)"和"OpenTelemetry ObservableGauge"两种形式注册。其中实体计数从final_entities表读取(而非体积大 20~30 倍的search表),并带有 30 秒 TTL 的单飞缓存来合并并发抓取查询,避免每次 scrape 对数据库发起重复的重型查询。实体计数还按kind维度打标签,查询时用数据库方言从entity_ref解析 kind(PostgreSQL 用split_part、MySQL 用substring_index、SQLite 用substr/instr),见 entityRefKindExpression。 - Scaffolder 指标定义在 NunjucksWorkflowRunner 的 scaffoldingTracker:同样以 prom-client(已标记 DEPRECATED)和 OpenTelemetry 两种方式注册
scaffolder.task.count、scaffolder.task.duration、scaffolder.step.count、scaffolder.step.duration,其中 OpenTelemetry 版本带template/step/result标签,unit: 's'明确以秒为单位,与教程中"Catalog 处理直方图以秒为单位"的提示一致。prom-client 版本指标名使用下划线命名(如scaffolder_task_count),OpenTelemetry 版本使用点号命名(如scaffolder.task.count),二者是同一语义的双份实现。 - 各插件通用的指标创建工具函数位于 catalog-backend util/metrics.ts 与 scaffolder-backend util/metrics.ts,均为"先查全局 register 是否已注册同名指标,已注册则复用、否则新建"的模式,保证指标幂等创建。
健康检查端点
Backstage 内置了健康检查端点,可用于 Kubernetes 或其他编排系统中的存活(liveness)与就绪(readiness)探针:
/.backstage/health/v1/readiness:当后端已准备好对外提供流量时返回 healthy。/.backstage/health/v1/liveness:当后端进程存活时返回 healthy。
这两个端点在 createHealthRouter 中实现:readiness端点调用RootHealthService.getReadiness(),liveness端点调用getLiveness(),二者都以对应 HTTP 状态码 + JSON payload 响应。源码还支持通过backend.health.headers配置为健康检查响应附加自定义头(例如供负载均衡器识别的标识),这在 K8s 探针与外部 LB 健康检查结合的场景下非常有用。
一个典型的 Kubernetes 探针配置示例:
livenessProbe: httpGet: path: /.backstage/health/v1/liveness port: backend readinessProbe: httpGet: path: /.backstage/health/v1/readiness port: backend前端分析:Analytics API
Backstage 提供事件驱动的 Analytics API,用于追踪前端用户行为。它既给应用集成方提供了"在自选分析工具中收集和分析使用数据"的灵活性,也给插件开发者提供了"为关键用户交互插桩"的标准接口。完整说明见 Plugin Analytics 文档。
核心概念
- Events(事件):至少由
action(如click)和subject(如"被点击的东西")组成。 - Attributes(属性):事件级别的附加维度数据(键值对)。例如用户点击跳转到的 URL 可表示为
{ "to": "/a/page" }。 - Context(上下文):事件发生的更广背景,默认包含
pluginId和extensionId。
这种事件组合方式支持从多个粒度分析:既能回答"某个路由上被点击最多的是什么"这样的细粒度问题,也能回答"我的 Backstage 实例中哪个插件使用最多"这样的宏观问题。
支持的 Analytics 工具
Analytics 事件转发本质上是 AnalyticsApi 的一个具体实现,常见的集成已打包为插件提供:
| 分析工具 | 支持状态 |
|---|---|
| Google Analytics | 支持 ✅ |
| Google Analytics 4 | 支持 ✅ |
| New Relic Browser | 社区 ✅ |
| Matomo | 社区 ✅ |
| Quantum Metric | 社区 ✅ |
| Generic HTTP | 社区 ✅ |
本文档场景下,你需要重点关注的三个是Google Analytics 4、New Relic Browser 与 Matomo(前文 006-monitoring.md 明确列出的三个)。
关键事件
以下表格总结了各插件可能捕获的关键事件(取决于你安装了哪些插件):
| Action | Subject | 其他说明 |
|---|---|---|
navigate | 被导航到的页面 URL | 路由位置变化时立即触发(若关联插件/路由数据不明确,则推迟到数据可知后、下一个事件或文档卸载前触发)。当前路由参数会作为 attributes 附带 |
click | 被点击链接的文本 | to属性表示点击跳转的 URL |
create | 被创建软件的名称;若模板未要求name属性,则使用new {templateName} | context 携带entityRef(模板 ref,如template:default/template-name);value表示运行模板节省的分钟数(基于模板的backstage.io/time-saved注解) |
search | 任意搜索栏组件中输入的搜索词 | context 携带searchTypes;value表示查询结果总数(使用权限框架时可能不可见) |
discover | 被点击的搜索结果标题 | value为结果排名,同时提供to属性 |
not-found | 导致 404 页面的资源路径 | 至少由 TechDocs 触发 |
编写自定义集成
事件转发实现为 Backstage 的 Utility API,只需提供单个captureEvent(event)方法。新版前端系统使用AnalyticsImplementationBlueprint:
import { AnalyticsImplementationBlueprint } from '@backstage/plugin-app-react'; export const acmeAnalyticsImplementation = AnalyticsImplementationBlueprint.make({ name: 'acme', params: define => define({ deps: {}, factory() { return { captureEvent: event => { window._AcmeAnalyticsQ.push(event); }, }; }, }), });更完整的实现通常会封装初始化逻辑并从配置读取参数(旧版前端系统使用createApiFactory(analyticsApiRef, ...),见 plugins/analytics.md 文档):
import { AnalyticsApi, AnalyticsEvent, configApiRef, } from '@backstage/frontend-plugin-api'; import { AnalyticsImplementationBlueprint } from '@backstage/plugin-app-react'; import { AcmeAnalytics } from 'acme-analytics'; class AcmeAnalyticsImpl implements AnalyticsApi { private constructor(accountId: number) { AcmeAnalytics.init(accountId); } static fromConfig(config) { const accountId = config.getString('app.analytics.acme.id'); return new AcmeAnalyticsImpl(accountId); } captureEvent(event: AnalyticsEvent) { const { action, ...rest } = event; AcmeAnalytics.send(action, rest); } } export const acmeAnalyticsImplementation = AnalyticsImplementationBlueprint.make({ name: 'acme', params: define => define({ deps: { configApi: configApiRef }, factory: ({ configApi }) => AcmeAnalyticsImpl.fromConfig(configApi), }), });按社区惯例,此类集成包应命名为@backstage/analytics-module-[name],配置统一放在app.analytics.[name]键下。如果分析平台有一等公民的用户身份概念,可约定以identityApi作为依赖,并用identityApi.getBackstageIdentity()解析出的userEntityRef作为发送给平台的基础用户 ID。
捕获自定义事件
在组件中通过useAnalytics()钩子获取 tracker(来自@backstage/frontend-plugin-api),其captureEvent方法接收action与subject参数:
import { useAnalytics } from '@backstage/frontend-plugin-api'; const analytics = useAnalytics(); analytics.captureEvent('deploy', serviceName);捕获的事件应反映用户意图和插件专属的领域动作,而不是通用点击或 UI 生命周期事件。许多@backstage/ui组件(如Link、ButtonLink、Tab、MenuItem、Tag、Table行)会自动捕获click事件,因此通常不需要手动插桩导航类点击;如果默认事件不满足需求,可传入noTrackprop 关闭默认捕获,并在自己的点击处理器中调用captureEvent。
附加属性与数值:第三个options参数可携带维度attributes和数值value:
analytics.captureEvent('merge', pullRequestName, { value: pullRequestAgeInMinutes, attributes: { org, repo, }, });对应捕获到的事件对象:
{ "action": "merge", "subject": "Name of Pull Request", "value": 60, "attributes": { "org": "some-org", "repo": "some-repo" } }上下文提供:对于只在 React 树上层的元数据,使用<AnalyticsContext>包裹(context 可嵌套,沿 React 树向下合并,允许键被覆盖;核心自动附带的pluginId、extensionId始终存在):
import { AnalyticsContext, useAnalytics } from '@backstage/frontend-plugin-api'; const MyComponent = ({ value }) => { const analytics = useAnalytics(); const handleClick = () => analytics.captureEvent('check', value); return <SomeThing value={value} onClick={handleClick} />; }; const MyWrapper = () => { return ( <AnalyticsContext attributes={{ segment: 'xyz' }}> <MyComponent value={'Some Value'} /> </AnalyticsContext> ); };事件命名建议:避免使用过于具体的action(例如用filter而非filterEntityTable,让extensionId自动携带EntityTable上下文);添加 attributes/context 时参考既有事件,保持键的意图、类型甚至内容一致(例如 Catalog 相关事件通常包含entityRef上下文键),以便跨插件聚合分析。
单元测试:@backstage/frontend-test-utils提供MockAnalyticsApi,可在测试中配合TestApiProvider注入analyticsApiRef,用apiSpy.getEvents()断言捕获的事件内容(action、subject、attributes 等)。
前端错误上报
除了行为分析,还应考虑接入客户端错误上报服务(如 Sentry、CloudWatch RUM、Cloudflare RUM),用于捕获和诊断前端运行时错误。这是对 Analytics 的有力补充:分析回答"用户在做什么",错误上报回答"用户遇到了什么问题"。
日志:结构化 JSON 输出
Backstage 后端默认向 stdout 输出结构化 JSON 日志,包含service、plugin、level、message等字段,可直接被 Elasticsearch、Datadog、Splunk 等日志聚合工具解析。这意味着你无需编写额外的日志解析逻辑,只需将 stdout 采集到日志管道即可。
实际使用建议:
- 在 Kubernetes 中,容器 stdout 会被 kubelet 自动收集到节点日志目录,配合 DaemonSet 日志采集器(如 Filebeat、Fluentd)即可汇入聚合平台。
- 可基于
level字段(如error、warn)配置日志告警,与指标告警互为补充。 plugin字段可用于按插件维度分析错误分布,快速定位问题插件。
下一步:规模化监控
随着更多用户采用你的 Backstage 实例,监控数据会帮助你判断何时需要扩容——例如 API 响应时间上升、catalog.processing.duration显示 Catalog 处理落后、scaffolder 任务排队时间超出预期、用户反馈页面加载缓慢等信号。扩容前优先考虑水平扩展(增加副本),这比拆分后端更简单且能覆盖大多数增长场景。详见 Scaling your deployment 与 Scaling Backstage Deployments。
参考资源
- 本指南出处:Golden Path 部署系列 · Monitoring your deployment
- OpenTelemetry 完整接入教程:Setup OpenTelemetry
- 前端分析完整文档:Plugin Analytics(新前端系统) 与 Plugin Analytics(旧文档)
- 健康检查端点实现:createHealthRouter.ts
- Catalog 指标实现:database/metrics.ts
- Scaffolder 指标实现:NunjucksWorkflowRunner.ts
【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考