切换语言
切换主题

Next.js 生产环境监控完全指南:Sentry 集成、日志管理与告警配置实战

周五晚上 9 点 17 分,手机震了。

微信群里已经炸锅——“支付页面打不开”、“刚才下单失败了”、“白屏了白屏了”。我打开电脑连上 VPN,本地跑一遍,完美运行。查看服务器日志,除了一行”Internal Server Error”什么都没有。用户说点击支付按钮后页面就卡死,但我完全复现不了。

那个周末我几乎没睡。凌晨两点翻 Vercel 部署日志,早上六点重新部署测试版本,最后发现是一个第三方支付 SDK 在生产环境偶发性超时。整个排查过程像是在黑暗里找掉落的针。

说实话,这不是个例。你的 Next.js 应用在开发环境可以丝般顺滑,但上线后就像打开了潘多拉魔盒——服务端渲染偶尔 500、边缘函数莫名其妙报错、API 响应时间飙升却不知道瓶颈在哪。

问题的根源很简单:你缺少一套完整的生产环境监控体系。

这篇文章会手把手教你搭建 Next.js 监控系统,从 Sentry 错误追踪到结构化日志管理,从性能监控到告警配置。不是理论堆砌,全是能直接复制粘贴的配置代码。读完这篇,下次线上出问题,你会比用户更早知道。

为什么 Next.js 需要专门的监控方案

Next.js 的「三头怪」特性

传统前端应用只跑在浏览器,错误直接在 DevTools 控制台看到。Next.js 不一样——同一个应用同时跑在三个完全不同的地方:

  • 客户端(Browser):用户浏览器里的 React 组件
  • 服务端(Node.js):SSR 渲染、API Routes、Server Actions
  • 边缘网络(Edge Runtime):中间件、边缘函数

一个支付功能可能涉及:客户端表单验证 → 中间件鉴权 → Server Action 调用 → API Route 查数据库 → 返回客户端展示结果。任何一个环节出错,传统的浏览器监控都看不到全貌

去年我遇到过一个很诡异的bug:用户反馈”页面加载很慢,然后就显示 500”。浏览器 Network 面板显示请求确实慢,但具体慢在哪不知道。后来接入 Sentry 的分布式追踪,才发现是服务端渲染时调用了一个第三方 API,这个 API 响应时间从平时的 200ms 飙到了 8 秒。客户端监控永远发现不了这种问题。

SSR 的「黑盒效应」

服务端渲染出错时,用户看到的往往只有一个光秃秃的 500 页面。没有 stack trace,没有上下文,什么都没有

更要命的是 Hydration 错误。你可能见过这个警告:

Warning: Expected server HTML to contain a matching <div> in <div>

这种错误在开发环境可能不太明显,但在生产环境可能导致整个页面交互失效。没有监控系统,你只能靠用户反馈”页面点不动了”来被动发现。

根据 Vercel 的数据,SSR 相关的错误占 Next.js 生产环境问题的 35% 左右。这还只是错误,不包括性能问题——比如某个组件的 SSR 耗时突然变长,用户感觉页面”打开变慢了”,但你完全不知道瓶颈在哪。

完整监控的四个支柱

一个靠谱的 Next.js 监控方案需要覆盖这几点:

错误追踪
不只是捕获异常,还要知道:谁触发的错误(用户信息)、在什么环境(设备、浏览器、网络)、做了什么操作(面包屑追踪)、相关的请求参数是什么。

性能监控
LCP(最大内容绘制)有没有超过 2.5 秒? API 接口响应时间是不是变慢了? 哪个数据库查询拖慢了整个请求?

日志管理
结构化日志,能按时间、用户、请求 ID 快速检索。开发环境美化输出方便调试,生产环境接入日志平台统一分析。

告警配置
错误率超过阈值立即通知团队,新类型错误首次出现就推送 Slack,性能回归自动触发告警。

有了这四个,线上出问题时你不再是”盲人摸象”。接下来咱们一个个攻破。

Sentry 集成实战 - 从安装到深度配置

5 分钟快速接入

Sentry 对 Next.js 的支持已经很成熟了,官方提供了自动配置向导。真的只需要 5 分钟:

# 安装 SDK
npm install @sentry/nextjs

# 运行配置向导
npx @sentry/wizard@latest -i nextjs

向导会问你几个问题(Sentry项目 DSN、是否上传 Source Maps等),然后自动创建三个配置文件:

  • sentry.client.config.ts - 浏览器环境
  • sentry.server.config.ts - Node.js 服务端
  • sentry.edge.config.ts - Edge Runtime

