Skip to content

Commit

Permalink
feat: Added segment synthesis for internal spans (newrelic#2840)
Browse files Browse the repository at this point in the history
  • Loading branch information
bizob2828 authored Dec 17, 2024
1 parent 483cafe commit 359a266
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 1 deletion.
20 changes: 20 additions & 0 deletions lib/otel/rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class Rule {
static OTEL_SPAN_KIND_SERVER = 'server'
static OTEL_SPAN_KIND_CLIENT = 'client'
static OTEL_SPAN_KIND_PRODUCER = 'producer'
static OTEL_SPAN_KIND_INTERNAL = 'internal'

#name
#spanKinds
Expand Down Expand Up @@ -90,6 +91,10 @@ class Rule {
return this.#spanKinds.includes('consumer')
}

get isInternalRule() {
return this.#spanKinds.includes('internal')
}

get isProducerRule() {
return this.#spanKinds.includes(Rule.OTEL_SPAN_KIND_PRODUCER)
}
Expand Down Expand Up @@ -127,6 +132,7 @@ class RulesEngine {
#fallbackServerRules = new Map()
#clientRules = new Map()
#fallbackClientRules = new Map()
#fallbackInternalRules = new Map()
#fallbackProducerRules = new Map()

constructor() {
Expand All @@ -140,6 +146,8 @@ class RulesEngine {
this.#fallbackClientRules.set(rule.name, rule)
} else if (rule.isProducerRule === true) {
this.#fallbackProducerRules.set(rule.name, rule)
} else if (rule.isInternalRule === true) {
this.#fallbackInternalRules.set(rule.name, rule)
}
continue
}
Expand Down Expand Up @@ -209,6 +217,18 @@ class RulesEngine {
}
break
}

// there currently are no internal rules, just fallback
// if we add new rules they will have to be wired up
case SpanKind.INTERNAL: {
for (const rule of this.#fallbackInternalRules.values()) {
if (rule.matches(otelSpan) === true) {
result = rule
break
}
}
break
}
}

return result
Expand Down
1 change: 1 addition & 0 deletions lib/otel/rules.json
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@
},
{
"name": "Fallback",
"type": "internal",
"matcher": {
"required_span_kinds": [
"internal"
Expand Down
5 changes: 4 additions & 1 deletion lib/otel/segment-synthesis.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ const {
createDbSegment,
createHttpExternalSegment,
createServerSegment,
createProducerSegment
createProducerSegment,
createInternalSegment
} = require('./segments')

class SegmentSynthesizer {
Expand All @@ -36,6 +37,8 @@ class SegmentSynthesizer {
return createDbSegment(this.agent, otelSpan)
case 'external':
return createHttpExternalSegment(this.agent, otelSpan)
case 'internal':
return createInternalSegment(this.agent, otelSpan)
case 'producer':
return createProducerSegment(this.agent, otelSpan)
case 'server':
Expand Down
2 changes: 2 additions & 0 deletions lib/otel/segments/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ const createHttpExternalSegment = require('./http-external')
const createDbSegment = require('./database')
const createServerSegment = require('./server')
const createProducerSegment = require('./producer')
const createInternalSegment = require('./internal')

module.exports = {
createDbSegment,
createHttpExternalSegment,
createInternalSegment,
createProducerSegment,
createServerSegment
}
17 changes: 17 additions & 0 deletions lib/otel/segments/internal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright 2024 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

'use strict'

module.exports = function createInternalSegment(agent, otelSpan) {
const context = agent.tracer.getContext()
const name = `Custom/${otelSpan.name}`
const segment = agent.tracer.createSegment({
name,
parent: context.segment,
transaction: context.transaction
})
return { segment, transaction: context.transaction }
}
11 changes: 11 additions & 0 deletions test/unit/lib/otel/rules.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,14 @@ test('fallback producer rule is met', () => {
assert.notEqual(rule, undefined)
assert.equal(rule.name, 'FallbackProducer')
})

test('fallback internal rule is met', () => {
const engine = new RulesEngine()
const span = new Span(tracer, ROOT_CONTEXT, 'test-span', spanContext, SpanKind.INTERNAL, parentId)
span.setAttribute('foo.bar', 'baz')
span.end()

const rule = engine.test(span)
assert.notEqual(rule, undefined)
assert.equal(rule.name, 'Fallback')
})
19 changes: 19 additions & 0 deletions test/unit/lib/otel/segment-synthesizer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,25 @@ test('should create queue producer segment', (t, end) => {
})
})

test('should create internal custom segment', (t, end) => {
const { agent, synthesizer, parentId, tracer } = t.nr
helper.runInTransaction(agent, (tx) => {
const span = createSpan({
name: 'doer-of-stuff',
kind: SpanKind.INTERNAL,
parentId,
tx,
tracer
})
const { segment, transaction } = synthesizer.synthesize(span)
assert.equal(tx.id, transaction.id)
assert.equal(segment.name, 'Custom/doer-of-stuff')
assert.equal(segment.parentId, tx.trace.root.id)
tx.end()
end()
})
})

test('should log warning span does not match a rule', (t, end) => {
const { agent, synthesizer, loggerMock, parentId, tracer } = t.nr

Expand Down

0 comments on commit 359a266

Please sign in to comment.