Skip to content

Commit

Permalink
feat(nuxt): Set transaction name for server error (#13292)
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
s1gr1d authored Aug 12, 2024
1 parent 69a5e8e commit 0654dd0
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 2 deletions.
4 changes: 4 additions & 0 deletions dev-packages/e2e-tests/test-applications/nuxt-3/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
<header>
<nav>
<ul>
<li><NuxtLink to="/fetch-server-error">Fetch Server Error</NuxtLink></li>
<li><NuxtLink to="/param-error/1234">Fetch Param Server Error</NuxtLink></li>
<li><NuxtLink to="/client-error">Client Error</NuxtLink></li>
</ul>
</nav>
Expand All @@ -11,3 +13,5 @@
</NuxtLayout>
</template>

<script setup>
</script>
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);
});
});
13 changes: 11 additions & 2 deletions packages/nuxt/src/runtime/plugins/sentry.server.ts
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';
Expand All @@ -14,9 +14,18 @@ export default defineNitroPlugin(nitroApp => {
}
}

const { method, path } = {
method: errorContext.event && errorContext.event._method ? errorContext.event._method : '',
path: errorContext.event && errorContext.event._path ? errorContext.event._path : null,
};

if (path) {
Sentry.getCurrentScope().setTransactionName(`${method} ${path}`);
}

const structuredContext = extractErrorContext(errorContext);

captureException(error, {
Sentry.captureException(error, {
captureContext: { contexts: { nuxt: structuredContext } },
mechanism: { handled: false },
});
Expand Down

0 comments on commit 0654dd0

Please sign in to comment.