还会修改 next.config.js,加入 Sentry 的 webpack 插件。跑一遍向导,基础监控就通了

但这只是开始。生产环境需要更精细的配置。

App Router 的错误捕获要点

如果你用的是 App Router,有几个地方需要特别注意:

全局错误处理

创建 app/global-error.tsx,这是 App Router 的最后一道防线:

'use client';

import * as Sentry from '@sentry/nextjs';
import { useEffect } from 'react';

export default function GlobalError({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  useEffect(() => {
    // 发送到 Sentry
    Sentry.captureException(error);
  }, [error]);

  return (
    <html>
      <body>
        <div style={{ padding: '2rem', textAlign: 'center' }}>
          <h2>出了点问题</h2>
          <p>我们已经记录了这个错误,会尽快修复</p>
          <button onClick={() => reset()}>重试</button>
        </div>
      </body>
    </html>
  );
}

Server Actions 错误捕获

Server Actions 是 App Router 的杀手级功能,但错误处理经常被忽略:

'use server';

import * as Sentry from '@sentry/nextjs';

export async function createOrder(formData: FormData) {
  return await Sentry.withServerActionInstrumentation(
    'createOrder', // action 名称,会显示在 Sentry
    {
      recordResponse: true, // 记录响应数据
    },
    async () => {
      // 你的业务逻辑
      const productId = formData.get('productId');
      const order = await db.order.create({
        data: { productId, userId: getCurrentUserId() },
      });
      return order;
    }
  );
}

这样包装后,Server Action 里的任何错误都会自动上报,还能追踪执行时间。

生产环境优化配置

接入 Sentry 后,第一个月账单可能会吓你一跳——因为默认配置会上报所有事件。需要调整采样率:

// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs';

Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,

  // 性能追踪采样率
  // 开发环境 100%,生产环境 10%(根据流量调整)
  tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,

  // Session Replay 采样
  replaysSessionSampleRate: 0.1,      // 10% 的正常会话录制
  replaysOnErrorSampleRate: 1.0,      // 100% 的错误会话录制

  // 环境标识
  environment: process.env.NEXT_PUBLIC_VERCEL_ENV || 'development',

  // 忽略特定错误
  ignoreErrors: [
    // 浏览器扩展注入的错误
    'ResizeObserver loop limit exceeded',
    // 第三方脚本错误
    /chrome-extension/,
    /^Non-Error promise rejection/,
  ],
});

tracesSampleRate 设置指南:

  • 日 UV < 1 万: 0.2 - 0.5
  • 日 UV 1-10 万: 0.1 - 0.2
  • 日 UV > 10 万: 0.05 - 0.1

我们项目日 UV 3 万左右,设置 0.15,每个月大概用掉 Sentry 配额的 60%,既能覆盖足够样本,又不会超支。

Source Maps:可调试但不泄露代码

生产环境的 JavaScript 代码通常是压缩混淆的,错误堆栈看起来像这样:

at r.render (app.js:1:23456)

完全看不懂。Source Maps 能把混淆代码映射回原始代码,但直接暴露 Source Maps 会泄露源码。

Sentry 的做法是:上传 Source Maps 到 Sentry 服务器,用户浏览器拿不到,只有 Sentry 内部用来还原堆栈。

在 CI/CD 中配置(以 GitHub Actions 为例):

# .github/workflows/deploy.yml
- name: Upload Source Maps to Sentry
  env:
    SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
    SENTRY_ORG: your-org
    SENTRY_PROJECT: your-project
  run: npm run build

next.config.js 里 Sentry 插件会自动处理上传。记得把 SENTRY_AUTH_TOKEN 加到 GitHub Secrets,别提交到代码库。

高级功能:Session Replay 和分布式追踪

Session Replay 是我最喜欢的功能——回放用户操作,像看录像一样还原现场。

某次有个用户反馈”支付按钮点不了”,我看了他的 Session Replay,发现他用的是 iPad横屏,支付按钮被虚拟键盘挡住了。这种问题单靠错误日志永远发现不了。

开启很简单,在客户端配置里加:

import * as Sentry from '@sentry/nextjs';
import { Replay } from '@sentry/nextjs';

Sentry.init({
  integrations: [
    new Replay({
      maskAllText: false,           // 是否隐藏所有文本
      blockAllMedia: true,          // 是否阻止所有媒体
      maskAllInputs: true,          // 隐藏表单输入(避免泄露敏感信息)
    }),
  ],
  replaysSessionSampleRate: 0.1,
  replaysOnErrorSampleRate: 1.0,
});

