Skip to content

Commit

Permalink
feat: add _experimental_longtaskNoStartSession flag
Browse files Browse the repository at this point in the history
  • Loading branch information
Joozty committed Dec 6, 2024
1 parent 767d36e commit 10ef10a
Show file tree
Hide file tree
Showing 23 changed files with 646 additions and 303 deletions.
4 changes: 2 additions & 2 deletions .size-limit.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ module.exports = [

{
name: 'artifacts/splunk-otel-web.js',
limit: '40 kB',
limit: '41 kB',
path: './packages/web/dist/artifacts/splunk-otel-web.js',
},

{
name: 'artifacts/splunk-otel-web.js',
limit: '72 kB',
limit: '73 kB',
path: './packages/web/dist/artifacts/splunk-otel-web-legacy.js',
},

Expand Down
4 changes: 4 additions & 0 deletions packages/session-recorder/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,10 @@ const SplunkRumRecorder = {
return
}

if (SplunkRum._internalOnExternalSpanCreated) {
SplunkRum._internalOnExternalSpanCreated()
}

// Safeguards from our ingest getting DDOSed:
// 1. A session can send up to 4 hours of data
// 2. Recording resumes on session change if it isn't a background tab (session regenerated in an another tab)
Expand Down
11 changes: 8 additions & 3 deletions packages/web/integration-tests/tests/cookies/cookies.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,22 +80,27 @@ module.exports = {
}

/*
We are using nip.io to let us test subdomains not sure how reliable it is, so if
We are using nip.io to let us test subdomains not sure how reliable it is, so if
you are debugging flaky test then this should be your first guess.
cookies-domain.ejs has cookieDomain set to 127.0.0.1.nip.io, cookie set via cookieDomain
should be accessible for subdomains also so when we go to test. subdomain we should find the same
should be accessible for subdomains also so when we go to test. subdomain we should find the same
cookie.
*/
const protocol = browser.globals.enableHttps ? 'https' : 'http'
await browser.url(`${protocol}://127.0.0.1.nip.io:${browser.globals.httpPort}/cookies/cookies-domain.ejs`)
const cookie = await browser.getCookie('_splunk_rum_sid')
const cookieParse = decodeURI(cookie.value)

await browser.assert.ok(cookie)

await browser.url(`${protocol}://test.127.0.0.1.nip.io:${browser.globals.httpPort}/cookies/cookies-domain.ejs`)

const cookie2 = await browser.getCookie('_splunk_rum_sid')
const cookie2Parse = decodeURI(cookie2.value)

await browser.assert.strictEqual(cookie.domain, cookie2.domain)
await browser.assert.strictEqual(cookie.value, cookie2.value)
await browser.assert.strictEqual(cookieParse.id, cookie2Parse.id)
await browser.assert.strictEqual(cookieParse.startTime, cookie2Parse.startTime)

await browser.globals.assertNoErrorSpans()
},
Expand Down
8 changes: 1 addition & 7 deletions packages/web/src/SplunkContextManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,7 @@
import { Context, ContextManager, ROOT_CONTEXT } from '@opentelemetry/api'
import { unwrap } from 'shimmer'
import { getOriginalFunction, isFunction, wrapNatively } from './utils'

export interface ContextManagerConfig {
/** Enable async tracking of span parents */
async?: boolean
onBeforeContextEnd?: () => void
onBeforeContextStart?: () => void
}
import { ContextManagerConfig } from './types'

type EventListenerWithOrig = EventListener & { _orig?: EventListener }

Expand Down
18 changes: 17 additions & 1 deletion packages/web/src/SplunkLongTaskInstrumentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,21 @@
import { InstrumentationBase, InstrumentationConfig } from '@opentelemetry/instrumentation'

import { VERSION } from './version'
import { getCurrentSessionState } from './session'
import { SplunkOtelWebConfig } from './types'

