- 可观测性
- 后端
【免费下载链接】highlight
highlight.io: The open source, full-stack monitoring platform. Error monitoring, session replay, logging, distributed tracing, and more.
本篇指南围绕 Next.js 应用中分布式追踪(Distributed Tracing)的完整落地展开:从浏览器客户端注入追踪上下文、Next.js 服务端中间件传播 W3C Trace Context 头,到下游微服务接收 traceparent 并关联同一 Trace,最终在 Highlight 平台中可视化跨服务的瀑布流。读完本文,你将掌握 Highlight Next.js SDK 与 Node/Go SDK 的接入方式、withHighlightConfig各配置项的默认行为,以及propagation.extract/inject在源码中如何串联起一次完整的跨服务调用链。
什么是分布式追踪
分布式追踪用于理解和监控跨多个服务运行的复杂应用的执行情况:它把分散在不同服务中的操作(span)链接进同一条 trace,从而更容易定位性能瓶颈和排查问题。
在 Next.js 场景下,这条链路通常覆盖三段:
- 浏览器端:用户交互、页面渲染产生的操作;
- Next.js 服务端:middleware、API 路由、服务端组件中的业务逻辑;
- 下游微服务:Next.js 的 API 路由发起的 HTTP 调用(例如 Go 微服务)。
要实现三者关联,关键在于追踪上下文(trace context)的跨进程传播。Highlight 采用的是 W3C 标准traceparent/tracestate头(由 OpenTelemetry 的W3CTraceContextPropagator实现),此外还携带一个 Highlight 特有的x-highlight-request头,用于把服务端操作关联到触发该请求的用户会话(Session Replay)。
整体数据流
从源码结构看,完整的数据流如下:
浏览器 (highlight.run) │ 1. 发出 fetch 请求,自动携带 W3C trace context 头 │ 2. 初始化时把 sessionSecureID 写入 cookie ▼ Next.js middleware (highlightMiddleware) │ 3. 读取 cookie,补写 x-highlight-request 头 ▼ API 路由 (withHighlight / runWithHeaders) │ 4. propagation.extract 提取入站上下文 → 创建服务端 span │ 5. propagation.inject 把 traceparent 注入出站请求头 ▼ Go 微服务 (chi middleware + Go OTel SDK) 6. 提取 traceparent,挂到同一条 trace,输出 OTLP第一步:客户端安装与初始化
在 Next.js 项目中安装 Highlight 客户端 SDK:
yarn add @highlight-run/next在 App Router 的app/layout.tsx(或 Pages Router 的_app.tsx)中渲染HighlightInit组件:
import { HighlightInit } from '@highlight-run/next/client' export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <body> <HighlightInit projectId="your-project-id" excludedHostnames={['localhost']} /> {children} </body> </html> ) }客户端初始化完成后会自动为浏览器发出的请求注入追踪头。看 客户端入口源码 可以发现两件对追踪链路很关键的事:
- 会话 Cookie:
localH.init返回的sessionSecureID会被写入名为sessionSecureID的 cookie(next-client.tsx#L42-L49)。这是后续服务端把操作关联回会话回放(Session Replay)的基础。 - 代理模式:如果构建期注入了
configureHighlightProxy === 'true'(默认开启,见下文withHighlightConfig),初始化选项会被改写为backendUrl: '/highlight-events'和otlpEndpoint: window.location.origin(next-client.tsx#L32-L40)。也就是说,浏览器上报的 OTLP 数据(/v1/traces、/v1/metrics、/v1/logs)会走你自己的域名,由 Next.js 代理转发到 Highlight,避免跨域与混合内容问题。
第二步:服务端 middleware 传播请求上下文
客户端 cookie 只是标识,真正把追踪头“补齐”并交给后续逻辑的是 Next.js 的middleware.ts:
// middleware.ts import { highlightMiddleware } from '@highlight-run/next/server' import { NextResponse } from 'next/server' export async function middleware(request: Request) { await highlightMiddleware(request) return NextResponse.next() }仓库中的 e2e 示例 e2e/nextjs/middleware.ts 与上面完全一致。highlightMiddleware的实现非常轻量(highlight-middleware.ts#L3-L9):
export async function highlightMiddleware(request: Request) { const sessionSecureID = (await cookies()).get('sessionSecureID')?.value const xHighlightRequest = request.headers.get('x-highlight-request') if (!xHighlightRequest && sessionSecureID) { request.headers.set('x-highlight-request', `${sessionSecureID}/`) } }它从 cookie 取出sessionSecureID,在请求头不存在x-highlight-request时补写一个${sessionSecureID}/格式的值。这个头会一路传到 API 路由,服务端 SDK 据此解析出secureSessionId和requestId(见 client.ts 的 parseHeaders),把 span 打上highlight.session_id属性,从而在后端日志、错误和 trace 与前端会话回放之间建立关联。
第三步:withHighlightConfig 与 OTLP 代理配置
为了让浏览器与 Next.js 服务端能上报 OTLP 数据,Next.js 需要用withHighlightConfig包装配置文件。它会自动在rewrites中追加以下代理规则(源码见 with-highlight-config.ts#L141-L158):
| source | destination | 用途 |
|---|---|---|
/highlight-events | https://pub.highlight.io | 事件/会话等通用上报代理 |
/v1/traces | https://otel.highlight.io/v1/traces | OTLP trace 上报代理 |
/v1/metrics | https://otel.highlight.io/v1/metrics | OTLP metric 上报代理 |
/v1/logs | https://otel.highlight.io/v1/logs | OTLP log 上报代理 |
配置示例:
// next.config.mjs import { withHighlightConfig } from '@highlight-run/next/config' /** @type {import('next').NextConfig} */ const nextConfig = {} export default withHighlightConfig(nextConfig, { uploadSourceMaps: false, // 是否上传 source map(默认:生产构建且配置了 apiKey 时为 true) configureHighlightProxy: true, // 是否注入 /highlight-events、/v1/* 代理重写(默认 true) apiKey: process.env.HIGHLIGHT_SOURCEMAP_UPLOAD_API_KEY, environment: process.env.ENVIRONMENT, serviceName: 'my-nextjs-app', })各选项的默认值可以直接从 getDefaultOpts 的源码确认:
| 选项 | 默认值 | 说明 |
|---|---|---|
uploadSourceMaps | 生产构建(NODE_ENV === 'production')且配置了apiKey或HIGHLIGHT_SOURCEMAP_UPLOAD_API_KEY时为true | 启用后会改写 webpackdevtool并注入上传插件,同时把/:path*.map重写为 404 以防 source map 泄漏 |
configureHighlightProxy | true | 注入上表中的 4 条代理 rewrite,并把configureHighlightProxy作为构建期环境变量暴露给客户端代码 |
apiKey | '' | 上传 source map 时关联项目用的 API key,也可用环境变量HIGHLIGHT_SOURCEMAP_UPLOAD_API_KEY提供 |
appVersion | 优先取 NextgenerateBuildId()的结果 | 上传 source map 时使用的版本标识 |
environment | '' | 环境标识,用于区分 localhost 与生产环境的会话 |
serviceName | '' | 应用名 |
sourceMapsPath | '.next/' | source map 文件根目录 |
sourceMapsBasePath | '_next/' | 上传后 source map URL 前缀 |
sourceMapsBackendUrl | 未设置(自托管部署时可选) | 自托管 Highlight 部署的后端地址 |
需要注意:withHighlightConfig兼容next.config的三种形态(对象、同步函数、异步函数),见 withHighlightConfig 入口。
第四步:API 路由的追踪包装与 W3C 上下文传播
Next.js 的 API 路由(以及 Route Handler)需要用 Node SDK 的 handler 包装,让每个请求都运行在一个从入站头提取出来的 span 上下文里。仓库 e2e 中封装好的用法见 e2e/nextjs/pages/api/page-router-trace.ts:
import { withPageRouterHighlight } from '@/app/_utils/page-router-highlight.config' import { H } from '@highlight-run/next/server' import { context, propagation } from '@opentelemetry/api' export default withPageRouterHighlight(async function handler( req: NextApiRequest, res: NextApiResponse, ) { const { span } = H.startWithHeaders('page-router-span', {}) const headers = {} // 1) 手动复制入站头(含 W3C traceparent)转发给 Go 服务 await fetch('http://localhost:3010/x-highlight-request', { method: 'GET', headers: Object.entries(req.headers).reduce( (acc, [key, value]) => ({ ...acc, [key, value ]: value }), {}, ), }) // 2) 用 OTel propagation API 把当前活跃 span 注入出站头 propagation.inject(context.active(), headers) await fetch('http://localhost:3010/traceparent', { method: 'GET', headers }) res.send('Trace sent!') span.end() })包装器内部做的事情,本质上是 Node SDK 的runWithHeaders。从 client.ts#L425-L475 的实现可以看到完整的 W3C Trace Context 传播闭环:
async runWithHeaders<T>(name, headers, cb, options?) { const { span, ctx } = this.startWithHeaders(name, headers, options) return await api.context.with(ctx, async () => { propagation.inject(ctx, headers) // 注入 traceparent,供出站请求继承 try { return await cb(span) } catch (error) { span.recordException(error) throw error } finally { span.end() } }) } startWithHeaders(spanName, headers, options?) { const ctx = propagation.extract(api.context.active(), headers) // 1. 提取入站上下文 const span = this.tracer.startSpan(spanName, options, ctx) // 2. 派生子 span const contextWithSpanSet = api.trace.setSpan(ctx, span) let { secureSessionId, requestId } = this.parseHeaders(headers) // 3. 解析 x-highlight-request // ...将 secureSessionId 写入 span 属性 highlight.session_id 并放入 baggage propagation.inject(contextWithSpanSet, headers) // 4. 注入更新后的上下文 return { span, ctx: contextWithSpanSet } }而W3CTraceContextPropagator正是该 SDK 注册的传播器(见 client.ts#L221-L237 的 provider 配置),因此propagation.extract/inject读写的就是标准traceparent头。这条链路解释了文章开头所说的机制:服务端从入站请求提取 trace 头,创建属于当前请求的 span,再把更新后的上下文注入到所有出站请求头中——下游服务只要解析traceparent,就能把自身操作挂到同一条 trace 上,同时仍可为任意代码块开启新的嵌套 span。
对于 Express 风格的框架,Node SDK 还提供了通用的 middleware 与 errorHandler,例如Handlers.middleware(options)会在每个请求上调用H.runWithHeaders('GET - /url', req.headers, ...),并自动附上http.request.method、http.route、http.response.status_code等语义约定属性;Handlers.errorHandler则通过parseHeaders把错误关联回对应的会话与请求(handlers.ts#L23-L42)。
第五步:下游微服务接收 traceparent
以仓库中的 Go 示例服务为例(e2e/nextjs/go-service/main.go),Highlight 的 Go SDK 基于 OpenTelemetry Go 模块,初始化如下:
highlight.SetDebugMode(logger) highlight.SetOTLPEndpoint("http://localhost:4318") highlight.Start( highlight.WithServiceName("my-go-service"), highlight.WithProjectID("1"), highlight.WithSamplingRateMap(map[trace.SpanKind]float64{ trace.SpanKindServer: 1., }), ) defer highlight.Stop()Go 服务用对应框架的中间件接收上下文,SDK 在sdk/highlight-go/middleware/下为多个常见框架提供了开箱即用的实现:
- chi:
func Middleware(next http.Handler) http.Handler - echo
- fiber
- gin
- gorillamux
以 chi 为例,在路由注册前先套一层即可:
r.Use(highlightChi.Middleware)中间件内部使用 OTel 的propagation包从入站头提取traceparent,使 Go 服务创建的 server span 直接成为 Next.js span 的子 span。从源码结构看,Go SDK 还在 highlight.go#L90-L108 中定义了自己的 context key(HighlightRequestID、HighlightSessionSecureID),用于在服务内跨层传递 Highlight 会话与请求标识,实现与x-highlight-request头相同的会话关联能力。
可用的初始化选项(均为 functional options 风格,见 highlight.go#L49-L88)包括:WithProjectID、WithServiceName、WithServiceVersion、WithEnvironment、WithSamplingRate与WithSamplingRateMap(可按trace.SpanKind精细控制采样率)。
端到端可视化:瀑布流视图
完成上述接入后,一次请求就会产生一条跨三段的 trace:浏览器操作 → Next.js 服务端 span → Go 服务 span。在 Highlight 的 Trace 视图中,瀑布流(waterfall)会清晰展示每个 span 的时间窗口与父子关系,可以逐层展开定位是哪一段服务、哪一个操作耗时最长。由于会话 cookie 关联,你还可以从 Trace 直接跳转回触发该请求的用户会话回放。
与 OpenTelemetry、W3C 标准的关系
需要强调两点,这也是 Highlight 选择这套方案的原因:
- W3C Trace Context 是开放标准:
traceparent/tracestate头的传播方式与任何可观测性厂商的追踪工具兼容,接入 Highlight 不会造成供应商锁定——同一批头可以被 Jaeger、Zipkin、Honeycomb 等任何遵循该标准的基础设施消费。 - OpenTelemetry 提供了更厂商中立的 SDK 层:Highlight 的 Node SDK(
@highlight-run/node)与 Go SDK 都构建在官方 OpenTelemetry 包之上(如 Node 端依赖@opentelemetry/api、@opentelemetry/propagator-b3体系中的W3CTraceContextPropagator,Go 端依赖go.opentelemetry.io/otel全家桶,见 go.mod)。这意味着你可以复用 OTel 的 API、语义约定和生态中间件,同时把数据上报到 Highlight 的 OTLP 端点。
关键文件索引
| 内容 | 路径 |
|---|---|
| 客户端初始化组件(cookie、代理模式) | sdk/highlight-next/src/next-client.tsx |
| 服务端 middleware(x-highlight-request) | sdk/highlight-next/src/util/highlight-middleware.ts |
| next.config 包装与 OTLP 代理重写 | sdk/highlight-next/src/util/with-highlight-config.ts |
| W3C 上下文提取/注入(runWithHeaders) | sdk/highlight-node/src/client.ts |
| Express/serverless handler | sdk/highlight-node/src/handlers.ts |
| Go SDK 初始化与选项 | sdk/highlight-go/highlight.go |
| Go 框架中间件(chi/echo/fiber/gin/gorillamux) | sdk/highlight-go/middleware/ |
| e2e 完整示例(Next.js + Go 服务) | e2e/nextjs/middleware.ts、e2e/nextjs/pages/api/page-router-trace.ts、e2e/nextjs/go-service/main.go |
小结
在 Next.js 中落地分布式追踪的核心是“提取—注入”这一对称操作:客户端 SDK 为请求自动附加追踪头并维护会话 cookie;highlightMiddleware把会话标识补齐到请求头;withHighlightConfig配置 OTLP 代理;API 路由经runWithHeaders/startWithHeaders从traceparent提取上下文、创建子 span 并注入出站头;Go 微服务再用标准中间件提取上下文挂入同一条 trace。整条链路只依赖 W3C Trace Context 开放标准,既能在 Highlight 中获得端到端瀑布流与会话回放的联动,也能被任何兼容 OTel 的基础设施消费。
- 可观测性
- 后端
【免费下载链接】highlight
highlight.io: The open source, full-stack monitoring platform. Error monitoring, session replay, logging, distributed tracing, and more.
相关推荐
Plano 全链路追踪实战:基于 OpenTelemetry 与 W3C Trace Context 的 AI Agent 可观测性指南
Plano 全链路追踪实战:基于 OpenTelemetry 与 W3C Trace Context 的 AI Agent 可观测性指南 本文以 Plano 的
人工智能大模型后端API网关LLM 网关AI AgentAgent 编排可观测性AI 安全治理提示词注入防护GoFr 分布式追踪实战:基于 W3C TraceContext 的跨服务全链路可观测
GoFr 分布式追踪实战:基于 W3C TraceContext 的跨服务全链路可观测 GoFr 在框架层内置了基于 OpenTelemetry 的分布式追踪能
后端微服务云原生可观测性枪口飘上天别再忍!保姆级开源压枪宏上手笔记,让游戏外设自动化帮你稳住每一梭子
枪口飘上天别再忍!保姆级开源压枪宏上手笔记,让游戏外设自动化帮你稳住每一梭子 还记得我第一次用满配 M416 打房区遭遇战:前三发像模像样,第四发准星开始画彩虹
可观测性后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考