Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: span statistics -- ensure dropped span objects are still created #2694

Merged
merged 20 commits into from
May 20, 2022
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
4 changes: 4 additions & 0 deletions lib/instrumentation/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,10 @@ Instrumentation.prototype.addEndedSpan = function (span) {
}

Instrumentation.prototype._encodeAndSendSpan = function (span) {
if (!span.isRecorded()) {
return
}

astorm marked this conversation as resolved.
Show resolved Hide resolved
const agent = this._agent
// Note this error as an "inflight" event. See Agent#flush().
const inflightEvents = agent._inflightEvents
Expand Down
8 changes: 8 additions & 0 deletions lib/instrumentation/span.js
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,14 @@ Span.prototype.tryToCompress = function (spanToCompress) {
return this._compression.tryToCompress(this, spanToCompress)
}

Span.prototype.isRecorded = function () {
return this._context.isRecorded()
}

Span.prototype.setRecorded = function (value) {
return this._context.setRecorded(value)
}

function filterCallSite (callsite) {
var filename = callsite.getFileName()
return filename ? filename.indexOf('/node_modules/elastic-apm-node/') === -1 : true
Expand Down
16 changes: 9 additions & 7 deletions lib/instrumentation/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,6 @@ Transaction.prototype.createSpan = function (...args) {
if (!this.sampled) {
return null
}
if (this._builtSpans >= this._agent._conf.transactionMaxSpans) {
this._droppedSpans++
return null
}

// Exit spans must not have child spans (unless of the same type and subtype).
// https://github.com/elastic/apm/blob/master/specs/agents/tracing-spans.md#child-spans-of-exit-spans
Expand All @@ -153,8 +149,15 @@ Transaction.prototype.createSpan = function (...args) {
return null
}

const span = new Span(this, ...args, opts)

if (this._builtSpans >= this._agent._conf.transactionMaxSpans) {
Copy link
Contributor Author

@astorm astorm May 15, 2022

Choose a reason for hiding this comment

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

Note: The meaning of _buildSpans ends up changing slightly in this new world. The _buildSpans property is always incremented regardless of whether a span will be dropped or not. To get the number of started spans we subtract the number of dropped spans from built spans (see below)

this._droppedSpans++
span.setRecorded(false)
}

this._builtSpans++
return new Span(this, ...args, opts)
return span
}

// Note that this only returns a complete result when called *during* the call
Expand All @@ -172,7 +175,7 @@ Transaction.prototype.toJSON = function () {
sampled: this.sampled,
context: undefined,
span_count: {
started: this._builtSpans
started: this._builtSpans - this._droppedSpans
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The _builtSpans property now tracks all span objects that were created. To se the number of spans started we subtract the number dropped. (see above)

},
outcome: this.outcome,
faas: this._faas
Expand All @@ -191,7 +194,6 @@ Transaction.prototype.toJSON = function () {
cloud: this._cloud || {},
message: this._message || {}
}

// Only include dropped count when spans have been dropped.
if (this._droppedSpans > 0) {
payload.span_count.dropped = this._droppedSpans
Expand Down
6 changes: 3 additions & 3 deletions lib/opentelemetry-bridge/OTelTracer.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ class OTelTracer {
}
newTransOrSpan = trans.createSpan(name, spanOpts)

// There might be no span, e.g. if transactionMaxSpans was hit. We have
// to return some OTelSpan, and we also want to propagate the parent's
// trace-context, if any.
// There might be no span, e.g. if the span is a child of an exit span. We
// have to return some OTelSpan, and we also want to propagate the
// parent's trace-context, if any.
if (!newTransOrSpan) {
return otel.trace.wrapSpanContext(otelSpanContextFromTraceContext(parentGenericSpan._context))
}
Expand Down
4 changes: 4 additions & 0 deletions lib/tracecontext/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ class TraceContext {
setRecorded () {
return this.traceparent.setRecorded()
}

isRecorded () {
return this.traceparent.recorded
}
}

TraceContext.FLAGS = TraceParent.FLAGS
Expand Down
2 changes: 1 addition & 1 deletion test/instrumentation/transaction.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ test('#_encode() - dropped spans', function (t) {
var span0 = trans.startSpan('s0', 'type0')
trans.startSpan('s1', 'type1')
var span2 = trans.startSpan()
if (span2) {
if (span2.isRecorded()) {
t.fail('should have dropped the span')
}
span0.end()
Expand Down
24 changes: 6 additions & 18 deletions test/opentelemetry-bridge/fixtures.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,15 +162,11 @@ const cases = [
{
trentm marked this conversation as resolved.
Show resolved Hide resolved
// Expected trace:
// trace $traceId
// `- transaction $myTransId "myTrans"
// `- span "s0"
// `- transaction $myTransId "myTrans"
// `- span "s0"
// `- span "GET localhost:$port" (http)
// `- transaction "GET unknown route"
// `- span "s1" // This is the 3rd (max) span.
// `- transaction "GET unknown route"
// `- transaction "GET unknown route"
// `- transaction "GET unknown route"
// `- transaction "GET unknown route"
// `- transaction "GET unknown route"
// `- span "s1" // This is the 3rd (max) span.
script: 'hit-transaction-max-spans.js',
testOpts: {
// - This fixture fails with node.js [10.0, 10.4) due to an async context
Expand All @@ -184,7 +180,7 @@ const cases = [
ELASTIC_APM_TRANSACTION_MAX_SPANS: '3'
},
check: (t, events) => {
t.equal(events.length, 10, 'exactly 10 events')
t.equal(events.length, 6, 'exactly 6 events')
t.ok(events[0].metadata, 'APM server got event metadata object')

function assertIncomingHttpTrans (trans, parentId) {
Expand All @@ -207,7 +203,7 @@ const cases = [
// `- transaction $myTransId "myTrans"
const myTrans = tas[0].transaction
t.equal(myTrans.name, 'myTrans', 'myTrans.name')
t.deepEqual(myTrans.span_count, { started: 3, dropped: 4 }, 'myTrans.span_count')
t.deepEqual(myTrans.span_count, { started: 3, dropped: 7 }, 'myTrans.span_count')
// `- span "s0"
const s0 = tas[1].span
t.equal(s0.name, 's0', 's0')
Expand All @@ -221,14 +217,6 @@ const cases = [
const s1 = tas[4].span
t.equal(s1.name, 's1', 's1')
t.equal(s1.parent_id, myTrans.id, 's1.parent_id')
// `- transaction "GET unknown route"
assertIncomingHttpTrans(tas[5].transaction, tas[4].span.id)
// `- transaction "GET unknown route"
// `- transaction "GET unknown route"
// `- transaction "GET unknown route"
for (let i = 6; i < 9; i++) {
assertIncomingHttpTrans(tas[i].transaction, myTrans.id)
}
}
},

Expand Down