Skip to content

Commit a4bdf4f

Browse files
authored
refactor: Updated when partial granularity rules are applied (newrelic#3553)
1 parent 3d85fb5 commit a4bdf4f

File tree

3 files changed

+189
-311
lines changed

3 files changed

+189
-311
lines changed

lib/spans/span-event.js

Lines changed: 43 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,24 @@ const { DESTINATIONS } = require('../config/attribute-filter')
1212
const { addSpanKind, isEntryPointSpan, reparentSpan, shouldCreateSpan, HTTP_LIBRARY, REGEXS, SPAN_KIND, CATEGORIES } = require('./helpers')
1313
const EMPTY_USER_ATTRS = Object.freeze(Object.create(null))
1414
const SERVER_ADDRESS = 'server.address'
15+
const logger = require('../logger').child({ component: 'span-event' })
16+
1517
/**
1618
* This keeps a static list of attributes that are used by
1719
* one or more entity relationship rules to synthesize an entity relationship.
18-
* Note: These attributes also have corresponding TraceSegment attributes
19-
* as this list is checked before a span is made. The ones that are TraceSegment
20-
* attributes are noted in the comments. Any new span attributes being added must
21-
* be checked to ensure those are what is getting assigned to the TraceSegment as well.
2220
*/
2321
const SPAN_ENTITY_RELATIONSHIP_ATTRIBUTES = [
2422
'cloud.account.id',
2523
'cloud.platform',
2624
'cloud.region',
2725
'cloud.resource_id',
28-
'database_name', // gets mapped to `db.instance`
2926
'db.instance',
30-
'product', // gets mapped to `db.system`
3127
'db.system',
3228
'http.url',
33-
'url', // gets mapped to `http.url`
3429
'messaging.destination.name',
3530
'messaging.system',
3631
'peer.hostname',
37-
'host', // gets mapped to `server.address`
38-
'hostname', // gets mapped to `server.address`
3932
'server.address',
40-
'port', // gets mapped to `server.port`
41-
'port_path_or_id', // gets mapped to `server.port`
4233
'server.port',
4334
'span.kind',
4435
]
@@ -147,63 +138,74 @@ class SpanEvent {
147138
return HttpSpanEvent
148139
}
149140

150-
static isExitSpan(segment) {
151-
return REGEXS.CLIENT.EXTERNAL.test(segment.name) || REGEXS.CLIENT.DATASTORE.test(segment.name) || REGEXS.PRODUCER.test(segment.name)
141+
static isExitSpan(span) {
142+
const name = span.intrinsics?.name
143+
return REGEXS.CLIENT.EXTERNAL.test(name) || REGEXS.CLIENT.DATASTORE.test(name) || REGEXS.PRODUCER.test(name)
152144
}
153145

154-
static isLlmSpan(segment) {
155-
return segment.name.startsWith('Llm/')
146+
static isLlmSpan(span) {
147+
return span.intrinsics?.name?.startsWith('Llm/')
156148
}
157149

158150
/**
159-
* Filters attributes for partial trace span events based on a given mode.
151+
* Drops span or filters attributes based on partial trace rules for a given mode.
160152
* The rules are as such:
161-
* - If not a partial trace, return all attributes.
162-
* - If an entry point span, return all attributes.
163-
* - If an LLM span, return all attributes.
164-
* - If not an exit span, return no attributes.
165-
* - If mode is 'reduced' and there are entity relationship attributes, return all attributes.
166-
* - Otherwise, return no attributes.
153+
* - If not a partial trace, return span untouched
154+
* - If an entry point span, return span untouched
155+
* - If an LLM span, return span untouched
156+
* - If not an exit span, return null(aka drop span)
157+
* - If mode is 'reduced' and there are entity relationship attributes, return span untouched
158+
* - If mode is 'essential' and there are entity relationship attributes or error attributes, return span with only those attributes, drop custom attributes
159+
* - Otherwise return null(aka drop span)
167160
*
168161
* @param {object} params to function
169-
* @param {TraceSegment} params.segment segment to filter attributes from
170-
* @param {SpanContext} params.spanContext span context to filter attributes from
162+
* @param {SpanEvent} params.span span to apply rules to
171163
* @param {boolean} params.entryPoint whether the span is an entry point
172164
* @param {string} params.partialGranularityMode mode of partial trace ('reduced', 'essential', 'compact')
173165
* @param {boolean} params.isPartialTrace whether the trace is a partial trace
174-
* @returns {object} { attributes, customAttributes, dropSpan: boolean }
166+
* @returns {SpanEvent|null} the span after applying the rules, or null if dropped
175167
*/
176-
static filterPartialTraceAttributes({ segment, spanContext, entryPoint, partialGranularityMode, isPartialTrace }) {
177-
const attributes = segment.attributes.get(DESTINATIONS.SPAN_EVENT)
178-
const customAttributes = spanContext.customAttributes.get(DESTINATIONS.SPAN_EVENT)
179-
if (!isPartialTrace || entryPoint || SpanEvent.isLlmSpan(segment)) {
180-
return { attributes, customAttributes, dropSpan: false }
168+
static applyPartialTraceRules({ span, entryPoint, partialGranularityMode, isPartialTrace }) {
169+
const isLlmSpan = SpanEvent.isLlmSpan(span)
170+
171+
if (!isPartialTrace || entryPoint || isLlmSpan) {
172+
logger.trace('Span %s is either not a partial trace: %s, an entry point: %s, or an LLM span: %s, keeping span unchanged.', span.intrinsics.name, isPartialTrace, entryPoint, isLlmSpan)
173+
return span
181174
}
182175

183-
if (!SpanEvent.isExitSpan(segment)) {
184-
return { dropSpan: true }
176+
if (!SpanEvent.isExitSpan(span)) {
177+
logger.trace('Span %s is not an exit span and trace is partial granularity mode: %s.', span.intrinsics.name, partialGranularityMode)
178+
return null
185179
}
186180

181+
const attributes = span.attributes
187182
const attrKeys = Object.keys(attributes)
188183
const entityRelationshipAttrs = SPAN_ENTITY_RELATIONSHIP_ATTRIBUTES.filter((item) => attrKeys.includes(item))
189184
if (partialGranularityMode === 'reduced') {
190-
if (entityRelationshipAttrs.length > 0) {
191-
return { attributes, customAttributes, dropSpan: false }
185+
if (entityRelationshipAttrs.length === 0) {
186+
logger.trace('Span %s does not contain any entity relationship attributes %j and trace is partial granularity mode: %s, dropping span.', span.intrinsics.name, span.attributes, partialGranularityMode)
187+
return null
192188
}
189+
logger.trace('Span %s contains entity relationship attributes and trace is partial granularity mode: %s, keeping span unchanged.', span.intrinsics.name, partialGranularityMode)
193190
} else if (partialGranularityMode === 'essential') {
194-
const attributesToKeep = {}
191+
const attributesToKeep = Object.create(null)
195192
for (const item in attributes) {
196193
if (entityRelationshipAttrs.includes(item) || item.startsWith('error.')) {
197194
attributesToKeep[item] = attributes[item]
198195
}
199196
}
200197

201-
if (Object.keys(attributesToKeep).length > 0) {
202-
return { attributes: attributesToKeep, customAttributes: {}, dropSpan: false }
198+
if (Object.keys(attributesToKeep).length === 0) {
199+
logger.trace('Span %s does not contain any entity relationship attributes %j and trace is partial granularity mode: %s, dropping span.', span.intrinsics.name, span.attributes, partialGranularityMode)
200+
return null
203201
}
202+
203+
span.attributes = attributesToKeep
204+
span.customAttributes = Object.create(null)
205+
logger.trace('Span %s contains entity relationship attributes and trace is partial granularity mode: %s, only keeping entity relationship attributes and removing custom attributes.', span.intrinsics.name, partialGranularityMode)
204206
}
205207

206-
return { dropSpan: true }
208+
return span
207209
}
208210

209211
static createSpan({ segment, attributes, customAttributes }) {
@@ -252,17 +254,13 @@ class SpanEvent {
252254
}
253255
}
254256

255-
const { attributes, customAttributes, dropSpan } = SpanEvent.filterPartialTraceAttributes({ spanContext, entryPoint, segment, partialGranularityMode, isPartialTrace: transaction.isPartialTrace })
256-
// If attributes were stripped out due to partial trace filtering, do not create span.
257-
if (dropSpan) {
258-
return null
259-
}
260-
257+
const attributes = segment.attributes.get(DESTINATIONS.SPAN_EVENT)
258+
const customAttributes = spanContext.customAttributes.get(DESTINATIONS.SPAN_EVENT)
261259
const span = SpanEvent.createSpan({ segment, attributes, customAttributes })
262260
span.addIntrinsics({ segment, spanContext, transaction, parentId, isRoot, inProcessSpans, entryPoint })
263261

264262
addSpanKind({ segment, span })
265-
return span
263+
return SpanEvent.applyPartialTraceRules({ span, entryPoint, partialGranularityMode, isPartialTrace: transaction.isPartialTrace })
266264
}
267265

268266
toJSON() {

0 commit comments

Comments
 (0)