diff --git a/packages/deno/package.json b/packages/deno/package.json index 6ba7a746dad7..21d76bbed255 100644 --- a/packages/deno/package.json +++ b/packages/deno/package.json @@ -13,7 +13,6 @@ }, "files": ["index.mjs", "index.mjs.map", "index.d.ts"], "dependencies": { - "@sentry/browser": "8.0.0-alpha.0", "@sentry/core": "8.0.0-alpha.0", "@sentry/types": "8.0.0-alpha.0", "@sentry/utils": "8.0.0-alpha.0" diff --git a/packages/deno/src/index.ts b/packages/deno/src/index.ts index 067cfd8a1599..50a422f776ab 100644 --- a/packages/deno/src/index.ts +++ b/packages/deno/src/index.ts @@ -95,7 +95,6 @@ export { init, } from './sdk'; -export { breadcrumbsIntegration } from '@sentry/browser'; import { Integrations as CoreIntegrations } from '@sentry/core'; export { denoContextIntegration } from './integrations/context'; @@ -103,6 +102,7 @@ export { globalHandlersIntegration } from './integrations/globalhandlers'; export { normalizePathsIntegration } from './integrations/normalizepaths'; export { contextLinesIntegration } from './integrations/contextlines'; export { denoCronIntegration } from './integrations/deno-cron'; +export { breadcrumbsIntegration } from './integrations/breadcrumbs'; import * as DenoIntegrations from './integrations'; diff --git a/packages/deno/src/integrations/breadcrumbs.ts b/packages/deno/src/integrations/breadcrumbs.ts new file mode 100644 index 000000000000..886d941d843f --- /dev/null +++ b/packages/deno/src/integrations/breadcrumbs.ts @@ -0,0 +1,171 @@ +import { addBreadcrumb, defineIntegration, getClient } from '@sentry/core'; +import type { Client, Event as SentryEvent, HandlerDataConsole, HandlerDataFetch, IntegrationFn } from '@sentry/types'; +import type { FetchBreadcrumbData, FetchBreadcrumbHint } from '@sentry/types/build/types/breadcrumb'; +import { + addConsoleInstrumentationHandler, + addFetchInstrumentationHandler, + getEventDescription, + safeJoin, + severityLevelFromString, +} from '@sentry/utils'; + +interface BreadcrumbsOptions { + console: boolean; + fetch: boolean; + sentry: boolean; +} + +const INTEGRATION_NAME = 'Breadcrumbs'; + +const _breadcrumbsIntegration = ((options: Partial = {}) => { + const _options = { + console: true, + fetch: true, + sentry: true, + ...options, + }; + + return { + name: INTEGRATION_NAME, + setup(client) { + if (_options.console) { + addConsoleInstrumentationHandler(_getConsoleBreadcrumbHandler(client)); + } + if (_options.fetch) { + addFetchInstrumentationHandler(_getFetchBreadcrumbHandler(client)); + } + if (_options.sentry) { + client.on('beforeSendEvent', _getSentryBreadcrumbHandler(client)); + } + }, + }; +}) satisfies IntegrationFn; + +/** + * This breadcrumbsIntegration is almost the same as the one from @sentry/browser. + * The Deno-version does not support browser-specific APIs like dom, xhr and history. + */ +export const breadcrumbsIntegration = defineIntegration(_breadcrumbsIntegration); + +/** + * Adds a breadcrumb for Sentry events or transactions if this option is enabled. + * + */ +function _getSentryBreadcrumbHandler(client: Client): (event: SentryEvent) => void { + return function addSentryBreadcrumb(event: SentryEvent): void { + if (getClient() !== client) { + return; + } + + addBreadcrumb( + { + category: `sentry.${event.type === 'transaction' ? 'transaction' : 'event'}`, + event_id: event.event_id, + level: event.level, + message: getEventDescription(event), + }, + { + event, + }, + ); + }; +} + +/** + * Creates breadcrumbs from console API calls + */ +function _getConsoleBreadcrumbHandler(client: Client): (handlerData: HandlerDataConsole) => void { + return function _consoleBreadcrumb(handlerData: HandlerDataConsole): void { + if (getClient() !== client) { + return; + } + + const breadcrumb = { + category: 'console', + data: { + arguments: handlerData.args, + logger: 'console', + }, + level: severityLevelFromString(handlerData.level), + message: safeJoin(handlerData.args, ' '), + }; + + if (handlerData.level === 'assert') { + if (handlerData.args[0] === false) { + breadcrumb.message = `Assertion failed: ${safeJoin(handlerData.args.slice(1), ' ') || 'console.assert'}`; + breadcrumb.data.arguments = handlerData.args.slice(1); + } else { + // Don't capture a breadcrumb for passed assertions + return; + } + } + + addBreadcrumb(breadcrumb, { + input: handlerData.args, + level: handlerData.level, + }); + }; +} + +/** + * Creates breadcrumbs from fetch API calls + */ +function _getFetchBreadcrumbHandler(client: Client): (handlerData: HandlerDataFetch) => void { + return function _fetchBreadcrumb(handlerData: HandlerDataFetch): void { + if (getClient() !== client) { + return; + } + + const { startTimestamp, endTimestamp } = handlerData; + + // We only capture complete fetch requests + if (!endTimestamp) { + return; + } + + if (handlerData.fetchData.url.match(/sentry_key/) && handlerData.fetchData.method === 'POST') { + // We will not create breadcrumbs for fetch requests that contain `sentry_key` (internal sentry requests) + return; + } + + if (handlerData.error) { + const data: FetchBreadcrumbData = handlerData.fetchData; + const hint: FetchBreadcrumbHint = { + data: handlerData.error, + input: handlerData.args, + startTimestamp, + endTimestamp, + }; + + addBreadcrumb( + { + category: 'fetch', + data, + level: 'error', + type: 'http', + }, + hint, + ); + } else { + const response = handlerData.response as Response | undefined; + const data: FetchBreadcrumbData = { + ...handlerData.fetchData, + status_code: response && response.status, + }; + const hint: FetchBreadcrumbHint = { + input: handlerData.args, + response, + startTimestamp, + endTimestamp, + }; + addBreadcrumb( + { + category: 'fetch', + data, + type: 'http', + }, + hint, + ); + } + }; +} diff --git a/packages/deno/src/sdk.ts b/packages/deno/src/sdk.ts index b0452ca5302e..9ce7761bcea9 100644 --- a/packages/deno/src/sdk.ts +++ b/packages/deno/src/sdk.ts @@ -1,11 +1,16 @@ -import { breadcrumbsIntegration, dedupeIntegration } from '@sentry/browser'; import type { ServerRuntimeClientOptions } from '@sentry/core'; -import { functionToStringIntegration, inboundFiltersIntegration, linkedErrorsIntegration } from '@sentry/core'; +import { + dedupeIntegration, + functionToStringIntegration, + inboundFiltersIntegration, + linkedErrorsIntegration, +} from '@sentry/core'; import { getIntegrationsToSetup, initAndBind } from '@sentry/core'; import type { Integration, Options, StackParser } from '@sentry/types'; import { createStackParser, nodeStackLineParser, stackParserFromStackParserOptions } from '@sentry/utils'; import { DenoClient } from './client'; +import { breadcrumbsIntegration } from './integrations/breadcrumbs'; import { denoContextIntegration } from './integrations/context'; import { contextLinesIntegration } from './integrations/contextlines'; import { globalHandlersIntegration } from './integrations/globalhandlers'; @@ -21,14 +26,9 @@ export function getDefaultIntegrations(_options: Options): Integration[] { inboundFiltersIntegration(), functionToStringIntegration(), linkedErrorsIntegration(), - // From Browser dedupeIntegration(), - breadcrumbsIntegration({ - dom: false, - history: false, - xhr: false, - }), // Deno Specific + breadcrumbsIntegration(), denoContextIntegration(), contextLinesIntegration(), normalizePathsIntegration(),