如何将Next.js控制台显示的未捕获错误记录到Winston日志器?
解决Next.js中未捕获错误的Winston日志记录方案
一、服务端未捕获错误拦截
Next.js服务端的未捕获异常、未处理Promise拒绝,可通过Node.js全局事件监听结合Winston配置优化来处理。
- 全局事件监听
在自定义服务端入口(如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() }); });
- 优化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来覆盖更多场景。
- 全局客户端事件监听
在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;
- 优化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
相关产品推荐
相关产品推荐

