-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(nuxt): Set transaction name for server error #13292
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<template> | ||
<div> | ||
<button @click="fetchData">Fetch Server Data</button> | ||
</div> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
const fetchData = async () => { | ||
await useFetch('/api/server-error'); | ||
} | ||
</script> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,15 @@ | ||
<template> | ||
<p>{{ $route.params.param }} - {{ $route.params.param }}</p> | ||
|
||
<ErrorButton errorText="Error thrown from Param Route Button" /> | ||
<button @click="fetchData">Fetch Server Data</button> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
const route = useRoute(); | ||
const param = route.params.param; | ||
|
||
const fetchData = async () => { | ||
await useFetch(`/api/param-error/${param}`); | ||
} | ||
</script> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default defineEventHandler(_e => { | ||
throw new Error('Nuxt 3 Param Server error'); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default defineEventHandler(event => { | ||
throw new Error('Nuxt 3 Server error'); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export default defineEventHandler(event => { | ||
const param = getRouterParam(event, 'param'); | ||
|
||
return `Param: ${param}!`; | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"extends": "../.nuxt/tsconfig.server.json" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { expect, test } from '@playwright/test'; | ||
import { waitForError } from '@sentry-internal/test-utils'; | ||
|
||
test.describe('server-side errors', async () => { | ||
test('captures api fetch error (fetched on click)', async ({ page }) => { | ||
const errorPromise = waitForError('nuxt-3', async errorEvent => { | ||
return errorEvent?.exception?.values?.[0]?.value === 'Nuxt 3 Server error'; | ||
}); | ||
|
||
await page.goto(`/fetch-server-error`); | ||
await page.getByText('Fetch Server Data').click(); | ||
|
||
const error = await errorPromise; | ||
|
||
expect(error.transaction).toEqual('GET /api/server-error'); | ||
|
||
const exception = error.exception.values[0]; | ||
expect(exception.type).toEqual('Error'); | ||
expect(exception.value).toEqual('Nuxt 3 Server error'); | ||
expect(exception.mechanism.handled).toBe(false); | ||
}); | ||
|
||
test('captures api fetch error (fetched on click) with parametrized route', async ({ page }) => { | ||
const errorPromise = waitForError('nuxt-3', async errorEvent => { | ||
return errorEvent?.exception?.values?.[0]?.value === 'Nuxt 3 Param Server error'; | ||
}); | ||
|
||
await page.goto(`/test-param/1234`); | ||
await page.getByText('Fetch Server Data').click(); | ||
|
||
const error = await errorPromise; | ||
|
||
expect(error.transaction).toEqual('GET /api/param-error/1234'); | ||
|
||
const exception = error.exception.values[0]; | ||
expect(exception.type).toEqual('Error'); | ||
expect(exception.value).toEqual('Nuxt 3 Param Server error'); | ||
expect(exception.mechanism.handled).toBe(false); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
import { captureException } from '@sentry/node'; | ||
import * as Sentry from '@sentry/node'; | ||
import { H3Error } from 'h3'; | ||
import { defineNitroPlugin } from 'nitropack/runtime'; | ||
import type { NuxtRenderHTMLContext } from 'nuxt/app'; | ||
|
@@ -14,9 +14,17 @@ export default defineNitroPlugin(nitroApp => { | |
} | ||
} | ||
|
||
const currentScope = Sentry.getCurrentScope(); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. l: Is there a particular reason you're creating a binding for the current scope here instead of just calling |
||
const { method, path } = { | ||
method: errorContext.event && errorContext.event._method ? errorContext.event._method : '', | ||
path: errorContext.event && errorContext.event._path ? errorContext.event._path : 'unknown-path', | ||
}; | ||
currentScope.setTransactionName(`${method} ${path}`); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. m: I recommend not setting the transaction name at all if the path is unknown, because we will give other sensible default mechanisms to activate and show in the product, ie. root span name, or stack trace. |
||
const structuredContext = extractErrorContext(errorContext); | ||
|
||
captureException(error, { | ||
Sentry.captureException(error, { | ||
captureContext: { contexts: { nuxt: structuredContext } }, | ||
mechanism: { handled: false }, | ||
}); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
super-l/hint: It's totally fine to make a namespace (
import * as
) or named imports. No need to change it back, just fyi :)