分布式追踪能跟踪一个请求的完整生命周期。用户点击按钮 → 前端发请求 → API Route 查数据库 → 返回前端渲染,每一步的耗时都能看到。

配置也不复杂,确保前后端用同一个 Sentry.init 配置,Sentry SDK 会自动在请求头里传递 sentry-trace

自定义上下文:让错误更有意义

默认情况下 Sentry 只知道”发生了一个错误”。通过自定义上下文,能让错误报告更有价值:

import * as Sentry from '@sentry/nextjs';

// 设置用户信息
Sentry.setUser({
  id: user.id,
  email: user.email,
  username: user.username,
  // 别放密码之类的敏感信息!
});

// 添加业务上下文
Sentry.setContext('purchase', {
  orderId: '12345',
  amount: 99.99,
  paymentMethod: 'credit_card',
});

// 添加标签(方便筛选)
Sentry.setTag('feature', 'checkout');
Sentry.setTag('ab_test', 'variant_b');

这样当错误发生时,你能立刻知道是哪个用户、在什么业务场景下触发的。

真实案例:我们有次发现一个支付错误的发生频率比预期高,查看 Sentry 上下文发现全是 ab_test: variant_b 的用户。定位到 A/B 测试的新版支付流程有 bug,立刻关闭了这个变体,避免了更大的损失。

日志管理 - 让日志为你工作

console.log 不够用了

早期我也喜欢到处 console.log。调试时看起来挺爽,但上线后就抓瞎了:

  • 无法筛选: 10 万条日志里找一个用户的请求记录? 祝你好运。
  • 无法聚合: 想知道过去一小时有多少个数据库查询慢于 1 秒? 没法统计。
  • 无法告警: 日志里出现”Payment failed”? 没人知道。

结构化日志解决了这些问题。不是简单打印字符串,而是输出 JSON 对象,每个日志带上时间戳、日志级别、请求 ID、用户 ID 等元数据。后续可以按任意字段检索和聚合。

Pino vs Winston:怎么选

Node.js 生态两大日志库,各有千秋:

特性PinoWinston
性能超快,异步日志几乎无开销稍慢,但依然够用
易用性配置简洁,开箱即用功能丰富,插件生态好
可扩展性通过 Transport 扩展内置多种 Transport
社区Next.js 官方推荐老牌库,文档最全

我的建议:

  • 高并发场景(QPS > 1000):选 Pino,性能优势明显
  • 需要复杂日志处理(多格式、多目标):选 Winston
  • 不知道选啥:选 Pino,Next.js 官方文档都在用它

Pino 实战配置

先安装:

npm install pino
npm install pino-pretty --save-dev  # 开发环境美化输出

创建全局 logger:

// lib/logger.ts
import pino from 'pino';

const logger = pino({
  level: process.env.LOG_LEVEL || 'info',

  // 格式化日志级别
  formatters: {
    level: (label) => ({ level: label.toUpperCase() }),
  },

  // 开发环境使用 pino-pretty 美化输出
  transport: process.env.NODE_ENV === 'development'
    ? {
        target: 'pino-pretty',
        options: {
          colorize: true,
          translateTime: 'HH:MM:ss',
          ignore: 'pid,hostname',
        },
      }
    : undefined,
});

export { logger };

开发环境输出会是彩色的、易读的格式,生产环境是 JSON,方便日志平台解析。

在 API Route 中使用

关键是给每个请求分配一个 correlationId(关联 ID),把这个请求相关的所有日志串起来:

// app/api/products/route.ts
import { logger } from '@/lib/logger';
import { randomUUID } from 'crypto';

export async function GET(request: Request) {
  // 生成请求唯一 ID
  const correlationId = request.headers.get('x-correlation-id') || randomUUID();

  // 创建子 logger,自动带上 correlationId
  const log = logger.child({ correlationId });

  try {
    log.info({ url: request.url }, 'Processing product request');

    const products = await db.product.findMany();

    log.info({ count: products.length }, 'Products fetched successfully');

    return Response.json(products);
  } catch (error) {
    log.error({ error: error.message, stack: error.stack }, 'Failed to fetch products');
    throw error;
  }
}

这样所有带同一个 correlationId 的日志都能关联起来,排查问题时直接搜这个 ID,整个请求链路一目了然。

日志级别最佳实践

日志太少没用,太多淹没关键信息。我的分级标准:

ERROR - 需要立即处理的问题

  • 数据库连接失败
  • 支付接口调用失败
  • 关键业务逻辑抛异常
