@bugsnag/browser-performance
Advanced tools
Comparing version 2.2.0 to 2.3.0-alpha.0
@@ -5,7 +5,9 @@ import { defaultNetworkRequestCallback } from '@bugsnag/request-tracker-performance'; | ||
class NetworkRequestPlugin { | ||
constructor(spanFactory, fetchTracker, xhrTracker) { | ||
constructor(spanFactory, spanContextStorage, fetchTracker, xhrTracker) { | ||
this.spanFactory = spanFactory; | ||
this.spanContextStorage = spanContextStorage; | ||
this.fetchTracker = fetchTracker; | ||
this.xhrTracker = xhrTracker; | ||
this.configEndpoint = ''; | ||
this.autoInstrumentNetworkRequests = true; | ||
this.networkRequestCallback = defaultNetworkRequestCallback; | ||
@@ -16,5 +18,30 @@ this.logger = { debug: console.debug, warn: console.warn, info: console.info, error: console.error }; | ||
return; | ||
const networkRequestInfo = this.networkRequestCallback({ url: startContext.url, type: startContext.type }); | ||
if (!networkRequestInfo) | ||
return; | ||
const shouldPropagateTraceContextByDefault = startContext.url.startsWith(window.origin); | ||
const defaultRequestInfo = { | ||
url: startContext.url, | ||
type: startContext.type, | ||
propagateTraceContext: shouldPropagateTraceContextByDefault | ||
}; | ||
const networkRequestInfo = this.networkRequestCallback(defaultRequestInfo); | ||
// returning null neither creates a span nor propagates trace context | ||
// if autoInstrumentNetworkRequests not true, don't create a span | ||
// regardless of the result of networkRequestCallback | ||
if (!networkRequestInfo || !this.autoInstrumentNetworkRequests) { | ||
return { | ||
onRequestEnd: undefined, | ||
extraRequestHeaders: undefined | ||
}; | ||
} | ||
if (networkRequestInfo.propagateTraceContext === undefined) { | ||
networkRequestInfo.propagateTraceContext = shouldPropagateTraceContextByDefault; | ||
} | ||
// a span is not created if url is null | ||
if (!networkRequestInfo.url) { | ||
return { | ||
onRequestEnd: undefined, | ||
// propagate trace context if requested using span context | ||
extraRequestHeaders: networkRequestInfo.propagateTraceContext ? this.getExtraRequestHeaders() : undefined | ||
}; | ||
} | ||
// otherwise, create a span and propagate trace context if requested | ||
if (typeof networkRequestInfo.url !== 'string') { | ||
@@ -28,7 +55,13 @@ this.logger.warn(`expected url to be a string following network request callback, got ${typeof networkRequestInfo.url}`); | ||
span.setAttribute('http.url', networkRequestInfo.url); | ||
return (endContext) => { | ||
if (endContext.state === 'success') { | ||
span.setAttribute('http.status_code', endContext.status); | ||
this.spanFactory.endSpan(span, endContext.endTime); | ||
} | ||
return { | ||
onRequestEnd: (endContext) => { | ||
if (endContext.state === 'success') { | ||
span.setAttribute('http.status_code', endContext.status); | ||
this.spanFactory.endSpan(span, endContext.endTime); | ||
} | ||
}, | ||
// propagate trace context using network span | ||
extraRequestHeaders: networkRequestInfo.propagateTraceContext | ||
? this.getExtraRequestHeaders(span) | ||
: undefined | ||
}; | ||
@@ -39,8 +72,7 @@ }; | ||
this.logger = configuration.logger; | ||
if (configuration.autoInstrumentNetworkRequests) { | ||
this.configEndpoint = configuration.endpoint; | ||
this.xhrTracker.onStart(this.trackRequest); | ||
this.fetchTracker.onStart(this.trackRequest); | ||
this.networkRequestCallback = configuration.networkRequestCallback; | ||
} | ||
this.configEndpoint = configuration.endpoint; | ||
this.xhrTracker.onStart(this.trackRequest); | ||
this.fetchTracker.onStart(this.trackRequest); | ||
this.autoInstrumentNetworkRequests = configuration.autoInstrumentNetworkRequests; | ||
this.networkRequestCallback = configuration.networkRequestCallback; | ||
} | ||
@@ -50,4 +82,24 @@ shouldTrackRequest(startContext) { | ||
} | ||
getExtraRequestHeaders(span) { | ||
const extraRequestHeaders = {}; | ||
if (span) { | ||
const traceId = span.traceId; | ||
const parentSpanId = span.id; | ||
const sampled = this.spanFactory.sampler.shouldSample(span.samplingRate); | ||
extraRequestHeaders.traceparent = buildTraceparentHeader(traceId, parentSpanId, sampled); | ||
} | ||
else if (this.spanContextStorage.current) { | ||
const currentSpanContext = this.spanContextStorage.current; | ||
const traceId = currentSpanContext.traceId; | ||
const parentSpanId = currentSpanContext.id; | ||
const sampled = this.spanFactory.sampler.shouldSample(currentSpanContext.samplingRate); | ||
extraRequestHeaders.traceparent = buildTraceparentHeader(traceId, parentSpanId, sampled); | ||
} | ||
return extraRequestHeaders; | ||
} | ||
} | ||
function buildTraceparentHeader(traceId, parentSpanId, sampled) { | ||
return `00-${traceId}-${parentSpanId}-${sampled ? '01' : '00'}`; | ||
} | ||
export { NetworkRequestPlugin }; |
@@ -18,3 +18,4 @@ function shouldOmitSpan(startTime, endTime) { | ||
} | ||
const entry = performance.getEntriesByType('navigation')[0]; | ||
const entries = performance.getEntriesByType('navigation'); | ||
const entry = Array.isArray(entries) && entries[0]; | ||
if (entry) { | ||
@@ -21,0 +22,0 @@ createPageLoadPhaseSpan('Unload', entry.unloadEventStart, entry.unloadEventEnd); |
@@ -44,2 +44,3 @@ import { isObject, isString, coreSpanOptionSchema } from '@bugsnag/core-performance'; | ||
traceId: '', | ||
samplingRate: 0, | ||
isValid: () => false, | ||
@@ -68,2 +69,3 @@ end: () => { } | ||
isValid: span.isValid, | ||
samplingRate: span.samplingRate, | ||
end: (endTimeOrOptions) => { | ||
@@ -70,0 +72,0 @@ const options = isObject(endTimeOrOptions) ? endTimeOrOptions : { endTime: endTimeOrOptions }; |
@@ -52,3 +52,3 @@ import { createNoopClient, createClient, InMemoryQueue } from '@bugsnag/core-performance'; | ||
new ResourceLoadPlugin(spanFactory, spanContextStorage, window.PerformanceObserver), | ||
new NetworkRequestPlugin(spanFactory, fetchRequestTracker, xhrRequestTracker), | ||
new NetworkRequestPlugin(spanFactory, spanContextStorage, fetchRequestTracker, xhrRequestTracker), | ||
new RouteChangePlugin(spanFactory, window.location, document) | ||
@@ -55,0 +55,0 @@ ], |
@@ -26,9 +26,11 @@ import { Settler } from './settler.js'; | ||
++this.outstandingRequests; | ||
return (endContext) => { | ||
if (--this.outstandingRequests === 0) { | ||
// we wait 100ms to ensure that requests have actually stopped but don't | ||
// want the settled time to reflect that wait, so we record the time | ||
// here and use that when settling | ||
const settledTime = this.clock.now(); | ||
this.timeout = setTimeout(() => { this.settle(settledTime); }, 100); | ||
return { | ||
onRequestEnd: (endContext) => { | ||
if (--this.outstandingRequests === 0) { | ||
// we wait 100ms to ensure that requests have actually stopped but don't | ||
// want the settled time to reflect that wait, so we record the time | ||
// here and use that when settling | ||
const settledTime = this.clock.now(); | ||
this.timeout = setTimeout(() => { this.settle(settledTime); }, 100); | ||
} | ||
} | ||
@@ -35,0 +37,0 @@ }; |
@@ -8,3 +8,3 @@ import cuid from '@bugsnag/cuid'; | ||
return function resourceAttributesSource(config) { | ||
const attributes = new ResourceAttributes(config.releaseStage, config.appVersion, 'bugsnag.performance.browser', '2.2.0'); | ||
const attributes = new ResourceAttributes(config.releaseStage, config.appVersion, 'bugsnag.performance.browser', '2.3.0-alpha.0'); | ||
attributes.set('browser.user_agent', navigator.userAgent); | ||
@@ -11,0 +11,0 @@ // chromium only |
@@ -1,2 +0,2 @@ | ||
import { type InternalConfiguration, type Plugin, type SpanFactory } from '@bugsnag/core-performance'; | ||
import type { InternalConfiguration, Plugin, SpanFactory, SpanContextStorage } from '@bugsnag/core-performance'; | ||
import { type NetworkRequestInfo, type RequestTracker } from '@bugsnag/request-tracker-performance'; | ||
@@ -9,12 +9,15 @@ import { type BrowserConfiguration } from '../config'; | ||
private spanFactory; | ||
private readonly spanContextStorage; | ||
private fetchTracker; | ||
private xhrTracker; | ||
private configEndpoint; | ||
private autoInstrumentNetworkRequests; | ||
private networkRequestCallback; | ||
private logger; | ||
constructor(spanFactory: SpanFactory<BrowserConfiguration>, fetchTracker: RequestTracker, xhrTracker: RequestTracker); | ||
constructor(spanFactory: SpanFactory<BrowserConfiguration>, spanContextStorage: SpanContextStorage, fetchTracker: RequestTracker, xhrTracker: RequestTracker); | ||
configure(configuration: InternalConfiguration<BrowserConfiguration>): void; | ||
private trackRequest; | ||
private shouldTrackRequest; | ||
private getExtraRequestHeaders; | ||
} | ||
//# sourceMappingURL=network-request-plugin.d.ts.map |
@@ -43,3 +43,4 @@ class WebVitals { | ||
firstContentfulPaint() { | ||
const entry = this.performance.getEntriesByName('first-contentful-paint', 'paint')[0]; | ||
const entries = this.performance.getEntriesByName('first-contentful-paint', 'paint'); | ||
const entry = Array.isArray(entries) && entries[0]; | ||
if (entry) { | ||
@@ -50,3 +51,4 @@ return entry.startTime; | ||
timeToFirstByte() { | ||
const entry = this.performance.getEntriesByType('navigation')[0]; | ||
const entries = this.performance.getEntriesByType('navigation'); | ||
const entry = Array.isArray(entries) && entries[0]; | ||
let responseStart; | ||
@@ -67,3 +69,4 @@ if (entry) { | ||
firstInputDelay() { | ||
const entry = this.performance.getEntriesByType('first-input')[0]; | ||
const entries = this.performance.getEntriesByType('first-input'); | ||
const entry = Array.isArray(entries) && entries[0]; | ||
if (entry) { | ||
@@ -70,0 +73,0 @@ return { |
{ | ||
"name": "@bugsnag/browser-performance", | ||
"version": "2.2.0", | ||
"version": "2.3.0-alpha.0", | ||
"description": "BugSnag performance monitoring for browsers", | ||
@@ -24,6 +24,6 @@ "homepage": "https://www.bugsnag.com/", | ||
"dependencies": { | ||
"@bugsnag/core-performance": "^2.2.0", | ||
"@bugsnag/core-performance": "^2.3.0-alpha.0", | ||
"@bugsnag/cuid": "^3.0.2", | ||
"@bugsnag/delivery-fetch-performance": "^2.2.0", | ||
"@bugsnag/request-tracker-performance": "^2.2.0" | ||
"@bugsnag/delivery-fetch-performance": "^2.3.0-alpha.0", | ||
"@bugsnag/request-tracker-performance": "^2.3.0-alpha.0" | ||
}, | ||
@@ -36,3 +36,3 @@ "type": "module", | ||
], | ||
"gitHead": "7b88c34595012a11948078bed7505a1ee0854e17" | ||
"gitHead": "fde1316da0f448bedbdad6a22f9732d14c07ce61" | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
83366
1389
1