JulianMar / nuxt-bugsnag

Bugsnag module for nuxt.js
MIT License
49 stars 18 forks source link

Nuxt 3, bugstangs reports 404 errors #39

Closed vedmant closed 1 year ago

vedmant commented 1 year ago

I have following code in my Nuxt 3 app:

...
  async setup () {
    const route = useRoute()
    const uuid = route.params.orderUuid
    const nonce = route.query.nonce
    const app = useNuxtApp()
    const store = useOrderStore(app.$pinia)

    try {
      await store.getPaymentConf(uuid, nonce)
    } catch (e) {
      throw createError({ statusCode: 404, statusMessage: 'Order Not Found' })
    }
  },
...

This makes Bugsnag to report 404 error, which it should not report. What's the appropriate way to stop it reporting this kind of errors?

JulianMar commented 1 year ago

Hey, thanks for your Feedback.

This is the default behaviour of Bugsnag, so there is not much we can change. You could catch the error further outside of the Code, if you do the error won't be reported.

I am not sure if there is an option for not reporting unhandled errors in bugsnag.

vedmant commented 1 year ago

@JulianMar Thanks

You could catch the error further outside of the Code, if you do the error won't be reported.

How I can do this exactly? Outside code is only Nuxt code, I have no way to add code to Nuxt source code.

I think the only way is to use https://vuejs.org/api/application.html#app-config-errorhandler and ignore and report errors manually to bugsnag.

JulianMar commented 1 year ago

Yeah I think that could work. But there is no way right now in Bugsnag.

What you can do is ignore the error in the Bugsnag web ui

vedmant commented 1 year ago

I created a custom plugin for Nuxt based on bugsnag-vue, this works ok, it's not triggering 404 here.

import Bugsnag from '@bugsnag/js'
import { defineNuxtPlugin } from 'nuxt/app'
import { useAuthStore } from '~/stores/auth'

// https://github.com/vuejs/vue-next/blob/d5cce47789db8f37b9f5f8ea6602ea63e3a04b07/packages/runtime-core/src/component.ts#L153-L167
const LifecycleHooks = {
  BEFORE_CREATE: 'bc',
  CREATED: 'c',
  BEFORE_MOUNT: 'bm',
  MOUNTED: 'm',
  BEFORE_UPDATE: 'bu',
  UPDATED: 'u',
  BEFORE_UNMOUNT: 'bum',
  UNMOUNTED: 'um',
  DEACTIVATED: 'da',
  ACTIVATED: 'a',
  RENDER_TRIGGERED: 'rtg',
  RENDER_TRACKED: 'rtc',
  ERROR_CAPTURED: 'ec',
}

// https://github.com/vuejs/vue-next/blob/d5cce47789db8f37b9f5f8ea6602ea63e3a04b07/packages/runtime-core/src/errorHandling.ts#L6-L24
const ErrorCodes = {
  SETUP_FUNCTION: 0,
  RENDER_FUNCTION: 1,
  WATCH_GETTER: 2,
  WATCH_CALLBACK: 3,
  WATCH_CLEANUP: 4,
  NATIVE_EVENT_HANDLER: 5,
  COMPONENT_EVENT_HANDLER: 6,
  VNODE_HOOK: 7,
  DIRECTIVE_HOOK: 8,
  TRANSITION_HOOK: 9,
  APP_ERROR_HANDLER: 10,
  APP_WARN_HANDLER: 11,
  FUNCTION_REF: 12,
  ASYNC_COMPONENT_LOADER: 13,
  SCHEDULER: 14,
}

// https://github.com/vuejs/vue-next/blob/d5cce47789db8f37b9f5f8ea6602ea63e3a04b07/packages/runtime-core/src/errorHandling.ts#L26-L57
const ErrorTypeStrings = {
  [LifecycleHooks.BEFORE_CREATE]: 'beforeCreate hook',
  [LifecycleHooks.CREATED]: 'created hook',
  [LifecycleHooks.BEFORE_MOUNT]: 'beforeMount hook',
  [LifecycleHooks.MOUNTED]: 'mounted hook',
  [LifecycleHooks.BEFORE_UPDATE]: 'beforeUpdate hook',
  [LifecycleHooks.UPDATED]: 'updated',
  [LifecycleHooks.BEFORE_UNMOUNT]: 'beforeUnmount hook',
  [LifecycleHooks.UNMOUNTED]: 'unmounted hook',
  [LifecycleHooks.ACTIVATED]: 'activated hook',
  [LifecycleHooks.DEACTIVATED]: 'deactivated hook',
  [LifecycleHooks.ERROR_CAPTURED]: 'errorCaptured hook',
  [LifecycleHooks.RENDER_TRACKED]: 'renderTracked hook',
  [LifecycleHooks.RENDER_TRIGGERED]: 'renderTriggered hook',
  [ErrorCodes.SETUP_FUNCTION]: 'setup function',
  [ErrorCodes.RENDER_FUNCTION]: 'render function',
  [ErrorCodes.WATCH_GETTER]: 'watcher getter',
  [ErrorCodes.WATCH_CALLBACK]: 'watcher callback',
  [ErrorCodes.WATCH_CLEANUP]: 'watcher cleanup function',
  [ErrorCodes.NATIVE_EVENT_HANDLER]: 'native event handler',
  [ErrorCodes.COMPONENT_EVENT_HANDLER]: 'component event handler',
  [ErrorCodes.VNODE_HOOK]: 'vnode hook',
  [ErrorCodes.DIRECTIVE_HOOK]: 'directive hook',
  [ErrorCodes.TRANSITION_HOOK]: 'transition hook',
  [ErrorCodes.APP_ERROR_HANDLER]: 'app errorHandler',
  [ErrorCodes.APP_WARN_HANDLER]: 'app warnHandler',
  [ErrorCodes.FUNCTION_REF]: 'ref function',
  [ErrorCodes.ASYNC_COMPONENT_LOADER]: 'async component loader',
  [ErrorCodes.SCHEDULER]:
  'scheduler flush. This is likely a Vue internals bug. '
  + 'Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/vue-next',
}

export default defineNuxtPlugin((nuxtApp) => {
  const options = { ...nuxtApp.payload.config.public.bugsnag }

  options.onError = (event) => {
    event.errors[0].stacktrace = event.errors[0].stacktrace.map((row) => {
      row.file = row.file.replace('file://', '')
      return row
    })
    const user = useAuthStore().user
    user && event.setUser(user.sub, user.email, user.name)
  }

  const client = Bugsnag.start(options)

  nuxtApp.vueApp.config.errorHandler = (err, context, info) => {
    if (err?.statusCode === 404) { return }
    const handledState = { severity: 'error', unhandled: true, severityReason: { type: 'unhandledException' } }
    const event = client.Event.create(err, true, handledState, 'vue error handler', 1)

    event.addMetadata('vue', {
      errorInfo: ErrorTypeStrings[info] || info,
      component: context ? formatComponentName(context, true) : undefined,
      props: (context && context.$options) ? context.$options.propsData : undefined,
    })

    client._notify(event)
    console.error(err)
  }

  nuxtApp.vueApp.provide('bugsnag-client', client)

  return {
    provide: {
      bugsnag: client,
    },
  }
})

function formatComponentName (vm) {
  if (vm.$parent === null) { return 'App' }
  return (vm.$options && vm.$options.name) ? vm.$options.name : 'Anonymous'
}
JulianMar commented 1 year ago

Looks good!