Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion lib/metrics/names.js
Original file line number Diff line number Diff line change
Expand Up @@ -291,11 +291,15 @@ const DISTRIBUTED_TRACE = {

const SPAN_EVENT_PREFIX = 'SpanEvent/'

const PARTIAL_GRANULARITY = 'PartialGranularity/'

const SPAN_EVENTS = {
SEEN: SUPPORTABILITY.PREFIX + SPAN_EVENT_PREFIX + 'TotalEventsSeen',
SENT: SUPPORTABILITY.PREFIX + SPAN_EVENT_PREFIX + 'TotalEventsSent',
DROPPED: SUPPORTABILITY.PREFIX + SPAN_EVENT_PREFIX + 'Discarded',
LIMIT: SUPPORTABILITY.PREFIX + SPAN_EVENT_PREFIX + 'Limit'
LIMIT: SUPPORTABILITY.PREFIX + SPAN_EVENT_PREFIX + 'Limit',
KEPT: SUPPORTABILITY.PREFIX + 'DistributedTrace/' + PARTIAL_GRANULARITY + '%s' + '/Span/Kept',
INSTRUMENTED: SUPPORTABILITY.PREFIX + 'DistributedTrace/' + PARTIAL_GRANULARITY + '%s' + '/Span/Instrumented',
}

const INFINITE_TRACING = {
Expand Down
9 changes: 9 additions & 0 deletions lib/spans/span-event-aggregator.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const logger = require('../logger').child({ component: 'span_aggregator' })
const EventAggregator = require('../aggregators/event-aggregator')
const SpanEvent = require('./span-event')
const NAMES = require('../metrics/names')
const util = require('util')

const DEFAULT_SPAN_EVENT_LIMIT = 2000
// Used only when server value missing
Expand Down Expand Up @@ -75,10 +76,18 @@ class SpanEventAggregator extends EventAggregator {

return false
}

if (transaction.partialType) {
this._metrics.getOrCreateMetric(util.format(this._metricNames.INSTRUMENTED, transaction.partialType)).incrementCallCount()
}

const span = SpanEvent.fromSegment({ segment, transaction, parentId, isRoot, inProcessSpans: this.inProcessSpans })

if (span) {
this.add(span, transaction.priority)
if (transaction.partialType) {
this._metrics.getOrCreateMetric(util.format(this._metricNames.KEPT, transaction.partialType)).incrementCallCount()
}
}

if (segment.spanLinks.length > 0) {
Expand Down
49 changes: 49 additions & 0 deletions test/unit/spans/full-granularity-spans.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2025 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

'use strict'
const assert = require('node:assert')
const test = require('node:test')
const helper = require('#testlib/agent_helper.js')
const SpanEvent = require('#agentlib/spans/span-event.js')

test('Partial Granularity metrics with Full Granularity settings', async (t) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

full granularity tests are in span-events.test.js, we don't need another file for this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I'll move them. Sorry about that.

t.beforeEach((ctx) => {
const agent = helper.loadMockedAgent({
distributed_tracing: {
enabled: true,
sampler: {
full_granularity: {
enabled: true
},
partial_granularity: {
enabled: false,
}
}
}
})
ctx.nr = { agent }
})

t.afterEach((ctx) => {
helper.unloadAgent(ctx.nr.agent)
})

await t.test('should not record partial granularity metrics when not part of partialTrace', (t, end) => {
const { agent } = t.nr
helper.runInTransaction(agent, (transaction) => {
const segment = transaction.trace.add('Datastore/operation/Redis/SET')
const span = SpanEvent.fromSegment({ segment, transaction, inProcessSpans: true })
assert.ok(span)
transaction.end()
const unscopedMetrics = agent.metrics._metrics.unscoped
assert.equal(unscopedMetrics['Supportability/DistributedTrace/PartialGranularity/reduced/Span/Instrumented'], undefined)
assert.equal(unscopedMetrics['Supportability/DistributedTrace/PartialGranularity/reduced/Span/Kept'], undefined)
assert.equal(unscopedMetrics['Supportability/DistributedTrace/PartialGranularity/essential/Span/Instrumented'], undefined)
assert.equal(unscopedMetrics['Supportability/DistributedTrace/PartialGranularity/essential/Span/Kept'], undefined)
end()
})
})
})
69 changes: 63 additions & 6 deletions test/unit/spans/partial-granularity-spans.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ for (const mode of MODES) {
const agent = helper.loadMockedAgent({
distributed_tracing: {
enabled: true,
full_granularity: {
enabled: false
},
partial_granularity: {
enabled: true,
type: mode
sampler: {
full_granularity: {
enabled: false
},
partial_granularity: {
enabled: true,
type: mode
}
}
}
})
Expand Down Expand Up @@ -172,5 +174,60 @@ for (const mode of MODES) {
end()
})
})

await t.test('should record a instrumented and kept metric for exit span that has entity relationship attrs', (t, end) => {
const { agent } = t.nr
helper.runInTransaction(agent, (transaction) => {
transaction.partialType = mode
const segment = transaction.trace.add('Datastore/operation/Redis/SET')
segment.addAttribute('host', 'redis-service')
segment.addAttribute('port_path_or_id', 6379)
segment.addAttribute('foo', 'bar')
const spanContext = segment.getSpanContext()
spanContext.addCustomAttribute('custom', 'test')
const span = SpanEvent.fromSegment({ segment, transaction, inProcessSpans: true })
assert.ok(span)
transaction.end()

const unscopedMetrics = agent.metrics._metrics.unscoped
assert.equal(unscopedMetrics[`Supportability/DistributedTrace/PartialGranularity/${mode}/Span/Instrumented`].callCount, 1)
assert.equal(unscopedMetrics[`Supportability/DistributedTrace/PartialGranularity/${mode}/Span/Kept`].callCount, 1)
end()
})
})

await t.test('should record instrumented metric only for dropped exit span that does not have entity relationship attrs', (t, end) => {
const { agent } = t.nr
helper.runInTransaction(agent, (transaction) => {
transaction.partialType = mode
const segment = transaction.trace.add('Datastore/operation/Redis/SET')
segment.addAttribute('foo', 'bar')
const span = SpanEvent.fromSegment({ segment, transaction, inProcessSpans: true })
assert.ok(!span)
transaction.end()
const unscopedMetrics = agent.metrics._metrics.unscoped
assert.equal(unscopedMetrics[`Supportability/DistributedTrace/PartialGranularity/${mode}/Span/Instrumented`].callCount, 1)
// span was dropped so kept metric was not recorded
assert.equal(unscopedMetrics[`Supportability/DistributedTrace/PartialGranularity/${mode}/Span/Kept`], undefined)
end()
})
})

await t.test('should record instrumented metric only for dropped in process span', (t, end) => {
const { agent } = t.nr
helper.runInTransaction(agent, (transaction) => {
transaction.partialType = mode
const segment = transaction.trace.add('test-segment')
segment.addAttribute('foo', 'bar')
const span = SpanEvent.fromSegment({ segment, transaction, inProcessSpans: true })
assert.ok(!span)
transaction.end()
const unscopedMetrics = agent.metrics._metrics.unscoped
assert.equal(unscopedMetrics[`Supportability/DistributedTrace/PartialGranularity/${mode}/Span/Instrumented`].callCount, 1)
// span was dropped so kept metric was not recorded
assert.equal(unscopedMetrics[`Supportability/DistributedTrace/PartialGranularity/${mode}/Span/Kept`], undefined)
end()
})
})
})
}
Loading