-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #98 from jill64/issues-97
minor: support @sentry/sveltekit routing instrumentation
- Loading branch information
Showing
10 changed files
with
330 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { hasTracingEnabled } from '@sentry/core' | ||
import { BrowserOptions, BrowserTracing } from '@sentry/svelte' | ||
import { addOrUpdateIntegration } from '@sentry/utils' | ||
import { svelteKitRoutingInstrumentation } from './router.js' | ||
|
||
export const addClientIntegrations = (options: BrowserOptions) => { | ||
let integrations = options.integrations || [] | ||
|
||
if (hasTracingEnabled(options)) { | ||
const defaultBrowserTracingIntegration = new BrowserTracing({ | ||
routingInstrumentation: svelteKitRoutingInstrumentation | ||
}) | ||
|
||
integrations = addOrUpdateIntegration( | ||
defaultBrowserTracingIntegration, | ||
integrations, | ||
{ | ||
'options.routingInstrumentation': svelteKitRoutingInstrumentation | ||
} | ||
) | ||
} | ||
|
||
options.integrations = integrations | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { SDK_VERSION } from '@sentry/core' | ||
import { BrowserOptions } from '@sentry/svelte' | ||
|
||
const PACKAGE_NAME_PREFIX = 'npm:@sentry/' | ||
|
||
export const applySdkMetadata = (options: BrowserOptions, names: string[]) => { | ||
options._metadata = options._metadata || {} | ||
options._metadata.sdk = options._metadata.sdk || { | ||
name: 'sentry.javascript.sveltekit', | ||
packages: names.map((name) => ({ | ||
name: `${PACKAGE_NAME_PREFIX}${name}`, | ||
version: SDK_VERSION | ||
})), | ||
version: SDK_VERSION | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { WINDOW } from '@sentry/svelte' | ||
|
||
export const restoreFetch = (actualFetch: typeof fetch) => { | ||
const globalWithSentryFetchProxy = WINDOW | ||
|
||
// @ts-expect-error TODO: fix this | ||
globalWithSentryFetchProxy._sentryFetchProxy = | ||
globalWithSentryFetchProxy.fetch | ||
|
||
globalWithSentryFetchProxy.fetch = actualFetch | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import { navigating, page } from '$app/stores' | ||
import { getActiveTransaction } from '@sentry/core' | ||
import { WINDOW } from '@sentry/svelte' | ||
import type { Transaction, TransactionContext } from '@sentry/types' | ||
import type { Span } from '@sentry/types' | ||
|
||
const DEFAULT_TAGS = { | ||
'routing.instrumentation': '@sentry/sveltekit' | ||
} | ||
|
||
export const svelteKitRoutingInstrumentation = <T extends Transaction>( | ||
startTransactionFn: (context: TransactionContext) => T | undefined, | ||
startTransactionOnPageLoad?: boolean, | ||
startTransactionOnLocationChange?: boolean | ||
) => { | ||
if (startTransactionOnPageLoad) { | ||
instrumentPageload(startTransactionFn) | ||
} | ||
|
||
if (startTransactionOnLocationChange) { | ||
instrumentNavigations(startTransactionFn) | ||
} | ||
} | ||
|
||
const instrumentPageload = <T extends Transaction>( | ||
startTransactionFn: (context: TransactionContext) => T | undefined | ||
) => { | ||
const initialPath = WINDOW && WINDOW.location && WINDOW.location.pathname | ||
|
||
const pageloadTransaction = startTransactionFn({ | ||
name: initialPath, | ||
op: 'pageload', | ||
origin: 'auto.pageload.sveltekit', | ||
description: initialPath, | ||
tags: { | ||
...DEFAULT_TAGS | ||
}, | ||
metadata: { | ||
source: 'url' | ||
} | ||
}) | ||
|
||
page.subscribe((page) => { | ||
if (!page) { | ||
return | ||
} | ||
|
||
const routeId = page.route && page.route.id | ||
|
||
if (pageloadTransaction && routeId) { | ||
pageloadTransaction.setName(routeId, 'route') | ||
} | ||
}) | ||
} | ||
|
||
/** | ||
* Use the `navigating` store to start a transaction on navigations. | ||
*/ | ||
const instrumentNavigations = <T extends Transaction>( | ||
startTransactionFn: (context: TransactionContext) => T | undefined | ||
) => { | ||
let routingSpan: Span | undefined = undefined | ||
let activeTransaction | ||
|
||
navigating.subscribe((navigation) => { | ||
if (!navigation) { | ||
// `navigating` emits a 'null' value when the navigation is completed. | ||
// So in this case, we can finish the routing span. If the transaction was an IdleTransaction, | ||
// it will finish automatically and if it was user-created users also need to finish it. | ||
if (routingSpan) { | ||
routingSpan.finish() | ||
routingSpan = undefined | ||
} | ||
return | ||
} | ||
|
||
const from = navigation.from | ||
const to = navigation.to | ||
|
||
// for the origin we can fall back to window.location.pathname because in this emission, it still is set to the origin path | ||
const rawRouteOrigin = | ||
(from && from.url.pathname) || | ||
(WINDOW && WINDOW.location && WINDOW.location.pathname) | ||
|
||
const rawRouteDestination = to && to.url.pathname | ||
|
||
// We don't want to create transactions for navigations of same origin and destination. | ||
// We need to look at the raw URL here because parameterized routes can still differ in their raw parameters. | ||
if (rawRouteOrigin === rawRouteDestination) { | ||
return | ||
} | ||
|
||
const parameterizedRouteOrigin = from && from.route.id | ||
const parameterizedRouteDestination = to && to.route.id | ||
|
||
activeTransaction = getActiveTransaction() | ||
|
||
if (!activeTransaction) { | ||
activeTransaction = startTransactionFn({ | ||
name: parameterizedRouteDestination || rawRouteDestination || 'unknown', | ||
op: 'navigation', | ||
origin: 'auto.navigation.sveltekit', | ||
metadata: { source: parameterizedRouteDestination ? 'route' : 'url' }, | ||
tags: { | ||
...DEFAULT_TAGS | ||
} | ||
}) | ||
} | ||
|
||
if (activeTransaction) { | ||
if (routingSpan) { | ||
// If a routing span is still open from a previous navigation, we finish it. | ||
routingSpan.finish() | ||
} | ||
routingSpan = activeTransaction.startChild({ | ||
op: 'ui.sveltekit.routing', | ||
description: 'SvelteKit Route Change', | ||
origin: 'auto.ui.sveltekit' | ||
}) | ||
activeTransaction.setTag('from', parameterizedRouteOrigin) | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { BrowserOptions, configureScope, init } from '@sentry/svelte' | ||
import { addClientIntegrations } from './addClientIntegrations.js' | ||
import { applySdkMetadata } from './applySdkMetadata.js' | ||
import { restoreFetch } from './restoreFetch.js' | ||
import { switchToFetchProxy } from './switchToFetchProxy.js' | ||
|
||
export const sentryInit = (options: BrowserOptions) => { | ||
applySdkMetadata(options, ['sveltekit', 'svelte']) | ||
|
||
addClientIntegrations(options) | ||
|
||
// 1. Switch window.fetch to our fetch proxy we injected earlier | ||
const actualFetch = switchToFetchProxy() | ||
|
||
// 2. Initialize the SDK which will instrument our proxy | ||
init(options) | ||
|
||
// 3. Restore the original fetch now that our proxy is instrumented | ||
if (actualFetch) { | ||
restoreFetch(actualFetch) | ||
} | ||
|
||
configureScope((scope) => { | ||
scope.setTag('runtime', 'browser') | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { WINDOW } from '@sentry/svelte' | ||
|
||
export function switchToFetchProxy() { | ||
const globalWithSentryFetchProxy = WINDOW | ||
|
||
const actualFetch = globalWithSentryFetchProxy.fetch | ||
|
||
// @ts-expect-error TODO: fix this | ||
if (globalWithSentryFetchProxy._sentryFetchProxy && actualFetch) { | ||
globalWithSentryFetchProxy.fetch = | ||
// @ts-expect-error TODO: fix this | ||
globalWithSentryFetchProxy._sentryFetchProxy | ||
return actualFetch | ||
} | ||
|
||
return undefined | ||
} |
Oops, something went wrong.