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

如何将Next.js控制台显示的未捕获错误记录到Winston日志器?

解决Next.js中未捕获错误的Winston日志记录方案

一、服务端未捕获错误拦截

Next.js服务端的未捕获异常、未处理Promise拒绝,可通过Node.js全局事件监听结合Winston配置优化来处理。

  1. 全局事件监听
    在自定义服务端入口(如server.js)或pages/_app.js的服务端逻辑中添加:
// 捕获未捕获的服务端异常
process.on('uncaughtException', (error) => {
  logger.error('未捕获的服务端异常', {
    error: {
      message: error.message,
      stack: error.stack,
      name: error.name
    },
    timestamp: new Date().toISOString()
  });
  // 按Node.js最佳实践,uncaughtException后建议重启进程
  process.exit(1);
});

// 捕获未处理的服务端Promise拒绝
process.on('unhandledRejection', (reason, promise) => {
  logger.error('未处理的服务端Promise拒绝', {
    reason: reason instanceof Error ? {
      message: reason.message,
      stack: reason.stack,
      name: reason.name
    } : reason,
    promise: `${promise}`,
    timestamp: new Date().toISOString()
  });
});
  1. 优化Winston配置
    完善现有配置,确保异常和Promise拒绝都能以结构化对象输出:
const logger = createLogger({
  level: "silly",
  format: format.combine(
    format.timestamp(),
    format.json() // 确保错误序列化为JSON结构
  ),
  handleExceptions: true,
  handleRejections: true, // 新增:处理未处理的Promise拒绝
  transports: [
    new transports.Console()
  ],
  exceptionHandlers: [
    new transports.Console({
      format: format.combine(
        format.timestamp(),
        format.json()
      )
    })
  ],
  rejectionHandlers: [ // 新增:专门处理Promise拒绝的日志处理器
    new transports.Console({
      format: format.combine(
        format.timestamp(),
        format.json()
      )
    })
  ]
});

二、客户端未捕获错误拦截

客户端未捕获错误需结合全局事件监听和优化后的ErrorBoundary来覆盖更多场景。

  1. 全局客户端事件监听
    在pages/_app.js的useEffect中添加客户端全局监听:
import { useEffect } from 'react';

function MyApp({ Component, pageProps }) {
  useEffect(() => {
    // 捕获客户端未捕获的异常
    const handleError = (error) => {
      logger.error('未捕获的客户端异常', {
        error: {
          message: error.message,
          stack: error.stack,
          name: error.name
        },
        timestamp: new Date().toISOString(),
        url: window.location.href
      });
    };

    // 捕获客户端未处理的Promise拒绝
    const handleRejection = (event) => {
      event.preventDefault(); // 阻止浏览器默认处理逻辑
      logger.error('未处理的客户端Promise拒绝', {
        reason: event.reason instanceof Error ? {
          message: event.reason.message,
          stack: event.reason.stack,
          name: event.reason.name
        } : event.reason,
        timestamp: new Date().toISOString(),
        url: window.location.href
      });
    };

    window.addEventListener('error', handleError);
    window.addEventListener('unhandledrejection', handleRejection);

    return () => {
      window.removeEventListener('error', handleError);
      window.removeEventListener('unhandledrejection', handleRejection);
    };
  }, []);

  return <Component {...pageProps} />;
}

export default MyApp;
  1. 优化ErrorBoundary的错误信息
    自定义ErrorBoundary以获取更精准的错误栈和组件信息:
import React from 'react';

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // 记录完整错误栈和组件调用栈
    logger.error('React渲染错误', {
      error: {
        message: error.message,
        stack: error.stack,
        name: error.name
      },
      componentStack: errorInfo.componentStack,
      timestamp: new Date().toISOString(),
      url: window.location.href
    });
  }

  render() {
    if (this.state.hasError) {
      return <h1>页面出现错误,请稍后重试。</h1>;
    }

    return this.props.children;
  }
}

// 在_app.js中用ErrorBoundary包裹页面组件
function MyApp({ Component, pageProps }) {
  // ...之前的useEffect代码
  return (
    <ErrorBoundary>
      <Component {...pageProps} />
    </ErrorBoundary>
  );
}

三、修复错误页面组件的问题

根据你使用的Next.js路由模式,调整错误页面的写法:

Pages Router模式(_error.js)

确保正确接收错误参数:

function Error({ statusCode, err }) {
  if (err) {
    logger.error('Next.js页面错误', {
      statusCode,
      error: {
        message: err.message,
        stack: err.stack,
        name: err.name
      },
      timestamp: new Date().toISOString()
    });
  }
  return (
    <p>{statusCode ? `服务端发生错误 ${statusCode}` : '客户端发生错误'}</p>
  );
}

Error.getInitialProps = ({ res, err }) => {
  const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
  return { statusCode, err };
};

export default Error;

App Router模式(error.js)

在对应路由目录下创建error.js:

'use client';

import { useEffect } from 'react';

export default function Error({ error, reset }) {
  useEffect(() => {
    logger.error('App Router页面错误', {
      error: {
        message: error.message,
        stack: error.stack,
        name: error.name
      },
      timestamp: new Date().toISOString()
    });
  }, [error]);

  return (
    <div>
      <h2>页面出错了!</h2>
      <button onClick={() => reset()}>重试</button>
    </div>
  );
}

四、确保Datadog识别结构化日志

要让Datadog将日志标记为错误并显示完整结构:

  • 保持Winston的format.json()配置,确保输出JSON格式日志
  • 在Datadog日志配置中开启JSON解析,将error字段识别为错误对象,level: error标记为错误级别

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 12:15:43