log.error({ error, userId, orderId }, 'Payment processing failed');

WARN - 异常但可恢复的情况

  • API 调用重试成功
  • 降级逻辑触发
  • 接近配额限制
log.warn({ retryCount: 3 }, 'External API retry succeeded');

INFO - 关键业务节点

  • 用户登录/登出
  • 订单创建/完成
  • 关键配置变更
log.info({ userId, ip }, 'User logged in');

DEBUG - 详细调试信息

  • 函数参数和返回值
  • 中间状态
  • 性能计时
log.debug({ params }, 'Calling external API');

生产环境默认 INFO 级别,出问题时临时调到 DEBUG 排查。

日志聚合与分析

本地开发用 pino-pretty 就够了,生产环境需要接入日志平台。几个主流选择:

Vercel Logs
如果部署在 Vercel,内置日志系统,零配置。但只保留 7 天,搜索功能有限。

Datadog
企业级方案,APM + 日志 + 监控全家桶。配置:

import { datadogLogs } from '@datadog/browser-logs';

datadogLogs.init({
  clientToken: process.env.NEXT_PUBLIC_DATADOG_CLIENT_TOKEN,
  site: 'datadoghq.com',
  forwardErrorsToLogs: true,
  sampleRate: 100,
});

Logtail/BetterStack
性价比高,专注日志分析。支持实时搜索、告警规则、自定义面板。

个人项目我用 Logtail,配置简单,每月 1G 日志免费。团队项目用 Datadog,虽然贵但功能强大。

关键日志字段设计

一个好的日志应该包含这些字段:

{
  "timestamp": "2025-12-20T15:00:06.123Z",  // 时间戳
  "level": "INFO",                          // 日志级别
  "correlationId": "abc-123-def",           // 请求关联 ID
  "userId": "user_456",                     // 用户 ID
  "action": "create_order",                 // 业务动作
  "duration": 234,                          // 执行时长(ms)
  "status": "success",                      // 状态
  "metadata": {                             // 额外元数据
    "orderId": "order_789",
    "amount": 99.99
  }
}

这样的日志能回答”谁在什么时间做了什么,结果如何”。

性能监控 - 数据驱动的优化

Core Web Vitals:Google 在乎的指标

Google 把 Core Web Vitals 作为搜索排名因素,你也应该关注:

  • LCP (Largest Contentful Paint): 最大内容绘制时间,理想值 < 2.5s
  • FID (First Input Delay) / INP (Interaction to Next Paint): 交互响应时间,< 100ms / < 200ms
  • CLS (Cumulative Layout Shift): 累积布局偏移,< 0.1

Next.js 内置了 Web Vitals 上报,只需在 app/layout.tsx 里加几行:

'use client';

import { useReportWebVitals } from 'next/web-vitals';

export function WebVitalsReporter() {
  useReportWebVitals((metric) => {
    // 发送到 Sentry
    if (window.Sentry) {
      window.Sentry.captureMessage(`Web Vital: ${metric.name}`, {
        level: 'info',
        tags: {
          web_vital: metric.name,
        },
        contexts: {
          web_vitals: {
            value: metric.value,
            rating: metric.rating,
          },
        },
      });
    }

    // 或发送到你的分析平台
    fetch('/api/analytics/web-vitals', {
      method: 'POST',
      body: JSON.stringify(metric),
    });
  });

  return null;
}

然后在根布局里引入:

// app/layout.tsx
export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <WebVitalsReporter />
        {children}
      </body>
    </html>
  );
}

API 性能追踪

前端性能只是一半,后端 API 慢用户一样等得难受。Sentry 的 Performance Monitoring 可以追踪每个 API 请求:

// app/api/products/[id]/route.ts
import * as Sentry from '@sentry/nextjs';

export async function GET(
  request: Request,
  { params }: { params: { id: string } }
) {
  // 创建一个 transaction
  return await Sentry.startSpan(
    {
      op: 'api.request',
      name: 'GET /api/products/[id]',
    },
    async () => {
      // 追踪数据库查询
      const product = await Sentry.startSpan(
        {
          op: 'db.query',
          name: 'Fetch product from database',
        },
        async () => {
          return await db.product.findUnique({
            where: { id: params.id },
            include: { reviews: true },
          });
        }
      );

      if (!product) {
        return Response.json({ error: 'Not found' }, { status: 404 });
      }

      // 追踪外部 API 调用
      const pricing = await Sentry.startSpan(
        {
          op: 'http.client',
          name: 'Fetch pricing from external API',
        },
        async () => {
          const res = await fetch(`https://pricing-api.com/product/${params.id}`);
          return res.json();
        }
      );

      return Response.json({ ...product, pricing });
    }
  );
}

