@@ -41,7 +41,7 @@ export function inferSpanData(spanName: string, attributes: SpanAttributes, kind
4141 // eslint-disable-next-line deprecation/deprecation
4242 const httpMethod = attributes [ ATTR_HTTP_REQUEST_METHOD ] || attributes [ SEMATTRS_HTTP_METHOD ] ;
4343 if ( httpMethod ) {
44- return descriptionForHttpMethod ( { attributes, name : getOriginalName ( spanName , attributes ) , kind } , httpMethod ) ;
44+ return descriptionForHttpMethod ( { attributes, name : spanName , kind } , httpMethod ) ;
4545 }
4646
4747 // eslint-disable-next-line deprecation/deprecation
@@ -63,9 +63,8 @@ export function inferSpanData(spanName: string, attributes: SpanAttributes, kind
6363 const rpcService = attributes [ SEMATTRS_RPC_SERVICE ] ;
6464 if ( rpcService ) {
6565 return {
66+ ...getUserUpdatedNameAndSource ( spanName , attributes , 'route' ) ,
6667 op : 'rpc' ,
67- description : getOriginalName ( spanName , attributes ) ,
68- source : customSourceOrRoute ,
6968 } ;
7069 }
7170
@@ -74,9 +73,8 @@ export function inferSpanData(spanName: string, attributes: SpanAttributes, kind
7473 const messagingSystem = attributes [ SEMATTRS_MESSAGING_SYSTEM ] ;
7574 if ( messagingSystem ) {
7675 return {
76+ ...getUserUpdatedNameAndSource ( spanName , attributes , customSourceOrRoute ) ,
7777 op : 'message' ,
78- description : getOriginalName ( spanName , attributes ) ,
79- source : customSourceOrRoute ,
8078 } ;
8179 }
8280
@@ -85,9 +83,8 @@ export function inferSpanData(spanName: string, attributes: SpanAttributes, kind
8583 const faasTrigger = attributes [ SEMATTRS_FAAS_TRIGGER ] ;
8684 if ( faasTrigger ) {
8785 return {
86+ ...getUserUpdatedNameAndSource ( spanName , attributes , customSourceOrRoute ) ,
8887 op : faasTrigger . toString ( ) ,
89- description : getOriginalName ( spanName , attributes ) ,
90- source : customSourceOrRoute ,
9188 } ;
9289 }
9390
@@ -107,14 +104,27 @@ export function parseSpanDescription(span: AbstractSpan): SpanDescription {
107104 const attributes = spanHasAttributes ( span ) ? span . attributes : { } ;
108105 const name = spanHasName ( span ) ? span . name : '<unknown>' ;
109106 const kind = getSpanKind ( span ) ;
107+ // console.log('x parseSpanDesc', { attributes, name, kind });
110108
111- return inferSpanData ( name , attributes , kind ) ;
109+ const res = inferSpanData ( name , attributes , kind ) ;
110+
111+ // console.log('x parseSpanDesc res', res);
112+ return res ;
112113}
113114
114115function descriptionForDbSystem ( { attributes, name } : { attributes : Attributes ; name : string } ) : SpanDescription {
115- // if we already set the source to custom, we don't overwrite the span description but just adjust the op
116+ // if we already have a custom name, we don't overwrite it but only set the op
117+ if ( typeof attributes [ '_sentry_span_name_set_by_user' ] === 'string' ) {
118+ return {
119+ op : 'db' ,
120+ description : attributes [ '_sentry_span_name_set_by_user' ] ,
121+ source : ( attributes [ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ] as TransactionSource ) || 'custom' ,
122+ } ;
123+ }
124+
125+ // if we already have the source set to custom, we don't overwrite the span description but only set the op
116126 if ( attributes [ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ] === 'custom' ) {
117- return { op : 'db' , description : getOriginalName ( name , attributes ) , source : 'custom' } ;
127+ return { op : 'db' , description : name , source : 'custom' } ;
118128 }
119129
120130 // Use DB statement (Ex "SELECT * FROM table") if possible as description.
@@ -150,7 +160,7 @@ export function descriptionForHttpMethod(
150160 const { urlPath, url, query, fragment, hasRoute } = getSanitizedUrl ( attributes , kind ) ;
151161
152162 if ( ! urlPath ) {
153- return { op : opParts . join ( '.' ) , description : getOriginalName ( name , attributes ) , source : 'custom' } ;
163+ return { ... getUserUpdatedNameAndSource ( name , attributes ) , op : opParts . join ( '.' ) } ;
154164 }
155165
156166 const graphqlOperationsAttribute = attributes [ SEMANTIC_ATTRIBUTE_SENTRY_GRAPHQL_OPERATION ] ;
@@ -160,12 +170,12 @@ export function descriptionForHttpMethod(
160170
161171 // When the http span has a graphql operation, append it to the description
162172 // We add these in the graphqlIntegration
163- const description = graphqlOperationsAttribute
173+ const inferredDescription = graphqlOperationsAttribute
164174 ? `${ baseDescription } (${ getGraphqlOperationNamesFromAttribute ( graphqlOperationsAttribute ) } )`
165175 : baseDescription ;
166176
167177 // If `httpPath` is a root path, then we can categorize the transaction source as route.
168- const source : TransactionSource = hasRoute || urlPath === '/' ? 'route' : 'url' ;
178+ const inferredSource : TransactionSource = hasRoute || urlPath === '/' ? 'route' : 'url' ;
169179
170180 const data : Record < string , string > = { } ;
171181
@@ -189,15 +199,22 @@ export function descriptionForHttpMethod(
189199 const origin = attributes [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] || 'manual' ;
190200 const isManualSpan = ! `${ origin } ` . startsWith ( 'auto' ) ;
191201
192- // If users (or in very rare occasions we) set the source to custom, we don't overwrite it
202+ // If users (or in very rare occasions we) set the source to custom, we don't overwrite the name
193203 const alreadyHasCustomSource = attributes [ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ] === 'custom' ;
194204
195- const useInferredDescription = ! alreadyHasCustomSource && ( isClientOrServerKind || ! isManualSpan ) ;
205+ const useInferredDescription =
206+ ! alreadyHasCustomSource &&
207+ attributes [ '_sentry_span_name_set_by_user' ] == null &&
208+ ( isClientOrServerKind || ! isManualSpan ) ;
209+
210+ const { description, source } = useInferredDescription
211+ ? { description : inferredDescription , source : inferredSource }
212+ : getUserUpdatedNameAndSource ( name , attributes ) ;
196213
197214 return {
198215 op : opParts . join ( '.' ) ,
199- description : useInferredDescription ? description : getOriginalName ( name , attributes ) ,
200- source : useInferredDescription ? source : 'custom' ,
216+ description,
217+ source,
201218 data,
202219 } ;
203220}
@@ -264,15 +281,32 @@ export function getSanitizedUrl(
264281}
265282
266283/**
267- * Because Otel decided to mutate span names via `span.updateName`, the only way to ensure
268- * that a user-set span name is preserved is to store it as a tmp attribute on the span.
284+ * Because Otel instrumentation sometimes mutates span names via `span.updateName`, the only way
285+ * to ensure that a user-set span name is preserved is to store it as a tmp attribute on the span.
269286 * We delete this attribute once we're done with it when preparing the event envelope.
287+ *
288+ * This temp attribute always takes precedence over the original name.
289+ *
290+ * We also need to take care of setting the correct source. Users can always update the source
291+ * after updating the name, so we need to respect that.
292+ *
270293 * @internal exported only for testing
271294 */
272- export function getOriginalName ( name : string , attributes : Attributes ) : string {
273- return attributes [ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ] === 'custom' &&
274- attributes [ '_sentry_span_name_set_by_user' ] &&
275- typeof attributes [ '_sentry_span_name_set_by_user' ] === 'string'
276- ? attributes [ '_sentry_span_name_set_by_user' ]
277- : name ;
295+ export function getUserUpdatedNameAndSource (
296+ originalName : string ,
297+ attributes : Attributes ,
298+ fallbackSource : TransactionSource = 'custom' ,
299+ ) : {
300+ description : string ;
301+ source : TransactionSource ;
302+ } {
303+ const source = ( attributes [ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ] as TransactionSource ) || fallbackSource ;
304+
305+ if ( attributes [ '_sentry_span_name_set_by_user' ] && typeof attributes [ '_sentry_span_name_set_by_user' ] === 'string' )
306+ return {
307+ description : attributes [ '_sentry_span_name_set_by_user' ] ,
308+ source,
309+ } ;
310+
311+ return { description : originalName , source } ;
278312}
0 commit comments