Skip to content
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

Add Sentry Nuxt module #5279

Merged
merged 6 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ jobs:
outputs: type=docker,dest=/tmp/${{ matrix.image }}.tar
build-contexts: ${{ matrix.build-contexts }}
build-args: |
SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
SEMANTIC_VERSION=${{ needs.get-image-tag.outputs.image_tag }}
OV_PDM_VERSION=${{ steps.prepare-build-args.outputs.ov_pdm_version }}
CATALOG_PY_VERSION=${{ steps.prepare-build-args.outputs.catalog_py_version }}
Expand Down
3 changes: 3 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,6 @@ src/locales/scripts/wp-locales.json
# To prevent accidentally adding a hardcoded robots.txt, see
# /src/server-middleware/robots.js for the robots.txt file.
src/static/robots.txt

# Sentry Config File
.env.sentry-build-plugin
Comment on lines +47 to +48
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the reason for this being named .env.sentry-build-plugin? Could it be similar to the env setup for other other packages so that we can reuse the existing workflow (.env file created from env.template using the env recipe etc.)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was added by the Sentry module wizard. I added the env variable to the GitHub repository, so it's not really necessary anymore.

10 changes: 10 additions & 0 deletions frontend/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export default defineNuxtConfig({
"@nuxt/test-utils/module",
"@nuxtjs/sitemap",
"@nuxtjs/robots",
"@sentry/nuxt/module",
],
routeRules: {
"/photos/**": { redirect: { to: "/image/**", statusCode: 301 } },
Expand Down Expand Up @@ -126,4 +127,13 @@ export default defineNuxtConfig({
trailingSlash: false,
vueI18n: "./vue-i18n",
},
sentry: {
sourceMapsUploadOptions: {
org: "openverse",
project: "openverse-frontend",
},
},
sourcemap: {
client: "hidden",
},
})
4 changes: 2 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@
"@nuxtjs/sitemap": "^7.0.0",
"@nuxtjs/tailwindcss": "^6.12.1",
"@pinia/nuxt": "^0.9.0",
"@sentry/node": "^8.26.0",
"@sentry/vue": "^8.26.0",
"@sentry/nuxt": "^8.45.0",
"@tailwindcss/typography": "^0.5.13",
"@vueuse/core": "^12.0.0",
"@wordpress/is-shallow-equal": "^5.3.0",
"async-mutex": "^0.5.0",
"axios": "^1.7.2",
"axios-mock-adapter": "^1.22.0",
"clipboard": "^2.0.11",
"dotenv": "^16.4.7",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This surprised me because I had read that Nuxt has dotenv built-in.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, we already had it in the lock file due to Nuxt. I added it because it was mentioned in the Nuxt Sentry module setup docs, but removed it after your comment.

"focus-trap": "^7.5.4",
"focus-visible": "^5.2.0",
"pinia": "^2.2.5",
Expand Down
16 changes: 16 additions & 0 deletions frontend/sentry.client.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useAppConfig, useRuntimeConfig } from "#imports"

import * as Sentry from "@sentry/nuxt"

Sentry.init({
dsn: useRuntimeConfig().public.sentry.dsn,
environment: useRuntimeConfig().public.sentry.environment,
release: useAppConfig().semanticVersion,
ignoreErrors: [
// Can be safely ignored, @see https://github.com/WICG/resize-observer/issues/38
/ResizeObserver loop limit exceeded/i,
],

tracesSampleRate: 1.0,
})
Sentry.setContext("render context", { platform: "client" })
15 changes: 15 additions & 0 deletions frontend/sentry.server.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as Sentry from "@sentry/nuxt"
import dotenv from "dotenv"

// Necessary for loading environment variables before Nuxt is loaded
// @see the section on server setup: https://nuxt.com/modules/sentry
dotenv.config()

Sentry.init({
dsn: process.env.NUXT_PUBLIC_SENTRY_DSN,
environment: process.env.NUXT_PUBLIC_SENTRY_ENVIRONMENT,
release: process.env.SEMANTIC_VERSION,

tracesSampleRate: 1.0,
})
Sentry.setContext("render context", { platform: "server" })
37 changes: 0 additions & 37 deletions frontend/server/plugins/sentry.ts

This file was deleted.

6 changes: 3 additions & 3 deletions frontend/src/components/VSketchFabViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ const emit = defineEmits<{ failure: [] }>()
const { t } = useI18n({ useScope: "global" })
const label = t("sketchfabIframeTitle", { sketchfab: "Sketchfab" })
const node = ref<Element | undefined>()
const { $sentry } = useNuxtApp()
const { $captureException, $captureMessage } = useNuxtApp()