在 Sentry 面板上你能看到:

  • 整个请求耗时 450ms
    • 数据库查询 120ms
    • 外部 API 调用 300ms
    • 其他逻辑 30ms

一目了然,瓶颈在外部 API。

慢查询告警

数据库是性能瓶颈的常客。可以在 Prisma 中间件里加监控:

// lib/prisma.ts
import { PrismaClient } from '@prisma/client';
import { logger } from './logger';

const prisma = new PrismaClient();

// 监控慢查询
prisma.$use(async (params, next) => {
  const before = Date.now();
  const result = await next(params);
  const after = Date.now();
  const duration = after - before;

  // 超过 1 秒的查询记录为 WARN
  if (duration > 1000) {
    logger.warn({
      model: params.model,
      action: params.action,
      duration,
      args: params.args,
    }, 'Slow database query detected');

    // 同时发送到 Sentry
    Sentry.captureMessage('Slow database query', {
      level: 'warning',
      tags: { model: params.model, action: params.action },
      extra: { duration, args: params.args },
    });
  }

  return result;
});

export { prisma };

这样一来,任何慢查询都逃不过你的眼睛。

真实用户监控 vs 合成监控

真实用户监控(RUM)
用 Sentry、Datadog 等采集真实用户的性能数据。优点是反映真实场景(不同网络、设备),缺点是被动,问题发生了才知道。

合成监控(Synthetic Monitoring)
用 Checkly、Pingdom 等定时从全球各地模拟访问你的网站。优点是主动发现问题,缺点是无法覆盖所有用户场景。

两者结合最佳:

  • RUM 关注用户体验指标
  • 合成监控负责可用性和关键流程(登录、支付)

我用 Checkly 每 5 分钟从 5 个地理位置访问首页和登录接口,任何一个超时或失败立即告警。

告警配置 - 第一时间发现问题

Slack 集成:让团队第一时间知道

Sentry 集成 Slack 很简单,Settings → Integrations → Slack,授权后选择推送到哪个频道。

但默认配置会把所有错误都推送,很快变成噪音。需要配置告警规则:

在 Sentry 项目设置里:

  1. Alerts → Create Alert Rule
  2. 选择触发条件:
    • 错误率告警: “10 分钟内错误数 > 50”
    • 新错误告警: “首次出现的错误立即通知”
    • 性能回归: “API P95 响应时间 > 1s”
  3. 选择 Action: Send a notification via Slack

Slack 消息格式:

🚨 Production Error Spike

Project: my-nextjs-app
Environment: production
Error: TypeError: Cannot read property 'id' of undefined
Events: 127 events in 10 minutes

View in Sentry: https://sentry.io/...

点击链接直接跳转 Sentry,查看详情和堆栈。

告警分级:避免疲劳

不是所有问题都一样紧急。我的分级策略:

P0 - 严重 (立即处理)

  • 服务完全不可用
  • 支付功能失败
  • 数据库连接断开

触发方式:电话(PagerDuty) + Slack @channel

P1 - 重要 (1 小时内响应)

  • 核心功能异常
  • 错误率突增(10 分钟内 > 100 次)
  • API 响应时间 P95 > 3s

触发方式:Slack 推送到开发频道

P2 - 一般 (工作时间处理)

  • 小范围错误(< 10 次/小时)
  • 非关键功能异常
  • 第三方脚本错误

触发方式:邮件日报汇总

配置示例(Sentry Alert Rule):

// P0 告警:支付失败
{
  conditions: [
    { type: 'event.tag', key: 'feature', value: 'payment' },
    { type: 'event.level', value: 'error' }
  ],
  frequency: 'every event',  // 每次都通知
  actions: [
    { type: 'slack', channel: '#critical-alerts', mention: '@channel' },
    { type: 'pagerduty', service: 'payments' }
  ]
}

// P1 告警:错误率飙升
{
  conditions: [
    { type: 'event.count', value: 100, interval: '10m' }
  ],
  frequency: 'once per issue',  // 每个问题只通知一次
  actions: [
    { type: 'slack', channel: '#alerts-dev' }
  ]
}

告警降噪技巧

刚接入监控时,你可能会被告警淹没。几个降噪技巧:

1. 忽略已知问题

开发环境的热更新错误、第三方脚本异常,这些噪音可以过滤:

