You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js App Router服务器组件如何实现服务端错误日志?

Next.js App Router 全局服务端错误处理(日志记录+客户端脱敏)

在Next.js App Router中,要实现服务端完整错误日志记录且不向客户端暴露敏感信息,可通过以下全局方案替代逐个try/catch的繁琐操作:

一、全局捕获Server Component渲染错误

利用根layout.js(Server Component)的包裹特性,捕获所有子Server Component的同步/异步渲染错误,在服务端记录完整日志后返回脱敏内容给客户端。

示例代码:

// app/layout.js
import { logErrorToService } from '@/lib/logger';

export default async function RootLayout({ children }) {
  try {
    return (
      <html lang="zh-CN">
        <body>{children}</body>
      </html>
    );
  } catch (error) {
    // 服务端记录完整错误信息(含栈追踪、敏感上下文)
    await logErrorToService({
      message: error.message,
      stack: error.stack,
      timestamp: new Date().toISOString(),
      context: 'Server Component Render'
    });

    // 返回脱敏错误页面,不暴露敏感细节
    return (
      <html lang="zh-CN">
        <body className="flex items-center justify-center h-screen">
          <div className="text-center">
            <h1 className="text-2xl font-bold mb-4">服务端出现异常</h1>
            <p>我们已收到通知,正在处理,请稍后重试。</p>
          </div>
        </body>
      </html>
    );
  }
}

该方案覆盖根路由下所有Server Component的渲染错误,包括并行路由、嵌套路由的组件错误。

二、全局捕获Server Action错误

针对Server Action的未处理错误,有两种全局处理方式:

方式1:Node.js全局错误事件监听

在服务端注册全局错误事件,捕获所有未被try/catch处理的异步/同步错误,适用于Node.js Runtime环境。

示例代码:

// app/global-error-handler.js
import { logErrorToService } from '@/lib/logger';

// 捕获未处理的Promise拒绝(异步错误)
process.on('unhandledRejection', async (reason, promise) => {
  await logErrorToService({
    message: 'Unhandled Rejection',
    reason: reason instanceof Error ? reason.message : String(reason),
    stack: reason instanceof Error ? reason.stack : undefined,
    promise: promise.toString(),
    timestamp: new Date().toISOString(),
    context: 'Server Action'
  });
});

// 捕获未捕获的同步错误
process.on('uncaughtException', async (error) => {
  await logErrorToService({
    message: 'Uncaught Exception',
    errorMessage: error.message,
    stack: error.stack,
    timestamp: new Date().toISOString(),
    context: 'Server Action'
  });
  // 同步未捕获错误会导致进程不稳定,捕获后建议优雅退出
  process.exit(1);
});

在根layout.js中导入该模块,确保服务端启动时加载:

// app/layout.js
import './global-error-handler.js';
// ... 其他代码

方式2:自动注入Server Action包装器

通过Babel插件自动给所有标记'use server'的函数加上错误处理包装,无需手动逐个包裹。

  1. 创建包装函数:
// lib/server-action-wrapper.js
import { logErrorToService } from './logger';

export function wrapServerAction(action) {
  return async (...args) => {
    try {
      return await action(...args);
    } catch (error) {
      await logErrorToService({
        message: 'Server Action Error',
        actionName: action.name,
        errorMessage: error.message,
        stack: error.stack,
        timestamp: new Date().toISOString(),
        context: 'Server Action'
      });
      // 抛出脱敏错误给客户端
      throw new Error('操作失败,请稍后重试');
    }
  };
}
  1. 配置Next.js的Babel插件:
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  babel: {
    plugins: [
      [
        './babel-plugins/wrap-server-actions.js',
        { wrapperPath: '@/lib/server-action-wrapper.js' }
      ]
    ]
  }
};

module.exports = nextConfig;
  1. 自定义Babel插件实现自动包装:
// babel-plugins/wrap-server-actions.js
module.exports = function ({ types: t }) {
  return {
    visitor: {
      FunctionDeclaration(path) {
        const hasUseServer = path.node.directives?.some(
          dir => dir.value.value === 'use server'
        );
        if (hasUseServer) {
          // 导入包装函数
          const wrapperImport = t.importDeclaration(
            [t.importSpecifier(t.identifier('wrapServerAction'), t.identifier('wrapServerAction'))],
            t.stringLiteral('@/lib/server-action-wrapper.js')
          );
          path.hub.file.path.unshiftContainer('body', wrapperImport);
          // 用包装函数包裹目标Server Action
          path.replaceWith(
            t.variableDeclaration('const', [
              t.variableDeclarator(
                path.node.id,
                t.callExpression(t.identifier('wrapServerAction'), [path.node])
              )
            ])
          );
        }
      }
    }
  };
};

三、注意事项

  • 所有敏感错误信息仅在服务端日志中记录,客户端仅接收脱敏提示。
  • 使用Edge Runtime时,Node.js全局错误事件不生效,需改用对应平台的错误日志服务或在middleware中捕获。
  • 根layout.js的try/catch仅捕获Server Component渲染阶段的错误,若需处理客户端组件错误,仍需配合客户端Error Boundary。

内容的提问来源于stack exchange,提问作者TrueWill

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 12:57:11