Skip to content

Commit

Permalink
feat: added spanContextCallback
Browse files Browse the repository at this point in the history
  • Loading branch information
cjr125 authored and Christopher Roberts committed Jan 22, 2025
1 parent 21534b8 commit 9d4a46e
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 25 deletions.
38 changes: 35 additions & 3 deletions docs/configuration.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -428,8 +428,9 @@ NOTE: If APM Server is deployed in an origin different than the page’s origin,
* *Type:* Function
* *Default:* `null`

`transactionContextCallback` allows the agent to specify a function to be called when starting automatically instrumented transactions and spans and return
context to be set as tags. This enables the agent to capture the context when instrumented events are fired from files which do not import the RUM agent library.
`transactionContextCallback` allows the agent to specify a function to be called when starting automatically instrumented transactions and return context to
be set as tags. This enables the agent to capture data such as call stack frames and variable values from the scope when instrumented events are fired from
files which do not import the RUM agent library.

The following example illustrates an example which captures the stack trace:

Expand All @@ -448,4 +449,35 @@ var options = {
return { stack };
}
}
----
----


[function]
[[span-context-callback]]
==== `spanContextCallback`

* *Type:* Function
* *Default:* `null`

`spanContextCallback` allows the agent to specify a function to be called when starting automatically instrumented spans and return context to be set as tags.
This enables the agent to capture data such as call stack frames and variable values from the scope when instrumented events are fired from files which do
not import the RUM agent library.

The following example illustrates an example which captures the stack trace:

[source,js]
----
var options = {
spanContextCallback: () => {
let stack
try {
throw new Error('')
}
catch (error) {
stack = (error as Error).stack || ''
}
stack = stack.split('\n').map(function (line) { return line.trim(); })
return { stack };
}
}
----
3 changes: 2 additions & 1 deletion packages/rum-core/src/common/config-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ class Config {
session: false,
apmRequest: null,
sendCredentials: false,
transactionContextCallback: null
transactionContextCallback: null,
spanContextCallback: null
}

this.events = new EventHandler()
Expand Down
13 changes: 13 additions & 0 deletions packages/rum-core/src/performance-monitoring/span.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,19 @@ class Span extends SpanBase {
this.action = fields[2]
}
this.sync = this.options.sync

if (
this.options.spanContextCallback &&
typeof this.options.spanContextCallback === 'function'
) {
let tags
try {
tags = this.options.spanContextCallback()
this.addLabels(tags)
} catch (e) {
console.error('Failed to execute span context callback', e)
}
}
}

end(endTime, data) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ class TransactionService {

createOptions(options) {
const config = this._config.config
let presetOptions = {
let presetOptions = {
transactionSampleRate: config.transactionSampleRate
}
if (config.transactionContextCallback) {
Expand All @@ -110,6 +110,12 @@ class TransactionService {
transactionContextCallback: config.transactionContextCallback
}
}
if (config.spanContextCallback) {
presetOptions = {
...presetOptions,
spanContextCallback: config.spanContextCallback
}
}
let perfOptions = extend(presetOptions, options)
if (perfOptions.managed) {
perfOptions = extend(
Expand Down Expand Up @@ -292,7 +298,6 @@ class TransactionService {
if (name === NAME_UNKNOWN && pageLoadTransactionName) {
tr.name = pageLoadTransactionName
}

/**
* Capture the TBT as span after observing for all long task entries
* and once performance observer is disconnected
Expand Down Expand Up @@ -493,13 +498,6 @@ class TransactionService {
)
}

if (this._config.config.transactionContextCallback) {
options = {
...options,
tags: this._config.config.transactionContextCallback()
}
}

const span = tr.startSpan(name, type, options)
if (__DEV__) {
this._logger.debug(
Expand Down
26 changes: 21 additions & 5 deletions packages/rum-core/src/performance-monitoring/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,16 @@ class Transaction extends SpanBase {
this.sampleRate = this.options.transactionSampleRate
this.sampled = Math.random() <= this.sampleRate

if (this.options.transactionContextCallback) {
this.options = {
...this.options,
tags: this.options.transactionContextCallback()
if (
this.options.transactionContextCallback &&
typeof this.options.transactionContextCallback === 'function'
) {
let tags
try {
tags = this.options.transactionContextCallback()
this.addLabels(tags)
} catch (e) {
console.error('Failed to execute transaction context callback', e)
}
}
}
Expand Down Expand Up @@ -103,7 +109,17 @@ class Transaction extends SpanBase {
if (this.ended) {
return
}
const opts = extend({}, options)
let opts = extend({}, options)

if (
this.options.spanContextCallback &&
typeof this.options.spanContextCallback === 'function'
) {
opts = {
...opts,
spanContextCallback: this.options.spanContextCallback
}
}

opts.onEnd = trc => {
this._onSpanEnd(trc)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -652,18 +652,19 @@ describe('TransactionService', function () {
transaction.end(pageLoadTime + 1000)
})

it('should capture tags from dispatch context', done => {
it('should capture tags from transaction dispatch context', done => {
config.setConfig({
transactionContextCallback: () => {
let stack
try {
throw new Error('')
}
catch (error) {
} catch (error) {
stack = error.stack || ''
}
stack = stack.split('\n').map(function (line) { return line.trim(); })
return { stack };
stack = stack.split('\n').map(function (line) {
return line.trim()
})
return { stack }
}
})
const transactionService = new TransactionService(logger, config)
Expand All @@ -674,11 +675,37 @@ describe('TransactionService', function () {
)

tr1.onEnd = () => {
expect(tr1.options.tags.stack).toBeTruthy()
expect(tr1.context.tags.stack).toBeTruthy()
done()
}
tr1.end()
})

it('should capture tags from span dispatch context', done => {
config.setConfig({
spanContextCallback: () => {
let stack
try {
throw new Error('')
} catch (error) {
stack = error.stack || ''
}
stack = stack.split('\n').map(function (line) {
return line.trim()
})
return { stack }
}
})
const transactionService = new TransactionService(logger, config)

const sp1 = transactionService.startSpan('span1', 'span')

sp1.onEnd = () => {
expect(sp1.context.tags.stack).toBeTruthy()
done()
}
sp1.end()
})
})

it('should truncate active spans after transaction ends', () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/rum/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ declare module '@elastic/apm-rum' {
payload?: string
headers?: Record<string, string>
}) => boolean,
transactionContextCallback?: (...args: any[]) => any
transactionContextCallback?: (...args: any[]) => any,
spanContextCallback?: (...args: any[]) => any
}

type Init = (options?: AgentConfigOptions) => ApmBase
Expand Down

0 comments on commit 9d4a46e

Please sign in to comment.