1- import { type RawAttributes , isAttributeObject } from '../attributes' ;
21import type { Client } from '../client' ;
32import { getClient , getGlobalScope } from '../currentScopes' ;
43import { DEBUG_BUILD } from '../debug-build' ;
@@ -16,10 +15,12 @@ import {
1615 SEMANTIC_ATTRIBUTE_USER_USERNAME ,
1716} from '../semanticAttributes' ;
1817import { getCapturedScopesOnSpan } from '../tracing/utils' ;
18+ import type { SerializedAttributes } from '../types-hoist/attributes' ;
1919import type { Span , SpanV2JSON } from '../types-hoist/span' ;
2020import { mergeScopeData } from '../utils/applyScopeDataToEvent' ;
2121import { debug } from '../utils/debug-logger' ;
2222import { INTERNAL_getSegmentSpan , spanToV2JSON } from '../utils/spanUtils' ;
23+ import { safeSetSpanAttributes } from './spanFirstUtils' ;
2324
2425/**
2526 * Captures a span and returns it to the caller, to be enqueued for sending.
@@ -36,14 +37,19 @@ export function captureSpan(span: Span, client = getClient()): void {
3637 const { isolationScope : spanIsolationScope , scope : spanScope } = getCapturedScopesOnSpan ( span ) ;
3738 const finalScopeData = getFinalScopeData ( spanIsolationScope , spanScope ) ;
3839
39- const originalAttributeKeys = Object . keys ( serializedSegmentSpan . attributes ?? { } ) ;
40+ const originalAttributes = serializedSegmentSpan . attributes ?? { } ;
4041
41- applyCommonSpanAttributes ( span , serializedSegmentSpan , client , finalScopeData , originalAttributeKeys ) ;
42+ applyCommonSpanAttributes ( span , serializedSegmentSpan , client , finalScopeData , originalAttributes ) ;
4243
4344 if ( span === segmentSpan ) {
44- applyScopeToSegmentSpan ( span , finalScopeData , originalAttributeKeys ) ;
45+ applyScopeToSegmentSpan ( span , finalScopeData , originalAttributes ) ;
4546 }
4647
48+ // Allow integrations to add additional data to span. Pass in a serialized
49+ // span to avoid having to potentially serialize the span in every integration
50+ // (for improved performance).
51+ client . emit ( 'processSpan' , span , { readOnlySpan : spanToV2JSON ( span ) } ) ;
52+
4753 // Wondering where we apply the beforeSendSpan callback?
4854 // We apply it directly before sending the span,
4955 // so whenever the buffer this span gets enqueued in is being flushed.
@@ -55,11 +61,15 @@ export function captureSpan(span: Span, client = getClient()): void {
5561 client . emit ( 'enqueueSpan' , span ) ;
5662}
5763
58- function applyScopeToSegmentSpan ( segmentSpan : Span , scopeData : ScopeData , originalAttributeKeys : string [ ] ) : void {
64+ function applyScopeToSegmentSpan (
65+ segmentSpan : Span ,
66+ scopeData : ScopeData ,
67+ originalAttributes : SerializedAttributes ,
68+ ) : void {
5969 // TODO: Apply all scope data from auto instrumentation (contexts, request) to segment span
6070 const { attributes } = scopeData ;
6171 if ( attributes ) {
62- setAttributesIfNotPresent ( segmentSpan , originalAttributeKeys , attributes ) ;
72+ safeSetSpanAttributes ( segmentSpan , attributes , originalAttributes ) ;
6373 }
6474}
6575
@@ -68,28 +78,32 @@ function applyCommonSpanAttributes(
6878 serializedSegmentSpan : SpanV2JSON ,
6979 client : Client ,
7080 scopeData : ScopeData ,
71- originalAttributeKeys : string [ ] ,
81+ originalAttributes : SerializedAttributes ,
7282) : void {
7383 const sdk = client . getSdkMetadata ( ) ;
7484 const { release, environment, sendDefaultPii } = client . getOptions ( ) ;
7585
7686 // avoid overwriting any previously set attributes (from users or potentially our SDK instrumentation)
77- setAttributesIfNotPresent ( span , originalAttributeKeys , {
78- [ SEMANTIC_ATTRIBUTE_SENTRY_RELEASE ] : release ,
79- [ SEMANTIC_ATTRIBUTE_SENTRY_ENVIRONMENT ] : environment ,
80- [ SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_NAME ] : serializedSegmentSpan . name ,
81- [ SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID ] : serializedSegmentSpan . span_id ,
82- [ SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME ] : sdk ?. sdk ?. name ,
83- [ SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION ] : sdk ?. sdk ?. version ,
84- ...( sendDefaultPii
85- ? {
86- [ SEMANTIC_ATTRIBUTE_USER_ID ] : scopeData . user ?. id ,
87- [ SEMANTIC_ATTRIBUTE_USER_EMAIL ] : scopeData . user ?. email ,
88- [ SEMANTIC_ATTRIBUTE_USER_IP_ADDRESS ] : scopeData . user ?. ip_address ?? undefined ,
89- [ SEMANTIC_ATTRIBUTE_USER_USERNAME ] : scopeData . user ?. username ,
90- }
91- : { } ) ,
92- } ) ;
87+ safeSetSpanAttributes (
88+ span ,
89+ {
90+ [ SEMANTIC_ATTRIBUTE_SENTRY_RELEASE ] : release ,
91+ [ SEMANTIC_ATTRIBUTE_SENTRY_ENVIRONMENT ] : environment ,
92+ [ SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_NAME ] : serializedSegmentSpan . name ,
93+ [ SEMANTIC_ATTRIBUTE_SENTRY_SEGMENT_ID ] : serializedSegmentSpan . span_id ,
94+ [ SEMANTIC_ATTRIBUTE_SENTRY_SDK_NAME ] : sdk ?. sdk ?. name ,
95+ [ SEMANTIC_ATTRIBUTE_SENTRY_SDK_VERSION ] : sdk ?. sdk ?. version ,
96+ ...( sendDefaultPii
97+ ? {
98+ [ SEMANTIC_ATTRIBUTE_USER_ID ] : scopeData . user ?. id ,
99+ [ SEMANTIC_ATTRIBUTE_USER_EMAIL ] : scopeData . user ?. email ,
100+ [ SEMANTIC_ATTRIBUTE_USER_IP_ADDRESS ] : scopeData . user ?. ip_address ?? undefined ,
101+ [ SEMANTIC_ATTRIBUTE_USER_USERNAME ] : scopeData . user ?. username ,
102+ }
103+ : { } ) ,
104+ } ,
105+ originalAttributes ,
106+ ) ;
93107}
94108
95109// TODO: Extract this to a helper in core. It's used in multiple places.
@@ -103,35 +117,3 @@ function getFinalScopeData(isolationScope: Scope | undefined, scope: Scope | und
103117 }
104118 return finalScopeData ;
105119}
106-
107- function setAttributesIfNotPresent (
108- span : Span ,
109- originalAttributeKeys : string [ ] ,
110- newAttributes : RawAttributes < Record < string , unknown > > ,
111- ) : void {
112- Object . keys ( newAttributes ) . forEach ( key => {
113- if ( ! originalAttributeKeys . includes ( key ) ) {
114- setAttributeOnSpanWithMaybeUnit ( span , key , newAttributes [ key ] ) ;
115- }
116- } ) ;
117- }
118-
119- function setAttributeOnSpanWithMaybeUnit ( span : Span , attributeKey : string , attributeValue : unknown ) : void {
120- if ( isAttributeObject ( attributeValue ) ) {
121- const { value, unit } = attributeValue ;
122-
123- if ( isSupportedAttributeType ( value ) ) {
124- span . setAttribute ( attributeKey , value ) ;
125- }
126-
127- if ( unit ) {
128- span . setAttribute ( `${ attributeKey } .unit` , unit ) ;
129- }
130- } else if ( isSupportedAttributeType ( attributeValue ) ) {
131- span . setAttribute ( attributeKey , attributeValue ) ;
132- }
133- }
134-
135- function isSupportedAttributeType ( value : unknown ) : value is Parameters < Span [ 'setAttribute' ] > [ 1 ] {
136- return [ 'string' , 'number' , 'boolean' ] . includes ( typeof value ) || Array . isArray ( value ) ;
137- }
0 commit comments