// sentry.client.config.ts
Sentry.init({
  ignoreErrors: [
    // 浏览器扩展错误
    /chrome-extension/,
    /moz-extension/,
    // 第三方脚本
    /google-analytics/,
    // 开发环境热更新
    /HMR/,
  ],
  denyUrls: [
    // 忽略特定域名的脚本错误
    /extensions\//i,
    /^chrome:\/\//i,
  ],
});

2. 合并重复告警

相同错误 10 分钟内只通知一次,避免频道被刷屏。Sentry 的 “Issue Grouping” 会自动合并相似错误。

3. 设置静默期

部署期间可能有短暂的错误尖峰,可以设置 “Mute for 10 minutes”。

4. 使用指纹(Fingerprint)

自定义错误分组规则,把相同根因的错误合并:

Sentry.captureException(error, {
  fingerprint: ['database-connection-error', databaseName],
});

这样不同数据库的连接错误会被分开,方便定位。

实战案例 - 完整监控方案落地

电商网站监控架构

去年帮一个电商网站做监控改造,分享下完整方案:

背景:

  • 日均 UV 8 万
  • 高峰期 QPS 3000+
  • 主要问题:支付偶发失败、首页加载慢

监控架构:

┌─────────────┐
│   Next.js   │
│   前端/SSR  │
└──────┬──────┘

       ├─ Sentry (错误 + 性能)
       ├─ Pino (结构化日志) → Datadog
       ├─ Web Vitals → Sentry
       └─ Checkly (合成监控)

关键配置:

  1. 用户行为追踪
// lib/tracking.ts
import * as Sentry from '@sentry/nextjs';

export function trackCheckoutStep(step: string, data: any) {
  Sentry.addBreadcrumb({
    category: 'checkout',
    message: `Checkout step: ${step}`,
    data,
    level: 'info',
  });
}

// 在购物流程中调用
trackCheckoutStep('add_to_cart', { productId, price });
trackCheckoutStep('proceed_to_payment', { cartTotal });
trackCheckoutStep('payment_submitted', { method: 'credit_card' });

这样当支付失败时,能看到用户完整的购物路径。

  1. 支付监控
// app/api/payment/route.ts
export async function POST(request: Request) {
  const log = logger.child({ action: 'payment' });

  try {
    const result = await processPayment(data);

    log.info({ orderId, amount, method }, 'Payment succeeded');

    return Response.json({ success: true, orderId });
  } catch (error) {
    log.error({ error, orderId, userId }, 'Payment failed');

    // P0 告警
    Sentry.captureException(error, {
      tags: { feature: 'payment', severity: 'critical' },
      level: 'fatal',
    });

    return Response.json({ error: 'Payment failed' }, { status: 500 });
  }
}

任何支付失败都会立即通知团队。

  1. 性能基线设定

用 Sentry Performance Monitoring 建立基线:

  • 首页 LCP < 2s
  • 商品详情页 LCP < 2.5s
  • API /api/products P95 < 500ms

超过基线自动告警。

效果:

  • 故障发现时间从平均 40 分钟降到 3 分钟
  • 支付失败率从 0.8% 降到 0.2%
  • 首页 LCP 优化后从 3.2s 降到 1.8s

监控 Checklist

最后给一个清单,照着检查你的项目:

**错误监控**
- [ ] Sentry 已配置并测试通过
- [ ] Source Maps 上传成功
- [ ] global-error.tsx 已创建(App Router)
- [ ] Server Actions 已包装错误处理
- [ ] 忽略规则已配置(过滤噪音)

**日志管理**
- [ ] 日志库已集成(Pino/Winston)
- [ ] 生产环境输出 JSON 格式
- [ ] 日志包含 correlationId
- [ ] 日志级别正确设置(生产环境 INFO)
- [ ] 日志已接入聚合平台

**性能监控**
- [ ] Web Vitals 上报已启用
- [ ] Core Web Vitals 达标(LCP<2.5s, INP<200ms, CLS<0.1)
- [ ] 关键 API 已添加性能追踪
- [ ] 慢查询监控已配置
- [ ] 合成监控已设置(可选)

**告警配置**
- [ ] Slack/邮件告警已测试
- [ ] 告警规则已按优先级分级
- [ ] 告警降噪规则已配置
- [ ] 团队成员都知道告警流程
- [ ] P0 事件有明确的响应人

**持续改进**
- [ ] 每周复盘监控数据
- [ ] 错误趋势分析(哪些错误在增加)
- [ ] 性能回归检测(哪些页面变慢了)
- [ ] 告警规则定期优化

