-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add BrowserTracing integration (#2723)
* test: remove hub.startSpan test * feat(tracing): Add BrowserTracing integration and tests * fix: defaultRoutingInstrumentation * ref: Remove static methods * multiple before finishes * ref: Routing Instrumentation * remove tracing
- Loading branch information
1 parent
f0f288d
commit 9587853
Showing
17 changed files
with
698 additions
and
1,334 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
import { Hub } from '@sentry/hub'; | ||
import { EventProcessor, Integration, Transaction as TransactionType, TransactionContext } from '@sentry/types'; | ||
import { logger } from '@sentry/utils'; | ||
|
||
import { startIdleTransaction } from '../hubextensions'; | ||
import { DEFAULT_IDLE_TIMEOUT } from '../idletransaction'; | ||
import { Span } from '../span'; | ||
|
||
import { defaultBeforeNavigate, defaultRoutingInstrumentation } from './router'; | ||
|
||
/** Options for Browser Tracing integration */ | ||
export interface BrowserTracingOptions { | ||
/** | ||
* The time to wait in ms until the transaction will be finished. The transaction will use the end timestamp of | ||
* the last finished span as the endtime for the transaction. | ||
* Time is in ms. | ||
* | ||
* Default: 1000 | ||
*/ | ||
idleTimeout: number; | ||
|
||
/** | ||
* Flag to enable/disable creation of `navigation` transaction on history changes. | ||
* | ||
* Default: true | ||
*/ | ||
startTransactionOnLocationChange: boolean; | ||
|
||
/** | ||
* Flag to enable/disable creation of `pageload` transaction on first pageload. | ||
* | ||
* Default: true | ||
*/ | ||
startTransactionOnPageLoad: boolean; | ||
|
||
/** | ||
* beforeNavigate is called before a pageload/navigation transaction is created and allows for users | ||
* to set a custom navigation transaction name. Defaults behaviour is to return `window.location.pathname`. | ||
* | ||
* If undefined is returned, a pageload/navigation transaction will not be created. | ||
*/ | ||
beforeNavigate(context: TransactionContext): TransactionContext | undefined; | ||
|
||
/** | ||
* Instrumentation that creates routing change transactions. By default creates | ||
* pageload and navigation transactions. | ||
*/ | ||
routingInstrumentation<T extends TransactionType>( | ||
startTransaction: (context: TransactionContext) => T | undefined, | ||
startTransactionOnPageLoad?: boolean, | ||
startTransactionOnLocationChange?: boolean, | ||
): void; | ||
} | ||
|
||
/** | ||
* The Browser Tracing integration automatically instruments browser pageload/navigation | ||
* actions as transactions, and captures requests, metrics and errors as spans. | ||
* | ||
* The integration can be configured with a variety of options, and can be extended to use | ||
* any routing library. This integration uses {@see IdleTransaction} to create transactions. | ||
*/ | ||
export class BrowserTracing implements Integration { | ||
/** | ||
* @inheritDoc | ||
*/ | ||
public static id: string = 'BrowserTracing'; | ||
|
||
/** Browser Tracing integration options */ | ||
public options: BrowserTracingOptions = { | ||
beforeNavigate: defaultBeforeNavigate, | ||
idleTimeout: DEFAULT_IDLE_TIMEOUT, | ||
routingInstrumentation: defaultRoutingInstrumentation, | ||
startTransactionOnLocationChange: true, | ||
startTransactionOnPageLoad: true, | ||
}; | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
public name: string = BrowserTracing.id; | ||
|
||
private _getCurrentHub?: () => Hub; | ||
|
||
// navigationTransactionInvoker() -> Uses history API NavigationTransaction[] | ||
|
||
public constructor(_options?: Partial<BrowserTracingOptions>) { | ||
this.options = { | ||
...this.options, | ||
..._options, | ||
}; | ||
} | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
public setupOnce(_: (callback: EventProcessor) => void, getCurrentHub: () => Hub): void { | ||
this._getCurrentHub = getCurrentHub; | ||
|
||
const { routingInstrumentation, startTransactionOnLocationChange, startTransactionOnPageLoad } = this.options; | ||
|
||
routingInstrumentation( | ||
(context: TransactionContext) => this._createRouteTransaction(context), | ||
startTransactionOnPageLoad, | ||
startTransactionOnLocationChange, | ||
); | ||
} | ||
|
||
/** Create routing idle transaction. */ | ||
private _createRouteTransaction(context: TransactionContext): TransactionType | undefined { | ||
if (!this._getCurrentHub) { | ||
logger.warn(`[Tracing] Did not creeate ${context.op} idleTransaction due to invalid _getCurrentHub`); | ||
return undefined; | ||
} | ||
|
||
const { beforeNavigate, idleTimeout } = this.options; | ||
|
||
// if beforeNavigate returns undefined, we should not start a transaction. | ||
const ctx = beforeNavigate({ | ||
...context, | ||
...getHeaderContext(), | ||
}); | ||
|
||
if (ctx === undefined) { | ||
logger.log(`[Tracing] Did not create ${context.op} idleTransaction due to beforeNavigate`); | ||
return undefined; | ||
} | ||
|
||
const hub = this._getCurrentHub(); | ||
logger.log(`[Tracing] starting ${ctx.op} idleTransaction on scope with context:`, ctx); | ||
return startIdleTransaction(hub, ctx, idleTimeout, true) as TransactionType; | ||
} | ||
} | ||
|
||
/** | ||
* Gets transaction context from a sentry-trace meta. | ||
*/ | ||
function getHeaderContext(): Partial<TransactionContext> { | ||
const header = getMetaContent('sentry-trace'); | ||
if (header) { | ||
const span = Span.fromTraceparent(header); | ||
if (span) { | ||
return { | ||
parentSpanId: span.parentSpanId, | ||
sampled: span.sampled, | ||
traceId: span.traceId, | ||
}; | ||
} | ||
} | ||
|
||
return {}; | ||
} | ||
|
||
/** Returns the value of a meta tag */ | ||
export function getMetaContent(metaName: string): string | null { | ||
const el = document.querySelector(`meta[name=${metaName}]`); | ||
return el ? el.getAttribute('content') : null; | ||
} |
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 @@ | ||
export { BrowserTracing } from './browsertracing'; |
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,61 @@ | ||
import { Transaction as TransactionType, TransactionContext } from '@sentry/types'; | ||
import { addInstrumentationHandler, getGlobalObject, logger } from '@sentry/utils'; | ||
|
||
// type StartTransaction | ||
const global = getGlobalObject<Window>(); | ||
|
||
/** | ||
* Creates a default router based on | ||
*/ | ||
export function defaultRoutingInstrumentation<T extends TransactionType>( | ||
startTransaction: (context: TransactionContext) => T | undefined, | ||
startTransactionOnPageLoad: boolean = true, | ||
startTransactionOnLocationChange: boolean = true, | ||
): void { | ||
if (!global || !global.location) { | ||
logger.warn('Could not initialize routing instrumentation due to invalid location'); | ||
return; | ||
} | ||
|
||
let startingUrl: string | undefined = global.location.href; | ||
|
||
let activeTransaction: T | undefined; | ||
if (startTransactionOnPageLoad) { | ||
activeTransaction = startTransaction({ name: global.location.pathname, op: 'pageload' }); | ||
} | ||
|
||
if (startTransactionOnLocationChange) { | ||
addInstrumentationHandler({ | ||
callback: ({ to, from }: { to: string; from?: string }) => { | ||
/** | ||
* This early return is there to account for some cases where navigation transaction | ||
* starts right after long running pageload. We make sure that if `from` is undefined | ||
* and that a valid `startingURL` exists, we don't uncessarily create a navigation transaction. | ||
* | ||
* This was hard to duplicate, but this behaviour stopped as soon as this fix | ||
* was applied. This issue might also only be caused in certain development environments | ||
* where the usage of a hot module reloader is causing errors. | ||
*/ | ||
if (from === undefined && startingUrl && startingUrl.indexOf(to) !== -1) { | ||
startingUrl = undefined; | ||
return; | ||
} | ||
if (from !== to) { | ||
startingUrl = undefined; | ||
if (activeTransaction) { | ||
// We want to finish all current ongoing idle transactions as we | ||
// are navigating to a new page. | ||
activeTransaction.finish(); | ||
} | ||
activeTransaction = startTransaction({ name: global.location.pathname, op: 'navigation' }); | ||
} | ||
}, | ||
type: 'history', | ||
}); | ||
} | ||
} | ||
|
||
/** default implementation of Browser Tracing before navigate */ | ||
export function defaultBeforeNavigate(context: TransactionContext): TransactionContext | undefined { | ||
return context; | ||
} |
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
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 |
---|---|---|
@@ -1,2 +1 @@ | ||
export { Express } from './express'; | ||
export { Tracing } from './tracing'; |
Oops, something went wrong.