const LONGTASK_PERFORMANCE_TYPE = 'longtask'
const MODULE_NAME = 'splunk-longtask'

export class SplunkLongTaskInstrumentation extends InstrumentationBase {
private _longtaskObserver: PerformanceObserver | undefined

constructor(config: InstrumentationConfig = {}) {
private initOptions: SplunkOtelWebConfig

constructor(config: InstrumentationConfig = {}, initOptions: SplunkOtelWebConfig) {
super(MODULE_NAME, VERSION, Object.assign({}, config))

this.initOptions = initOptions
}

disable(): void {
Expand All @@ -52,6 +58,16 @@ export class SplunkLongTaskInstrumentation extends InstrumentationBase {
init(): void {}

private _createSpanFromEntry(entry: PerformanceEntry) {
if (
!!this.initOptions._experimental_longtaskNoStartSession &&
!getCurrentSessionState({
forceStoreRead: false,
})
) {
// session expired, we do not want to spawn new session from long tasks
return
}

const span = this.tracer.startSpan(LONGTASK_PERFORMANCE_TYPE, {
startTime: entry.startTime,
})
Expand Down
172 changes: 31 additions & 141 deletions packages/web/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,24 @@
*/

import './polyfill-safari10'
import { InstrumentationConfig, registerInstrumentations } from '@opentelemetry/instrumentation'
import { registerInstrumentations } from '@opentelemetry/instrumentation'
import {
ConsoleSpanExporter,
SimpleSpanProcessor,
BatchSpanProcessor,
ReadableSpan,
SpanExporter,
SpanProcessor,
BufferConfig,
AlwaysOffSampler,
AlwaysOnSampler,
ParentBasedSampler,
} from '@opentelemetry/sdk-trace-base'
import { WebTracerConfig } from '@opentelemetry/sdk-trace-web'
import { Attributes, diag, DiagConsoleLogger, DiagLogLevel } from '@opentelemetry/api'
import { SplunkDocumentLoadInstrumentation } from './SplunkDocumentLoadInstrumentation'
import { SplunkXhrPlugin } from './SplunkXhrPlugin'
import { SplunkFetchInstrumentation } from './SplunkFetchInstrumentation'
import {
SplunkUserInteractionInstrumentation,
SplunkUserInteractionInstrumentationConfig,
DEFAULT_AUTO_INSTRUMENTED_EVENTS,
DEFAULT_AUTO_INSTRUMENTED_EVENT_NAMES,
UserInteractionEventsConfig,
Expand All @@ -46,163 +43,35 @@ import { SplunkExporterConfig } from './exporters/common'
import { SplunkZipkinExporter } from './exporters/zipkin'
import { ERROR_INSTRUMENTATION_NAME, SplunkErrorInstrumentation } from './SplunkErrorInstrumentation'
import { generateId, getPluginConfig } from './utils'
import { getRumSessionId, initSessionTracking } from './session'
import { getRumSessionId, initSessionTracking, updateSessionStatus } from './session'
import { SplunkWebSocketInstrumentation } from './SplunkWebSocketInstrumentation'
import { WebVitalsInstrumentationConfig, initWebVitals } from './webvitals'
import { initWebVitals } from './webvitals'
import { SplunkLongTaskInstrumentation } from './SplunkLongTaskInstrumentation'
import { SplunkPageVisibilityInstrumentation } from './SplunkPageVisibilityInstrumentation'
import { SplunkConnectivityInstrumentation } from './SplunkConnectivityInstrumentation'
import {
SplunkPostDocLoadResourceInstrumentation,
SplunkPostDocLoadResourceInstrumentationConfig,
} from './SplunkPostDocLoadResourceInstrumentation'
import { SplunkPostDocLoadResourceInstrumentation } from './SplunkPostDocLoadResourceInstrumentation'
import { SplunkWebTracerProvider } from './SplunkWebTracerProvider'
import { FetchInstrumentationConfig } from '@opentelemetry/instrumentation-fetch'
import { XMLHttpRequestInstrumentationConfig } from '@opentelemetry/instrumentation-xml-http-request'
import { InternalEventTarget, SplunkOtelWebEventTarget } from './EventTarget'
import { ContextManagerConfig, SplunkContextManager } from './SplunkContextManager'
import { SplunkContextManager } from './SplunkContextManager'
import { Resource, ResourceAttributes } from '@opentelemetry/resources'
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'
import { SDK_INFO, _globalThis } from '@opentelemetry/core'
import { VERSION } from './version'
import { getSyntheticsRunId, SYNTHETICS_RUN_ID_ATTRIBUTE } from './synthetics'
import { SplunkSpanAttributesProcessor } from './SplunkSpanAttributesProcessor'
import { SessionBasedSampler } from './SessionBasedSampler'
import {
SocketIoClientInstrumentationConfig,
SplunkSocketIoClientInstrumentation,
} from './SplunkSocketIoClientInstrumentation'
import { SplunkSocketIoClientInstrumentation } from './SplunkSocketIoClientInstrumentation'
import { SplunkOTLPTraceExporter } from './exporters/otlp'
import { registerGlobal, unregisterGlobal } from './global-utils'
import { BrowserInstanceService } from './services/BrowserInstanceService'
import { SessionId } from './types'
import { SessionId } from './session'
import { SplunkOtelWebConfig, SplunkOtelWebExporterOptions, SplunkOtelWebOptionsInstrumentations } from './types'

export { SplunkExporterConfig } from './exporters/common'
export { SplunkZipkinExporter } from './exporters/zipkin'
export * from './SplunkWebTracerProvider'
export * from './SessionBasedSampler'

interface SplunkOtelWebOptionsInstrumentations {
connectivity?: boolean | InstrumentationConfig
document?: boolean | InstrumentationConfig
errors?: boolean
fetch?: boolean | FetchInstrumentationConfig
interactions?: boolean | SplunkUserInteractionInstrumentationConfig
longtask?: boolean | InstrumentationConfig
postload?: boolean | SplunkPostDocLoadResourceInstrumentationConfig
socketio?: boolean | SocketIoClientInstrumentationConfig
visibility?: boolean | InstrumentationConfig
websocket?: boolean | InstrumentationConfig
webvitals?: boolean | WebVitalsInstrumentationConfig
xhr?: boolean | XMLHttpRequestInstrumentationConfig
}

export interface SplunkOtelWebExporterOptions {
/**
* Allows remapping Span's attributes right before they're serialized.
* One potential use case of this method is to remove PII from the attributes.
*/
onAttributesSerializing?: (attributes: Attributes, span: ReadableSpan) => Attributes

/**
* Switch from zipkin to otlp for exporting
*/
otlp?: boolean
}

export interface SplunkOtelWebConfig {
/**
* If enabled, all spans are treated as activity and extend the duration of the session. Defaults to false.
*/
_experimental_allSpansExtendSession?: boolean

/** Allows http beacon urls */
allowInsecureBeacon?: boolean

/** Application name
* @deprecated Renamed to `applicationName`
*/
app?: string

/** Application name */
applicationName?: string

/** Destination for the captured data */
beaconEndpoint?: string

/**
* Destination for the captured data
* @deprecated Renamed to `beaconEndpoint`, or use realm
*/
beaconUrl?: string

/** Options for context manager */
context?: ContextManagerConfig

/** Sets session cookie to this domain */
cookieDomain?: string

/** Turns on/off internal debug logging */
debug?: boolean

/**
* Sets a value for the `environment` attribute (persists through calls to `setGlobalAttributes()`)
* */
deploymentEnvironment?: string

/**
* Sets a value for the `environment` attribute (persists through calls to `setGlobalAttributes()`)
* @deprecated Renamed to `deploymentEnvironment`
*/
environment?: string

/** Allows configuring how telemetry data is sent to the backend */
exporter?: SplunkOtelWebExporterOptions

/** Sets attributes added to every Span. */
globalAttributes?: Attributes

/**
* Applies for XHR, Fetch and Websocket URLs. URLs that partially match any regex in ignoreUrls will not be traced.
* In addition, URLs that are _exact matches_ of strings in ignoreUrls will also not be traced.
* */
ignoreUrls?: Array<string | RegExp>

/** Configuration for instrumentation modules. */
instrumentations?: SplunkOtelWebOptionsInstrumentations

/**
* The name of your organization’s realm. Automatically configures beaconUrl with correct URL
*/
realm?: string

/**
* Publicly-visible rum access token value. Please do not paste any other access token or auth value into here, as this
* will be visible to every user of your app
*/
rumAccessToken?: string

/**
* Publicly-visible `rumAuth` value. Please do not paste any other access token or auth value into here, as this
* will be visible to every user of your app
* @deprecated Renamed to rumAccessToken
*/
rumAuth?: string

/**
* Config options passed to web tracer
*/
tracer?: WebTracerConfig

/** Use local storage to save session ID instead of cookie */
useLocalStorage?: boolean

/**
* Sets a value for the 'app.version' attribute
*/
version?: string
}

interface SplunkOtelWebConfigInternal extends SplunkOtelWebConfig {
bufferSize?: number
bufferTimeout?: number
Expand Down Expand Up @@ -327,6 +196,11 @@ export interface SplunkOtelWebType extends SplunkOtelWebEventTarget {
*/
_internalInit: (options: Partial<SplunkOtelWebConfigInternal>) => void

/* Used internally by the SplunkSessionRecorder - span from session can extend the session */
_internalOnExternalSpanCreated: () => void

_processedOptions: SplunkOtelWebConfigInternal | null

attributesProcessor?: SplunkSpanAttributesProcessor

deinit: (force?: boolean) => void
Expand Down Expand Up @@ -370,6 +244,8 @@ export const SplunkRum: SplunkOtelWebType = {
ParentBasedSampler,
SessionBasedSampler,

_processedOptions: null,

get inited(): boolean {
return inited
},
Expand Down Expand Up @@ -421,6 +297,8 @@ export const SplunkRum: SplunkOtelWebType = {
},
)

this._processedOptions = processedOptions

if (processedOptions.realm) {
if (!processedOptions.beaconEndpoint) {
processedOptions.beaconEndpoint = getBeaconEndpointForRealm(processedOptions)
Expand Down Expand Up @@ -487,8 +365,12 @@ export const SplunkRum: SplunkOtelWebType = {
const instrumentations = INSTRUMENTATIONS.map(({ Instrument, confKey, disable }) => {
const pluginConf = getPluginConfig(processedOptions.instrumentations[confKey], pluginDefaults, disable)
if (pluginConf) {
// @ts-expect-error Can't mark in any way that processedOptions.instrumentations[confKey] is of specifc config type
const instrumentation = new Instrument(pluginConf)
const instrumentation =
Instrument === SplunkLongTaskInstrumentation
? new Instrument(pluginConf, options)
: // @ts-expect-error Can't mark in any way that processedOptions.instrumentations[confKey] is of specifc config type
new Instrument(pluginConf)

if (confKey === ERROR_INSTRUMENTATION_NAME && instrumentation instanceof SplunkErrorInstrumentation) {
_errorInstrumentation = instrumentation
}
Expand Down Expand Up @@ -633,6 +515,14 @@ export const SplunkRum: SplunkOtelWebType = {
_experimental_getSessionId() {
return this.getSessionId()
},

_internalOnExternalSpanCreated() {
if (!this._processedOptions) {
return
}

updateSessionStatus({ forceStore: false, useLocalStorage: this._processedOptions.useLocalStorage ?? false })
},
}

export default SplunkRum
Loading

0 comments on commit 10ef10a

Please sign in to comment.