Skip to content

Commit 91f91c2

Browse files
authored
refactor: Added ability to prevent creation of segments in subscriber based instrumentation when parent is marked as internal and of the same package (newrelic#3322)
1 parent aa3f8d9 commit 91f91c2

File tree

2 files changed

+81
-5
lines changed

2 files changed

+81
-5
lines changed

lib/subscribers/base.js

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,40 @@
88
const { tracingChannel } = require('node:diagnostics_channel')
99

1010
/**
11-
* Base class for defining a subscriber.
12-
* events property is an array with the following possible event names:
13-
* `start`, `end`, `asyncStart`, `asyncEnd`, `error`
14-
* @link https://nodejs.org/api/diagnostics_channel.html#class-tracingchannel
11+
* The baseline parameters available to all subscribers.
12+
*
13+
* @typedef {object} SubscriberParams
14+
* @property {object} agent A New Relic Node.js agent instance.
15+
* @property {object} logger An agent logger instance.
16+
* @property {string} packageName The npm installable name for the package
17+
* being instrumented. This is what a developer would provide to the `require`
18+
* function.
19+
* @property {string} channelName A unique name for the diagnostics channel
20+
* that will be created and monitored.
21+
*/
22+
23+
/**
24+
* @property {object} agent A New Relic Node.js agent instance.
25+
* @property {object} logger An agent logger instance.
26+
* @property {object} config The agent configuration object.
27+
* @property {string} packageName The name of the module being instrumented.
28+
* This is the same string one would pass to the `require` function.
29+
* @property {string} channelName A unique name for the diagnostics channel
30+
* that will be registered.
31+
* @property {string[]} [events=[]] Set of tracing channel event names to
32+
* register handlers for. For any name in the set, a corresponding method
33+
* must exist on the subscriber instance. The method will be passed the
34+
* event object. Possible event names are `start`, `end`, `asyncStart`,
35+
* `asyncEnd`, and `error`. @link https://nodejs.org/api/diagnostics_channel.html#class-tracingchannel
36+
* @property {boolean} [opaque=false] If true, any children segments will not be created
37+
* @property {boolean} [internal=false] If true, any children segments from the same library will not be created
38+
* @property {string} [prefix='orchestrion:'] String to prepend to diagnostics
39+
* channel event names. This provides a namespace for the events we are
40+
* injecting into a module.
41+
* @property {boolean} [requireActiveTx=true] If true, the subscriber will only handle events when there is an active transaction.
42+
* @property {string} id A unique identifier for the subscriber, combining the prefix, package name, and channel name.
43+
* @property {TracingChannel} channel The tracing channel instance this subscriber will be monitoring.
44+
* @property {AsyncLocalStorage} store The async local storage instance used for context management.
1545
*/
1646
class Subscriber {
1747
constructor({ agent, logger, packageName, channelName }) {
@@ -22,13 +52,20 @@ class Subscriber {
2252
this.channelName = channelName
2353
this.events = []
2454
this.opaque = false
55+
this.internal = false
2556
this.prefix = 'orchestrion:'
2657
this.requireActiveTx = true
2758
this.id = `${this.prefix}${this.packageName}:${this.channelName}`
2859
this.channel = tracingChannel(this.id)
2960
this.store = agent.tracer._contextManager._asyncLocalStorage
3061
}
3162

63+
shouldCreateSegment(parent) {
64+
return !(parent?.opaque ||
65+
(this.internal && this.packageName === parent?.shimId)
66+
)
67+
}
68+
3269
/**
3370
* Creates a segment with a name, parent, transaction and optional recorder.
3471
* If the segment is successfully created, it will be started and added to the context.
@@ -39,15 +76,23 @@ class Subscriber {
3976
* @returns {Context} - The updated context with the new segment or existing context if segment creation fails
4077
*/
4178
createSegment({ name, recorder, ctx }) {
79+
const parent = ctx?.segment
80+
81+
if (this.shouldCreateSegment(parent) === false) {
82+
this.logger.trace('Skipping segment creation for %s, %s(parent) is of the same package: %s and incoming segment is marked as internal', name, parent?.name, this.packageName)
83+
return ctx
84+
}
85+
4286
const segment = this.agent.tracer.createSegment({
4387
name,
44-
parent: ctx?.segment,
88+
parent,
4589
recorder,
4690
transaction: ctx?.transaction,
4791
})
4892

4993
if (segment) {
5094
segment.opaque = this.opaque
95+
segment.shimId = this.packageName
5196
segment.start()
5297
this.logger.trace('Created segment %s', name)
5398
this.addAttributes(segment)

test/unit/lib/subscribers/base.test.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,37 @@ test('should not create segment if no active tx', (t) => {
9595
assert.ok(!newCtx.segment)
9696
})
9797

98+
test('should not create segment if parent is opaque', async (t) => {
99+
const { agent, subscriber } = t.nr
100+
await helper.runInTransaction(agent, async () => {
101+
const ctx = agent.tracer.getContext()
102+
ctx.segment.opaque = true
103+
const newCtx = subscriber.createSegment({
104+
name: 'test-segment',
105+
ctx,
106+
})
107+
108+
assert.deepEqual(newCtx, ctx)
109+
assert.equal(newCtx.segment.name, ctx.segment.name)
110+
})
111+
})
112+
113+
test('should not create segment if parent is of same package and subscriber is internal', async (t) => {
114+
const { agent, subscriber } = t.nr
115+
await helper.runInTransaction(agent, async () => {
116+
const ctx = agent.tracer.getContext()
117+
ctx.segment.shimId = 'test-package'
118+
subscriber.internal = true
119+
const newCtx = subscriber.createSegment({
120+
name: 'test-segment',
121+
ctx,
122+
})
123+
124+
assert.deepEqual(newCtx, ctx)
125+
assert.equal(newCtx.segment.name, ctx.segment.name)
126+
})
127+
})
128+
98129
test('should touch segment when asyncEnd is called', (t, end) => {
99130
const { agent, subscriber } = t.nr
100131
helper.runInTransaction(agent, () => {

0 commit comments

Comments
 (0)