结论

从被动救火到主动发现,监控让你对生产环境有了掌控力。

回顾一下我们搭建的监控体系:

  • Sentry负责错误追踪和性能监控,还能回放用户操作
  • Pino提供结构化日志,按 correlationId 串联整个请求链路
  • Web Vitals关注用户体验指标,直接影响 SEO 排名
  • Slack 告警让团队第一时间知道问题,分级处理避免疲劳

更重要的是思维转变:监控不是”有了更好”,而是生产环境的安全气囊。你不会等到出车祸才装安全气囊,同样不应该等线上出问题才想起监控。

今天就行动起来。如果你的项目还没有任何监控:

  1. 这周末花 2 小时接入 Sentry,配置基础错误追踪
  2. 下周加上结构化日志和 correlationId
  3. 再下周设置 Slack 告警和性能基线

不要追求一步到位,先把最基础的错误监控跑起来,再逐步完善。每次线上故障后问自己:“监控能否更早发现这个问题?” 持续改进,监控会成为你最可靠的队友。

最后,如果这篇文章帮到你,转发给团队一起提升。毕竟监控是团队的事,不是一个人的战斗。

祝你的 Next.js 应用稳如老狗,永不宕机。(但现实往往事与愿违,所以监控真的很重要😄)

Next.js生产环境监控完整配置流程

从Sentry集成到日志管理、性能监控、告警配置的完整步骤

⏱️ 预计耗时: 3 小时

  1. 1

    步骤1: 集成Sentry错误追踪

    安装:
    ```bash
    npm install @sentry/nextjs
    ```

    初始化:
    ```bash
    npx @sentry/wizard@latest -i nextjs
    ```

    配置客户端:
    ```ts
    // sentry.client.config.ts
    import * as Sentry from '@sentry/nextjs'

    Sentry.init({
    dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
    environment: process.env.NODE_ENV,
    tracesSampleRate: 1.0,
    })
    ```

    配置服务端:
    ```ts
    // sentry.server.config.ts
    import * as Sentry from '@sentry/nextjs'

    Sentry.init({
    dsn: process.env.SENTRY_DSN,
    environment: process.env.NODE_ENV,
    tracesSampleRate: 1.0,
    })
    ```

    关键点:
    • 客户端和服务端分开配置
    • 设置tracesSampleRate控制采样率
    • 配置环境变量
  2. 2

    步骤2: 配置结构化日志

    使用correlationId关联请求:
    ```ts
    // middleware.ts
    import { v4 as uuidv4 } from 'uuid'

    export function middleware(request: NextRequest) {
    const correlationId = request.headers.get('x-correlation-id') || uuidv4()

    const response = NextResponse.next()
    response.headers.set('x-correlation-id', correlationId)

    return response
    }
    ```

    在日志中使用:
    ```ts
    import { headers } from 'next/headers'

    export async function handler() {
    const headersList = headers()
    const correlationId = headersList.get('x-correlation-id')

    console.log({
    correlationId,
    message: 'User action',
    timestamp: new Date().toISOString(),
    })
    }
    ```

    关键点:
    • 每个请求生成唯一ID
    • 所有日志都带上correlationId
    • 方便追踪整个请求链路
  3. 3

    步骤3: 配置性能监控

    Sentry APM:
    ```ts
    Sentry.init({
    tracesSampleRate: 1.0, // 100%采样
    integrations: [
    new Sentry.Integrations.Http({ tracing: true }),
    ],
    })
    ```

    自定义性能监控:
    ```ts
    const transaction = Sentry.startTransaction({
    op: 'http.server',
    name: 'API Route',
    })

    try {
    // 业务逻辑
    await processRequest()
    } finally {
    transaction.finish()
    }
    ```

    关键点:
    • 设置tracesSampleRate控制采样率
    • 监控API响应时间
    • 识别性能瓶颈
  4. 4

    步骤4: 配置告警

    Sentry告警:
    • 在Sentry Dashboard配置告警规则
    • 设置错误阈值
    • 配置通知渠道(Slack、邮件等)

    Slack集成:
    ```ts
    // 在Sentry Dashboard配置
    // Webhook URL: https://hooks.slack.com/services/...
    ```

    邮件告警:
    • 在Sentry Dashboard配置
    • 设置收件人
    • 配置告警条件

    关键点:
    • 设置合理的告警阈值
    • 避免告警疲劳
    • 及时响应告警

常见问题

为什么Next.js需要专门的监控方案?
原因:Next.js的"三头怪"特性。