const initSketchfab = async () => {
await loadScript(sketchfabUrl)
if (typeof window.Sketchfab === "undefined") {
$sentry.captureMessage("Unable to find window.Sketchfab after loading")
$captureMessage("Unable to find window.Sketchfab after loading")
return
}

Expand All @@ -44,7 +44,7 @@ const initSketchfab = async () => {
const sf = new window.Sketchfab(node.value)
sf.init(props.uid, {
error: (e: unknown) => {
$sentry.captureException(e)
$captureException(e)
emit("failure")
},
})
Expand Down
8 changes: 3 additions & 5 deletions frontend/src/plugins/01.api-token.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { defineNuxtPlugin, useRuntimeConfig } from "#imports"

import { Mutex, MutexInterface } from "async-mutex"
import axios from "axios"
import * as Sentry from "@sentry/nuxt"

import { debug, warn } from "~/utils/console"

import type { AxiosError } from "axios"
import type { NuxtApp } from "#app"

/* Process level state */

Expand Down Expand Up @@ -161,14 +161,12 @@ export const getApiAccessToken = async (): Promise<string | undefined> => {
return process.tokenData.accessToken
}

export default defineNuxtPlugin(async (app) => {
export default defineNuxtPlugin(async () => {
let openverseApiToken: string | undefined
try {
openverseApiToken = await getApiAccessToken()
} catch (e) {
const sentry =
app.ssrContext?.event.context.$sentry ?? (app as NuxtApp).$sentry
sentry.captureException(e)
Sentry.captureException(e)
}
return {
provide: {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/plugins/errors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { defineNuxtPlugin } from "#imports"

import { isAxiosError } from "axios"
import * as Sentry from "@sentry/nuxt"

import { ERR_UNKNOWN, ErrorCode, errorCodes } from "#shared/constants/errors"
import type { SupportedSearchType } from "#shared/constants/media"
Expand Down Expand Up @@ -122,8 +123,7 @@ export function recordError(
searchType: fetchingError.searchType,
})
} else {
const sentry = nuxtApp.ssrContext?.event.context.$sentry ?? nuxtApp.$sentry
sentry.captureException(originalError, { extra: { fetchingError } })
Sentry.captureException(originalError, { extra: { fetchingError } })
}
}

Expand Down
32 changes: 6 additions & 26 deletions frontend/src/plugins/sentry.client.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,13 @@
import { defineNuxtPlugin, useRuntimeConfig, useAppConfig } from "#imports"
import { defineNuxtPlugin } from "#imports"

import * as Sentry from "@sentry/vue"

export default defineNuxtPlugin((nuxtApp) => {
const {
public: { sentry },
} = useRuntimeConfig()

const { semanticVersion } = useAppConfig()

if (!sentry.dsn) {
console.warn("Sentry DSN wasn't provided")
}

Sentry.init({
dsn: sentry.dsn,
environment: sentry.environment,
release: semanticVersion,
app: nuxtApp.vueApp,
ignoreErrors: [
// Can be safely ignored, @see https://github.com/WICG/resize-observer/issues/38
/ResizeObserver loop limit exceeded/i,
],
})
Sentry.setContext("render context", { platform: "client" })
import * as Sentry from "@sentry/nuxt"

export default defineNuxtPlugin(() => {
const { captureException, captureMessage } = Sentry
return {
provide: {
sentry: Sentry,
captureException,
captureMessage,
},
}
})
7 changes: 4 additions & 3 deletions frontend/src/stores/active-media.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useNuxtApp } from "#imports"

import { defineStore } from "pinia"
import { useNuxtApp } from "#app"

import type { SupportedMediaType } from "#shared/constants/media"
import { audioErrorMessages } from "#shared/constants/audio"
Expand Down Expand Up @@ -85,8 +86,8 @@ export const useActiveMediaStore = defineStore(ACTIVE_MEDIA, {
? audioErrorMessages[err.name as keyof typeof audioErrorMessages]
: "err_unknown"
if (message === "err_unknown") {
const { $sentry } = useNuxtApp()
$sentry.captureException(err)
const { $captureException } = useNuxtApp()
$captureException(err)
}
this.setMessage({ message })
audio?.pause()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { expect } from "@playwright/test"
import { test } from "~~/test/playwright/utils/test"
import breakpoints from "~~/test/playwright/utils/breakpoints"
import {
Expand All @@ -6,7 +7,8 @@ import {
sleep,
} from "~~/test/playwright/utils/navigation"
import { setViewportToFullHeight } from "~~/test/playwright/utils/viewport"
import { languageDirections } from "~~/test/playwright/utils/i18n"
import { languageDirections, t } from "~~/test/playwright/utils/i18n"
import { getH1 } from "~~/test/playwright/utils/components"

import { ALL_MEDIA, supportedSearchTypes } from "#shared/constants/media"

Expand Down Expand Up @@ -56,6 +58,7 @@ breakpoints.describeXl(({ breakpoint, expectSnapshot }) => {
// eslint-disable-next-line playwright/no-networkidle
await page.waitForLoadState("networkidle")

await expect(getH1(page, t("404.title"))).toBeVisible()
await expectSnapshot(page, "generic-error-ltr", page, {
screenshotOptions: { fullPage: true },
})
Expand All @@ -77,6 +80,7 @@ for (const searchType of supportedSearchTypes) {
await preparePageForTests(page, breakpoint)
await goToSearchTerm(page, `SearchPage500error`, { searchType })

await expect(getH1(page, t("404.title"))).toBeVisible()
await expectSnapshot(page, "generic-error-ltr", page, {
screenshotOptions: { fullPage: true },
})
Expand All @@ -101,6 +105,7 @@ for (const searchType of supportedSearchTypes) {
searchType,
})

await expect(getH1(page, t("404.title"))).toBeVisible()
await expectSnapshot(page, "generic-error", page, {
dir,
screenshotOptions: {
Expand Down Expand Up @@ -136,6 +141,8 @@ for (const searchType of supportedSearchTypes) {
})
await goToSearchTerm(page, "cat", { dir, searchType, mode: "CSR" })

await expect(getH1(page, t("serverTimeout.heading", dir))).toBeVisible()

await setViewportToFullHeight(page)

await page.mouse.move(0, 82)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { expect } from "@playwright/test"
import { test } from "~~/test/playwright/utils/test"
import breakpoints from "~~/test/playwright/utils/breakpoints"
import {
isPageDesktop,
pathWithDir,
preparePageForTests,
} from "~~/test/playwright/utils/navigation"
Expand All @@ -11,6 +12,7 @@ import {
getHomepageSearchButton,
getLanguageSelect,
getLoadMoreButton,
getMenuButton,
} from "~~/test/playwright/utils/components"

test.describe.configure({ mode: "parallel" })
Expand All @@ -32,6 +34,11 @@ for (const contentPage of contentPages) {

await page.goto(pathWithDir(contentPage, dir))
// Ensure the page is hydrated
// eslint-disable-next-line playwright/no-conditional-in-test
if (!isPageDesktop(page)) {
// eslint-disable-next-line playwright/no-conditional-expect
await expect(getMenuButton(page, dir)).toBeEnabled()
}
await expect(page.locator("#language")).toHaveValue(
dir === "ltr" ? "en" : "ar"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,6 @@ const stubs = {
RouterLink: RouterLinkStub,
}

const captureExceptionMock = vi.fn()

vi.mock("#app", async () => {
const original = await import("#app")
return {
...original,
useNuxtApp: vi.fn(() => ({
$sentry: {
captureException: captureExceptionMock,
},
})),
}
})

describe("AudioTrack", () => {
let options = null
let props = null
Expand Down Expand Up @@ -109,7 +95,6 @@ describe("AudioTrack", () => {
${"NotAllowedError"} | ${/Reproduction not allowed./i}
${"NotSupportedError"} | ${/This audio format is not supported by your browser./i}
${"AbortError"} | ${/You aborted playback./i}
${"UnknownError"} | ${/An unexpected error has occurred./i}
`(
"on play error displays a message instead of the waveform",
async ({ errorType, errorText }) => {
Expand Down Expand Up @@ -139,15 +124,6 @@ describe("AudioTrack", () => {
expect(playStub).toHaveBeenCalledTimes(1)
expect(pauseStub).toHaveBeenCalledTimes(1)
expect(getByText(errorText)).toBeVisible()

// Only the UnknownError should be sent to Sentry.
if (errorType === "UnknownError") {
// eslint-disable-next-line vitest/no-conditional-expect
expect(captureExceptionMock).toHaveBeenCalledWith(playError)
} else {
// eslint-disable-next-line vitest/no-conditional-expect
expect(captureExceptionMock).not.toHaveBeenCalled()
}
}
)

Expand Down
Loading
Loading