SSR 应用如何做日志、指标和链路追踪?
简化版
SSR 可观测性要同时覆盖服务端渲染和浏览器体验。服务端关注请求耗时、渲染耗时、接口耗时、错误率、状态码、缓存命中率;客户端关注 FCP、LCP、TTFB、水合耗时和脚本错误。最好用 request id 或 trace id 把浏览器请求、SSR 服务、后端接口和日志串起来,这样才能定位慢页面到底慢在渲染、数据、网络还是客户端水合。
详细版
SSR 的问题常常跨端:用户看到慢,可能是 CDN 慢、SSR server 慢、后端接口慢,也可能是 HTML 快但 hydration 卡住。只看前端埋点或只看服务端日志都不够。
落地时需要三层数据:日志用于排查个案,指标用于看整体趋势,链路追踪用于串起依赖。关键字段包括 URL、状态码、用户地区、缓存状态、接口耗时、渲染耗时和 trace id。
面试中要表达“可观测性是为了定位责任边界”,不是单纯接一个监控 SDK。
完整版教学
一、SSR 的故障边界更长
CSR 页面主要关注静态资源和接口。
SSR 页面多了服务端渲染层。
一次页面访问可能经过 CDN、负载均衡、SSR 服务、后端 API、缓存和数据库。
任何一层慢,用户都可能感知首屏慢。
二、核心指标
| 指标 | 含义 | 定位价值 |
|---|---|---|
| TTFB | 首字节时间 | 判断服务端和网络耗时 |
| render duration | SSR 渲染耗时 | 判断模板和组件渲染成本 |
| API duration | 后端接口耗时 | 判断数据依赖瓶颈 |
| cache hit ratio | 缓存命中率 | 判断缓存策略是否有效 |
| hydration duration | 水合耗时 | 判断客户端 JS 成本 |
| error rate | 错误率 | 判断稳定性 |
三、日志应该记录什么
每个请求要有 request id。
记录路径、状态码、耗时、用户代理、地区、缓存状态。
记录关键接口耗时和失败原因。
错误日志要带堆栈,但不能泄露 Cookie、token、手机号等敏感信息。
SSR 日志很容易碰到用户请求头和注水数据,脱敏是基础要求。
四、链路追踪怎么串起来
入口生成 trace id。
SSR 调用后端接口时透传 trace id。
后端日志和网关日志也记录同一 trace id。
浏览器端性能上报带上服务端返回的 request id。
这样一次慢请求可以从用户体验追到具体接口。
五、代码示例
export async function handleSsr(req) {
const traceId = req.headers["x-trace-id"] ?? crypto.randomUUID();
const start = performance.now();
try {
const data = await fetchData({ traceId });
return render(data, { traceId });
} finally {
log.info({
traceId,
path: req.url,
duration: performance.now() - start,
});
}
}
代码里要注意 finally 记录收尾,避免异常请求没有日志。
六、告警如何设计
只按错误数告警容易被流量影响。
更合理的是错误率、P95/P99 耗时、缓存命中率下降、特定状态码异常增加。
核心页面和普通页面阈值可以不同。
发布后要重点观察新版本的 SSR 错误和 TTFB。
七、误区和追问
- 误区:接了前端性能 SDK 就覆盖 SSR 了。 前端 SDK 看不到服务端内部接口耗时和渲染耗时。
- 误区:服务端日志越多越好。 日志过多会增加成本,也可能泄露敏感信息。
- 误区:平均耗时能代表用户体验。 SSR 性能更要看 P95、P99 和地区分布。
- 追问:TTFB 高怎么定位? 先拆 CDN、SSR 排队、接口耗时、渲染耗时和缓存命中。
- 追问:如何关联浏览器错误和服务端日志? 在 HTML 或响应头下发 request id,客户端上报时带回。
- 追问:SSR 监控要不要区分机器人流量? 要,爬虫和真实用户的路径、频率、性能特征都可能不同。
八、面试表达方式
可以按“指标看趋势、日志查个案、trace 串依赖、告警守底线”来回答。
这个框架比简单罗列监控项更像真实项目方案。