1- import type { Client , IntegrationFn , Span , SpanV2JSON } from '@sentry/core' ;
1+ import type { Client , IntegrationFn , Span , SpanAttributes , SpanAttributeValue , SpanV2JSON } from '@sentry/core' ;
22import {
33 createSpanV2Envelope ,
44 debug ,
55 defineIntegration ,
6+ getCapturedScopesOnSpan ,
67 getDynamicSamplingContextFromSpan ,
8+ getGlobalScope ,
79 getRootSpan as getSegmentSpan ,
810 isV2BeforeSendSpanCallback ,
11+ mergeScopeData ,
912 reparentChildSpans ,
13+ SEMANTIC_ATTRIBUTE_SENTRY_ENVIRONMENT ,
14+ SEMANTIC_ATTRIBUTE_SENTRY_RELEASE ,
15+ SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME ,
16+ SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION ,
17+ SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_NAME ,
18+ SEMANTIC_ATTRIBUTE_USER_EMAIL ,
19+ SEMANTIC_ATTRIBUTE_USER_ID ,
20+ SEMANTIC_ATTRIBUTE_USER_IP_ADDRESS ,
21+ SEMANTIC_ATTRIBUTE_USER_USERNAME ,
1022 shouldIgnoreSpan ,
1123 showSpanDropWarning ,
1224 spanToV2JSON ,
@@ -91,6 +103,9 @@ interface SpanProcessingOptions {
91103 beforeSendSpan : ( ( span : SpanV2JSON ) => SpanV2JSON ) | undefined ;
92104}
93105
106+ /**
107+ * Just the traceid alone isn't enough because there can be multiple span trees with the same traceid.
108+ */
94109function getSpanTreeMapKey ( span : Span ) : string {
95110 return `${ span . spanContext ( ) . traceId } -${ getSegmentSpan ( span ) . spanContext ( ) . spanId } ` ;
96111}
@@ -107,13 +122,17 @@ function processAndSendSpans(
107122 spanTreeMap . delete ( spanTreeMapKey ) ;
108123 return ;
109124 }
125+ const segmentSpanJson = spanToV2JSON ( segmentSpan ) ;
110126
111- const { ignoreSpans } = client . getOptions ( ) ;
127+ for ( const span of spansOfTrace ) {
128+ applyCommonSpanAttributes ( span , segmentSpanJson , client ) ;
129+ }
112130
113- // TODO: Apply scopes to spans
131+ // TODO: Apply scope data and contexts to segment span
132+
133+ const { ignoreSpans } = client . getOptions ( ) ;
114134
115135 // 1. Check if the entire span tree is ignored by ignoreSpans
116- const segmentSpanJson = spanToV2JSON ( segmentSpan ) ;
117136 if ( ignoreSpans ?. length && shouldIgnoreSpan ( segmentSpanJson , ignoreSpans ) ) {
118137 client . recordDroppedEvent ( 'before_send' , 'span' , spansOfTrace . size ) ;
119138 spanTreeMap . delete ( spanTreeMapKey ) ;
@@ -166,6 +185,41 @@ function processAndSendSpans(
166185 spanTreeMap . delete ( spanTreeMapKey ) ;
167186}
168187
188+ function applyCommonSpanAttributes ( span : Span , serializedSegmentSpan : SpanV2JSON , client : Client ) : void {
189+ const sdk = client . getSdkMetadata ( ) ;
190+ const { release, environment, sendDefaultPii } = client . getOptions ( ) ;
191+
192+ const { isolationScope : spanIsolationScope , scope : spanScope } = getCapturedScopesOnSpan ( span ) ;
193+
194+ const originalAttributeKeys = Object . keys ( spanToV2JSON ( span ) . attributes ?? { } ) ;
195+
196+ // TODO: Extract this scope data merge to a helper in core. It's used in multiple places.
197+ const finalScopeData = getGlobalScope ( ) . getScopeData ( ) ;
198+ if ( spanIsolationScope ) {
199+ mergeScopeData ( finalScopeData , spanIsolationScope . getScopeData ( ) ) ;
200+ }
201+ if ( spanScope ) {
202+ mergeScopeData ( finalScopeData , spanScope . getScopeData ( ) ) ;
203+ }
204+
205+ // avoid overwriting any previously set attributes (from users or potentially our SDK instrumentation)
206+ setAttributesIfNotPresent ( span , originalAttributeKeys , {
207+ [ SEMANTIC_ATTRIBUTE_SENTRY_RELEASE ] : release ,
208+ [ SEMANTIC_ATTRIBUTE_SENTRY_ENVIRONMENT ] : environment ,
209+ [ SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_NAME ] : serializedSegmentSpan . name ,
210+ [ SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME ] : sdk ?. sdk ?. name ,
211+ [ SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION ] : sdk ?. sdk ?. version ,
212+ ...( sendDefaultPii
213+ ? {
214+ [ SEMANTIC_ATTRIBUTE_USER_ID ] : finalScopeData . user ?. id ,
215+ [ SEMANTIC_ATTRIBUTE_USER_EMAIL ] : finalScopeData . user ?. email ,
216+ [ SEMANTIC_ATTRIBUTE_USER_IP_ADDRESS ] : finalScopeData . user ?. ip_address ?? undefined ,
217+ [ SEMANTIC_ATTRIBUTE_USER_USERNAME ] : finalScopeData . user ?. username ,
218+ }
219+ : { } ) ,
220+ } ) ;
221+ }
222+
169223function applyBeforeSendSpanCallback ( span : SpanV2JSON , beforeSendSpan : ( span : SpanV2JSON ) => SpanV2JSON ) : SpanV2JSON {
170224 const modifedSpan = beforeSendSpan ( span ) ;
171225 if ( ! modifedSpan ) {
@@ -174,3 +228,11 @@ function applyBeforeSendSpanCallback(span: SpanV2JSON, beforeSendSpan: (span: Sp
174228 }
175229 return modifedSpan ;
176230}
231+
232+ function setAttributesIfNotPresent ( span : Span , originalAttributeKeys : string [ ] , newAttributes : SpanAttributes ) : void {
233+ Object . keys ( newAttributes ) . forEach ( key => {
234+ if ( ! originalAttributeKeys . includes ( key ) ) {
235+ span . setAttribute ( key , newAttributes [ key ] ) ;
236+ }
237+ } ) ;
238+ }
0 commit comments