Redwood API logger - unhandled exceptions

I appreciate all the work you put into the redwood logger! It’s very helpful!

I’ve noted that if an exception is not handled in a redwood API service, the exception will go to the console and not to the redwood logger.

Is there a standard redwood way of catching unhandled exceptions so that they go to the logger?

Hi,

You might try loud-rejection - npm and pass it the logger

Cheers,
Al;

Hi @cjReimer great question. I don’t know if there is a standard way – I kow I wanted to see how people best used it and then come up with a consensus “standard” … but this is what I have done:

First, if the exception is in GraphQL hander – then you can log it there. Soon there will be some better logging support once Redwood moves to use Helix and envelop.

You can import logger in graphql.ts and use formatError():

import {
  createGraphQLHandler,
  makeMergedSchema,
  makeServices,
  AuthenticationError,
  ForbiddenError,
  ValidationError,
} from '@redwoodjs/api'

import schemas from 'src/graphql/**/*.{js,ts}'
import services from 'src/services/**/*.{js,ts}'

import { getCurrentUser } from 'src/lib/auth'
import { db } from 'src/lib/db'
import { logger } from 'src/lib/logger'

export const handler = createGraphQLHandler({
  formatError: (error) => {
    const originalError = error.originalError

    logger.error(
      { graphQL: error, originalError: originalError },
      error.message
    )

    if (originalError instanceof ValidationError) {
      return new ValidationError('Validation failed.')
    } else if (originalError instanceof ForbiddenError) {
      return new ForbiddenError(originalError.message)
    } else if (originalError instanceof AuthenticationError) {
      return new AuthenticationError(originalError.message)
    } else {
      return new Error('An error occurred.')
    }
  },
  schema: makeMergedSchema({
    schemas,
    services: makeServices({ services }),
  }),
  getCurrentUser,

  onException: () => {
    // Disconnect from your database with an unhandled exception.
    db.$disconnect()
  },
})

Again, this will get easier in a bit. Also, plan to add operation name, query variables and other useful info in dev to help debug.

Also, you can force console log to go use the logger by binding it to the logger as in the last line here (this would be in logger.ts): console.log = logger.info.bind(logger)

import { createLogger, isTest } from '@redwoodjs/api/logger'

/**
 * Creates a logger with RedwoodLoggerOptions
 *
 * These extend and override default LoggerOptions,
 * can define a destination like a file or other supported pino log transport stream,
 * and sets whether or not to show the logger configuration settings (defaults to false)
 *
 * @param RedwoodLoggerOptions
 *
 * RedwoodLoggerOptions have
 * @param {options} LoggerOptions - defines how to log, such as pretty printing, redaction, and format
 * @param {string | DestinationStream} destination - defines where to log, such as a transport stream or file
 * @param {boolean} showConfig - whether to display logger configuration on initialization
 */
export const logger = createLogger({})

console.log = logger.info.bind(logger)

You can choose the level for console.log to use by binding to info or what level best suits you.

If you are getting the exception in a serverless function or webhook, you need to catch and log as needed.

1 Like

I recommend setting up sentry, because it will be very useful for reporting unhandled exceptions with stack traces.

1 Like

Thanks @ajoslin103 , I’ll have to look at loud-rejection at some point!

And @dthyresson, your graphql handler example is exactly what I was looking for! Thanks! I really appreciate the time you took for a very detailed response!
I’m looking forward to seeing what the plans are for improved Redwood logging!

@viperfx , yes Thanks for the tip! I saw you posted something else about sentry a little while ago and had already added that to our backlog items. I appreciate the real-world experience you’re bringing to these forums!

@viperfx FYI once we get envelop in, adding Sentry support to GraphQL will be much easier too via this plugin:

This of course would not apply to serverless functions, etc.

Agreed! Also, if you want to just log, you would be to use a transport to send to DataDog, logDNA, etc. See: https://redwoodjs.com/docs/logger#log-to-datadog-using-a-transport-stream-destination etc

Yep, I’m currently trying to finalize the logging solution, using Redwood logger to logDNA. Logs of of both user events and exceptions have saved my bacon in the past, and are critical for us.

It’s good to know that envelop will make Sentry addition easier. Is there an ETA on that? Pre or post V1.0?

Pre 1.0. Am currently woking on that now – there is a PR https://github.com/redwoodjs/redwood/pull/2351 that is functional. Just want to have the option for people to use Apollo (aka classic api) and envelop for testing and transition.

Awesome! Thank you!!!