diff --git a/packages/opentelemetry/src/spanExporter.ts b/packages/opentelemetry/src/spanExporter.ts index 6430f0f23da5..3328b64c8230 100644 --- a/packages/opentelemetry/src/spanExporter.ts +++ b/packages/opentelemetry/src/spanExporter.ts @@ -191,6 +191,14 @@ export class SentrySpanExporter { sentSpans.add(span); const transactionEvent = createTransactionForOtelSpan(span); + // Add an attribute to the transaction event to indicate that this transaction is an orphaned transaction + if (root.parentNode && this._sentSpans.has(root.parentNode.id)) { + const traceData = transactionEvent.contexts?.trace?.data; + if (traceData) { + traceData['sentry.parent_span_already_sent'] = true; + } + } + // We'll recursively add all the child spans to this array const spans = transactionEvent.spans || []; diff --git a/packages/opentelemetry/test/integration/transactions.test.ts b/packages/opentelemetry/test/integration/transactions.test.ts index 9bc1847b422b..c3cc9b0e8b7b 100644 --- a/packages/opentelemetry/test/integration/transactions.test.ts +++ b/packages/opentelemetry/test/integration/transactions.test.ts @@ -612,6 +612,20 @@ describe('Integration | Transactions', () => { expect(transactions).toHaveLength(2); expect(transactions[0]?.spans).toHaveLength(1); + expect(transactions[0]?.transaction).toBe('test name'); + expect(transactions[0]?.contexts?.trace?.data).toEqual({ + 'sentry.origin': 'manual', + 'sentry.sample_rate': 1, + 'sentry.source': 'custom', + }); + + expect(transactions[1]?.transaction).toBe('inner span 2'); + expect(transactions[1]?.contexts?.trace?.data).toEqual({ + 'sentry.parent_span_already_sent': true, + 'sentry.origin': 'manual', + 'sentry.source': 'custom', + }); + const finishedSpans: any = exporter['_finishedSpanBuckets'].flatMap(bucket => bucket ? Array.from(bucket.spans) : [], );