同一个应用同时跑在三个地方:
• 客户端(Browser):用户浏览器里的React组件
• 服务端(Node.js):SSR渲染、API Routes、Server Actions
• 边缘网络(Edge Runtime):中间件、边缘函数

传统前端监控只看到客户端错误,看不到服务端和边缘的错误。

SSR黑盒效应:
• 服务端渲染出错时,用户只看到500页面
• 没有stack trace、没有上下文
• 需要Sentry分布式追踪才能发现问题

真实案例:
• 用户反馈"页面加载很慢,然后就显示500"
• 浏览器Network面板显示请求慢,但不知道慢在哪
• 接入Sentry后才发现是服务端调用第三方API响应时间从200ms飙到8秒

解决方案:需要一套完整的监控体系,覆盖客户端、服务端、边缘网络。
如何集成Sentry?
安装:
```bash
npm install @sentry/nextjs
```

初始化:
```bash
npx @sentry/wizard@latest -i nextjs
```

配置客户端:
```ts
// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs'

Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: 1.0,
})
```

配置服务端:
```ts
// sentry.server.config.ts
import * as Sentry from '@sentry/nextjs'

Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: 1.0,
})
```

关键点:
• 客户端和服务端分开配置
• 设置tracesSampleRate控制采样率
• 配置环境变量(NEXT_PUBLIC_SENTRY_DSN、SENTRY_DSN)
如何配置结构化日志?
使用correlationId关联请求:

在middleware中生成:
```ts
import { v4 as uuidv4 } from 'uuid'

export function middleware(request: NextRequest) {
const correlationId = request.headers.get('x-correlation-id') || uuidv4()

const response = NextResponse.next()
response.headers.set('x-correlation-id', correlationId)

return response
}
```

在日志中使用:
```ts
import { headers } from 'next/headers'

export async function handler() {
const headersList = headers()
const correlationId = headersList.get('x-correlation-id')

console.log({
correlationId,
message: 'User action',
timestamp: new Date().toISOString(),
})
}
```

优势:
• 每个请求生成唯一ID
• 所有日志都带上correlationId
• 方便追踪整个请求链路
• 快速定位问题

关键点:在middleware中生成,在日志中使用,方便关联。
如何配置性能监控?
Sentry APM:
```ts
Sentry.init({
tracesSampleRate: 1.0, // 100%采样(生产环境建议0.1)
integrations: [
new Sentry.Integrations.Http({ tracing: true }),
],
})
```

自定义性能监控:
```ts
const transaction = Sentry.startTransaction({
op: 'http.server',
name: 'API Route',
})

try {
// 业务逻辑
await processRequest()
} finally {
transaction.finish()
}
```

监控指标:
• API响应时间
• 数据库查询时间
• 第三方API调用时间
• 页面加载时间

关键点:
• 设置tracesSampleRate控制采样率(生产环境建议0.1)
• 监控关键路径
• 识别性能瓶颈
如何配置告警?
Sentry告警:
• 在Sentry Dashboard配置告警规则
• 设置错误阈值(如:5分钟内错误数>10)
• 配置通知渠道(Slack、邮件等)

Slack集成:
• 在Sentry Dashboard配置Webhook URL
• 设置告警条件
• 测试告警

邮件告警:
• 在Sentry Dashboard配置
• 设置收件人
• 配置告警条件

告警规则建议:
• 错误率超过阈值
• 响应时间超过阈值
• 特定错误类型
• 新错误出现

关键点:
• 设置合理的告警阈值
• 避免告警疲劳
• 及时响应告警

建议:先把最基础的错误监控跑起来,再逐步完善。
监控的最佳实践是什么?
渐进式实施:
1. 这周末花2小时接入Sentry,配置基础错误追踪
2. 下周加上结构化日志和correlationId
3. 再下周设置Slack告警和性能基线

不要追求一步到位,先把最基础的错误监控跑起来,再逐步完善。

持续改进:
• 每次线上故障后问自己:"监控能否更早发现这个问题?"
• 根据实际情况调整告警阈值
• 定期审查监控配置

关键指标:
• 错误率
• 响应时间
• 用户影响范围
• 恢复时间

建议:
• 监控是团队的事,不是一个人的战斗
• 定期分享监控数据
• 持续改进监控体系

记住:监控不是一次性任务,而是持续的过程。

17 分钟阅读 · 发布于: 2025年12月20日 · 修改于: 2026年1月22日

评论

使用 GitHub 账号登录后即可评论

相关文章