Skip to content

Commit 0654dd0

Browse files
authored
feat(nuxt): Set transaction name for server error (#13292)
Setting the transaction name to display the API route where the error happened: ![image](https://github.com/user-attachments/assets/5954fc45-e710-44ab-b29a-f90e6bd480a4)
1 parent 69a5e8e commit 0654dd0

File tree

9 files changed

+91
-2
lines changed

9 files changed

+91
-2
lines changed

dev-packages/e2e-tests/test-applications/nuxt-3/app.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
<header>
44
<nav>
55
<ul>
6+
<li><NuxtLink to="/fetch-server-error">Fetch Server Error</NuxtLink></li>
7+
<li><NuxtLink to="/param-error/1234">Fetch Param Server Error</NuxtLink></li>
68
<li><NuxtLink to="/client-error">Client Error</NuxtLink></li>
79
</ul>
810
</nav>
@@ -11,3 +13,5 @@
1113
</NuxtLayout>
1214
</template>
1315

16+
<script setup>
17+
</script>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<template>
2+
<div>
3+
<button @click="fetchData">Fetch Server Data</button>
4+
</div>
5+
</template>
6+
7+
<script setup lang="ts">
8+
const fetchData = async () => {
9+
await useFetch('/api/server-error');
10+
}
11+
</script>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
11
<template>
22
<p>{{ $route.params.param }} - {{ $route.params.param }}</p>
3+
34
<ErrorButton errorText="Error thrown from Param Route Button" />
5+
<button @click="fetchData">Fetch Server Data</button>
46
</template>
7+
8+
<script setup lang="ts">
9+
const route = useRoute();
10+
const param = route.params.param;
11+
12+
const fetchData = async () => {
13+
await useFetch(`/api/param-error/${param}`);
14+
}
15+
</script>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default defineEventHandler(_e => {
2+
throw new Error('Nuxt 3 Param Server error');
3+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default defineEventHandler(event => {
2+
throw new Error('Nuxt 3 Server error');
3+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export default defineEventHandler(event => {
2+
const param = getRouterParam(event, 'param');
3+
4+
return `Param: ${param}!`;
5+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "../.nuxt/tsconfig.server.json"
3+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { expect, test } from '@playwright/test';
2+
import { waitForError } from '@sentry-internal/test-utils';
3+
4+
test.describe('server-side errors', async () => {
5+
test('captures api fetch error (fetched on click)', async ({ page }) => {
6+
const errorPromise = waitForError('nuxt-3', async errorEvent => {
7+
return errorEvent?.exception?.values?.[0]?.value === 'Nuxt 3 Server error';
8+
});
9+
10+
await page.goto(`/fetch-server-error`);
11+
await page.getByText('Fetch Server Data').click();
12+
13+
const error = await errorPromise;
14+
15+
expect(error.transaction).toEqual('GET /api/server-error');
16+
17+
const exception = error.exception.values[0];
18+
expect(exception.type).toEqual('Error');
19+
expect(exception.value).toEqual('Nuxt 3 Server error');
20+
expect(exception.mechanism.handled).toBe(false);
21+
});
22+
23+
test('captures api fetch error (fetched on click) with parametrized route', async ({ page }) => {
24+
const errorPromise = waitForError('nuxt-3', async errorEvent => {
25+
return errorEvent?.exception?.values?.[0]?.value === 'Nuxt 3 Param Server error';
26+
});
27+
28+
await page.goto(`/test-param/1234`);
29+
await page.getByText('Fetch Server Data').click();
30+
31+
const error = await errorPromise;
32+
33+
expect(error.transaction).toEqual('GET /api/param-error/1234');
34+
35+
const exception = error.exception.values[0];
36+
expect(exception.type).toEqual('Error');
37+
expect(exception.value).toEqual('Nuxt 3 Param Server error');
38+
expect(exception.mechanism.handled).toBe(false);
39+
});
40+
});

packages/nuxt/src/runtime/plugins/sentry.server.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { captureException } from '@sentry/node';
1+
import * as Sentry from '@sentry/node';
22
import { H3Error } from 'h3';
33
import { defineNitroPlugin } from 'nitropack/runtime';
44
import type { NuxtRenderHTMLContext } from 'nuxt/app';
@@ -14,9 +14,18 @@ export default defineNitroPlugin(nitroApp => {
1414
}
1515
}
1616

17+
const { method, path } = {
18+
method: errorContext.event && errorContext.event._method ? errorContext.event._method : '',
19+
path: errorContext.event && errorContext.event._path ? errorContext.event._path : null,
20+
};
21+
22+
if (path) {
23+
Sentry.getCurrentScope().setTransactionName(`${method} ${path}`);
24+
}
25+
1726
const structuredContext = extractErrorContext(errorContext);
1827

19-
captureException(error, {
28+
Sentry.captureException(error, {
2029
captureContext: { contexts: { nuxt: structuredContext } },
2130
mechanism: { handled: false },
2231
});

0 commit comments

Comments
 (0)