@@ -637,7 +637,7 @@ export function _addResourceSpans(
637
637
startTime : number ,
638
638
duration : number ,
639
639
timeOrigin : number ,
640
- ignoreResourceSpans ?: Array < string > ,
640
+ ignoredResourceSpanOps ?: Array < string > ,
641
641
) : void {
642
642
// we already instrument based on fetch and xhr, so we don't need to
643
643
// duplicate spans here.
@@ -646,31 +646,15 @@ export function _addResourceSpans(
646
646
}
647
647
648
648
const op = entry . initiatorType ? `resource.${ entry . initiatorType } ` : 'resource.other' ;
649
- if ( ignoreResourceSpans ?. includes ( op ) ) {
649
+ if ( ignoredResourceSpanOps ?. includes ( op ) ) {
650
650
return ;
651
651
}
652
652
653
- const parsedUrl = parseUrl ( resourceUrl ) ;
654
-
655
653
const attributes : SpanAttributes = {
656
654
[ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : 'auto.resource.browser.metrics' ,
657
655
} ;
658
- setResourceEntrySizeData ( attributes , entry , 'transferSize' , 'http.response_transfer_size' ) ;
659
- setResourceEntrySizeData ( attributes , entry , 'encodedBodySize' , 'http.response_content_length' ) ;
660
- setResourceEntrySizeData ( attributes , entry , 'decodedBodySize' , 'http.decoded_response_content_length' ) ;
661
-
662
- // `deliveryType` is experimental and does not exist everywhere
663
- const deliveryType = ( entry as { deliveryType ?: 'cache' | 'navigational-prefetch' | '' } ) . deliveryType ;
664
- if ( deliveryType != null ) {
665
- attributes [ 'http.response_delivery_type' ] = deliveryType ;
666
- }
667
656
668
- // Types do not reflect this property yet
669
- const renderBlockingStatus = ( entry as { renderBlockingStatus ?: 'render-blocking' | 'non-render-blocking' } )
670
- . renderBlockingStatus ;
671
- if ( renderBlockingStatus ) {
672
- attributes [ 'resource.render_blocking_status' ] = renderBlockingStatus ;
673
- }
657
+ const parsedUrl = parseUrl ( resourceUrl ) ;
674
658
675
659
if ( parsedUrl . protocol ) {
676
660
attributes [ 'url.scheme' ] = parsedUrl . protocol . split ( ':' ) . pop ( ) ; // the protocol returned by parseUrl includes a :, but OTEL spec does not, so we remove it.
@@ -690,6 +674,49 @@ export function _addResourceSpans(
690
674
attributes [ 'network.protocol.version' ] = version ;
691
675
}
692
676
677
+ setResourceRequestAttributes ( entry , attributes , [
678
+ // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStatus
679
+ [ 'responseStatus' , 'http.request.response_status' ] ,
680
+
681
+ // Timing attributes (request/response lifecycle)
682
+ // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming#timestamps
683
+ [ 'redirectStart' , 'http.request.redirect_start' ] ,
684
+ [ 'redirectEnd' , 'http.request.redirect_end' ] ,
685
+
686
+ [ 'workerStart' , 'http.request.worker_start' ] ,
687
+
688
+ [ 'fetchStart' , 'http.request.fetch_start' ] ,
689
+
690
+ [ 'domainLookupStart' , 'http.request.domain_lookup_start' ] ,
691
+ [ 'domainLookupEnd' , 'http.request.domain_lookup_end' ] ,
692
+
693
+ [ 'connectStart' , 'http.request.connect_start' ] ,
694
+ [ 'secureConnectionStart' , 'http.request.secure_connection_start' ] ,
695
+ [ 'connectEnd' , 'http.request.connect_end' ] ,
696
+
697
+ [ 'requestStart' , 'http.request.request_start' ] ,
698
+ [ 'firstInterimResponseStart' , 'http.response.first_interim_response_start' ] ,
699
+ [ 'finalResponseHeadersStart' , 'http.response.final_response_headers_start' ] ,
700
+
701
+ // responseStart can also be interpreted as TTFB for resource requests: https://web.dev/articles/ttfb
702
+ [ 'responseStart' , 'http.response.response_start' ] ,
703
+ [ 'responseEnd' , 'http.response.response_end' ] ,
704
+
705
+ // Size attributes:
706
+ // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/transferSize
707
+ // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/encodedBodySize
708
+ // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/decodedBodySize
709
+ [ 'transferSize' , 'http.response_transfer_size' ] ,
710
+ [ 'encodedBodySize' , 'http.response_content_length' ] ,
711
+ [ 'decodedBodySize' , 'http.decoded_response_content_length' ] ,
712
+
713
+ // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/renderBlockingStatus
714
+ [ 'renderBlockingStatus' , 'resource.render_blocking_status' ] ,
715
+
716
+ // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/deliveryType
717
+ [ 'deliveryType' , 'http.response_delivery_type' ] ,
718
+ ] ) ;
719
+
693
720
const startTimestamp = timeOrigin + startTime ;
694
721
const endTimestamp = startTimestamp + duration ;
695
722
@@ -776,16 +803,34 @@ function _setWebVitalAttributes(span: Span, options: AddPerformanceEntriesOption
776
803
}
777
804
}
778
805
779
- function setResourceEntrySizeData (
806
+ type ExperimentalResourceTimingProperty =
807
+ | 'firstInterimResponseStart'
808
+ | 'finalResponseHeadersStart'
809
+ | 'renderBlockingStatus'
810
+ | 'deliveryType' ;
811
+
812
+ /**
813
+ * Use this to set any attributes we can take directly form the PerformanceResourceTiming entry.
814
+ *
815
+ * This is just a mapping function for entry->attribute to keep bundle-size minimal.
816
+ * Experimental properties are also accepted (see {@link ExperimentalResourceTimingProperty}).
817
+ * Assumes that all entry properties might be undefined for browser-specific differences.
818
+ * Only accepts string and number values for now and also sets 0-values.
819
+ */
820
+ function setResourceRequestAttributes (
821
+ entry : Partial < PerformanceResourceTiming > & Partial < Record < ExperimentalResourceTimingProperty , number > > ,
780
822
attributes : SpanAttributes ,
781
- entry : PerformanceResourceTiming ,
782
- key : keyof Pick < PerformanceResourceTiming , 'transferSize' | 'encodedBodySize' | 'decodedBodySize' > ,
783
- dataKey : 'http.response_transfer_size' | 'http.response_content_length' | 'http.decoded_response_content_length' ,
823
+ properties : [ keyof PerformanceResourceTiming | ExperimentalResourceTimingProperty , string ] [ ] ,
784
824
) : void {
785
- const entryVal = entry [ key ] ;
786
- if ( entryVal != null && entryVal < MAX_INT_AS_BYTES ) {
787
- attributes [ dataKey ] = entryVal ;
788
- }
825
+ properties . forEach ( ( [ entryKey , attributeKey ] ) => {
826
+ const entryVal = entry [ entryKey ] ;
827
+ if (
828
+ entryVal != null &&
829
+ ( ( typeof entryVal === 'number' && entryVal < MAX_INT_AS_BYTES ) || typeof entryVal === 'string' )
830
+ ) {
831
+ attributes [ attributeKey ] = entryVal ;
832
+ }
833
+ } ) ;
789
834
}
790
835
791
836
/**
0 commit comments