loggingnext.jsserverwinston

How to log uncaught errors that are visible in Nextjs console to the (winston) logger?


So I'm trying to log the errors that happen in my application using winston logger. Everything is working smoothly, when error happens on server side, or in client (I'm sending logs to the server to log them). But it's working in cases where I catch the error.

But there are cases where errors happens, because something is undefined somewhere, or I didn't catch the error somewhere. How I can catch this errors? Normally, in Nextjs console I have a whole error described, with error message and stack, but in Datadog it's logged as few lines of normal text (not even error).

The moment the error is happening and it's being shown in the console, I'd like to report it to my logger (winston) as and object. But I don't know how and where to intercept this error.

I have ErrorBoundry in app, but error is not as precise as error in Nextjs console. I do have _error file, but everything is 'undefined' (error and res). This is how my winston configuration looks like:

const logger = createLogger({
  level: "silly",
  format: format.json(),
  handleExceptions: true,
  transports: [new transports.Console({ handleExceptions: true, handleRejections: true })],
  exceptionHandlers: [new transports.Console()],
});


Solution

  • Use the process.on('uncaughtException') and process.on('unhandledRejection') event listeners. They allow you to capture errors that are not caught by your app. This is basically logging the errors as they happen.

    Here a the modified code:

    const { createLogger, format, transports } = require('winston');
    
    const logger = createLogger({
      level: 'silly',
      format: format.json(),
      handleExceptions: true,
      transports: [
        new transports.Console({ handleExceptions: true, handleRejections: true }),
      ],
      exceptionHandlers: [
        new transports.Console(),
      ],
    });
    
    // Log uncaught exceptions and exit the process
    process.on('uncaughtException', (error) => {
      logger.error('Uncaught Exception:', { error, stack: error.stack });
      logger.transports.forEach((transport) => transport.close());
      process.exit(1);
    });
    
    // Log unhandled promise rejections
    process.on('unhandledRejection', (reason, promise) => {
      logger.error('Unhandled Rejection:', { reason, stack: reason.stack });
    });
    
    // Dispose of transports on process exit to ensure logs are saved
    process.on('exit', () => {
      logger.transports.forEach((transport) => transport.close());
    });