@@ -98,6 +98,7 @@ const DD_NAMESPACED_TO_APM_CONVENTIONS: &[(&str, &str)] = &[
9898pub fn otel_span_to_dd_span (
9999 otel_span : & OtlpSpan , otel_resource : & Resource , instrumentation_scope : Option < & OtlpInstrumentationScope > ,
100100 ignore_missing_fields : bool , compute_top_level_by_span_kind : bool , interner : & GenericMapInterner ,
101+ string_builder : & mut StringBuilder < GenericMapInterner > ,
101102) -> DdSpan {
102103 let span_attributes = & otel_span. attributes ;
103104 let resource_attributes = & otel_resource. attributes ;
@@ -108,6 +109,7 @@ pub fn otel_span_to_dd_span(
108109 ignore_missing_fields,
109110 compute_top_level_by_span_kind,
110111 interner,
112+ string_builder,
111113 ) ;
112114
113115 for ( dd_key, apm_key) in DD_NAMESPACED_TO_APM_CONVENTIONS {
@@ -117,7 +119,14 @@ pub fn otel_span_to_dd_span(
117119 }
118120
119121 for attribute in span_attributes {
120- map_attribute_generic ( attribute, & mut meta, & mut metrics, ignore_missing_fields, interner) ;
122+ map_attribute_generic (
123+ attribute,
124+ & mut meta,
125+ & mut metrics,
126+ ignore_missing_fields,
127+ interner,
128+ string_builder,
129+ ) ;
121130 }
122131
123132 if !otel_span. trace_id . is_empty ( ) {
@@ -214,6 +223,7 @@ pub fn otel_span_to_dd_span(
214223 & mut metrics,
215224 ignore_missing_fields,
216225 interner,
226+ string_builder,
217227 ) ;
218228 }
219229 }
@@ -239,6 +249,7 @@ pub fn otel_span_to_dd_span(
239249pub fn otel_to_dd_span_minimal (
240250 otel_span : & OtlpSpan , otel_resource : & Resource , _instrumentation_scope : Option < & OtlpInstrumentationScope > ,
241251 ignore_missing_fields : bool , compute_top_level_by_span_kind : bool , interner : & GenericMapInterner ,
252+ string_builder : & mut StringBuilder < GenericMapInterner > ,
242253) -> (
243254 DdSpan ,
244255 FastHashMap < MetaString , MetaString > ,
@@ -323,10 +334,22 @@ pub fn otel_to_dd_span_minimal(
323334 service = get_otel_service ( span_attributes, resource_attributes, true , interner) ;
324335 }
325336 if name. is_empty ( ) {
326- name = get_otel_operation_name_v2 ( otel_span, span_attributes, resource_attributes, interner) ;
337+ name = get_otel_operation_name_v2 (
338+ otel_span,
339+ span_attributes,
340+ resource_attributes,
341+ interner,
342+ string_builder,
343+ ) ;
327344 }
328345 if resource. is_empty ( ) {
329- resource = get_otel_resource_v2_truncated ( otel_span, span_attributes, resource_attributes, interner) ;
346+ resource = get_otel_resource_v2_truncated (
347+ otel_span,
348+ span_attributes,
349+ resource_attributes,
350+ interner,
351+ string_builder,
352+ ) ;
330353 // Agent normalizer sets resource = name when resource is empty
331354 // https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/agent/normalizer.go#L245-248
332355 if resource. is_empty ( ) {
@@ -381,7 +404,8 @@ fn get_otel_service(
381404// GetOTelOperationNameV2 returns the DD operation name based on OTel span and resource attributes and given configs.
382405// based on code from https://github.com/DataDog/datadog-agent/blob/instrument-otlp-traffic/pkg/trace/traceutil/otel_util.go#L424
383406fn get_otel_operation_name_v2 (
384- otel_span : & OtlpSpan , span_attributes : & [ KeyValue ] , resource_attributes : & [ KeyValue ] , interner : & GenericMapInterner ,
407+ otel_span : & OtlpSpan , span_attributes : & [ KeyValue ] , resource_attributes : & [ KeyValue ] ,
408+ interner : & GenericMapInterner , string_builder : & mut StringBuilder < GenericMapInterner > ,
385409) -> MetaString {
386410 if let Some ( value) = use_both_maps ( span_attributes, resource_attributes, true , OPERATION_NAME_KEY , interner) {
387411 if !value. is_empty ( ) {
@@ -392,7 +416,6 @@ fn get_otel_operation_name_v2(
392416 let span_kind = SpanKind :: try_from ( otel_span. kind ) . unwrap_or ( SpanKind :: Unspecified ) ;
393417 let is_client = matches ! ( span_kind, SpanKind :: Client ) ;
394418 let is_server = matches ! ( span_kind, SpanKind :: Server ) ;
395- let mut string_builder = StringBuilder :: new ( ) ;
396419
397420 // http
398421 for http_request_method_key in HTTP_REQUEST_METHOD_KEYS {
@@ -573,10 +596,10 @@ fn get_otel_operation_name_v2(
573596// GetOTelResourceV2 returns the DD resource name based on OTel span and resource attributes.
574597// based on this code https://github.com/DataDog/datadog-agent/blob/instrument-otlp-traffic/pkg/trace/traceutil/otel_util.go#L348
575598fn get_otel_resource_v2 (
576- otel_span : & OtlpSpan , span_attributes : & [ KeyValue ] , resource_attributes : & [ KeyValue ] , interner : & GenericMapInterner ,
599+ otel_span : & OtlpSpan , span_attributes : & [ KeyValue ] , resource_attributes : & [ KeyValue ] ,
600+ interner : & GenericMapInterner , string_builder : & mut StringBuilder < GenericMapInterner > ,
577601) -> MetaString {
578602 let span_kind = SpanKind :: try_from ( otel_span. kind ) . unwrap_or ( SpanKind :: Unspecified ) ;
579- let mut string_builder = StringBuilder :: new ( ) ;
580603 if let Some ( value) = use_both_maps ( span_attributes, resource_attributes, true , RESOURCE_NAME_KEY , interner) {
581604 if !value. is_empty ( ) {
582605 return value;
@@ -664,9 +687,16 @@ fn get_otel_resource_v2(
664687}
665688
666689fn get_otel_resource_v2_truncated (
667- otel_span : & OtlpSpan , span_attributes : & [ KeyValue ] , resource_attributes : & [ KeyValue ] , interner : & GenericMapInterner ,
690+ otel_span : & OtlpSpan , span_attributes : & [ KeyValue ] , resource_attributes : & [ KeyValue ] ,
691+ interner : & GenericMapInterner , string_builder : & mut StringBuilder < GenericMapInterner > ,
668692) -> MetaString {
669- let res_name = get_otel_resource_v2 ( otel_span, span_attributes, resource_attributes, interner) ;
693+ let res_name = get_otel_resource_v2 (
694+ otel_span,
695+ span_attributes,
696+ resource_attributes,
697+ interner,
698+ string_builder,
699+ ) ;
670700 if res_name. len ( ) > MAX_RESOURCE_LEN {
671701 MetaString :: from ( truncate_utf8 ( & res_name, MAX_RESOURCE_LEN ) )
672702 } else {
@@ -753,7 +783,7 @@ const SQL_DB_SYSTEMS: &[&str] = &[
753783
754784fn map_attribute_generic (
755785 attribute : & KeyValue , meta : & mut FastHashMap < MetaString , MetaString > , metrics : & mut FastHashMap < MetaString , f64 > ,
756- ignore_missing_fields : bool , interner : & GenericMapInterner ,
786+ ignore_missing_fields : bool , interner : & GenericMapInterner , string_builder : & mut StringBuilder < GenericMapInterner > ,
757787) {
758788 if attribute. key . is_empty ( ) {
759789 return ;
@@ -772,6 +802,7 @@ fn map_attribute_generic(
772802 metrics,
773803 ignore_missing_fields,
774804 interner,
805+ string_builder,
775806 ) ;
776807 }
777808 OtlpValue :: BoolValue ( b) => {
@@ -783,6 +814,7 @@ fn map_attribute_generic(
783814 metrics,
784815 ignore_missing_fields,
785816 interner,
817+ string_builder,
786818 ) ;
787819 }
788820 OtlpValue :: BytesValue ( bytes) => {
@@ -794,6 +826,7 @@ fn map_attribute_generic(
794826 metrics,
795827 ignore_missing_fields,
796828 interner,
829+ string_builder,
797830 ) ;
798831 }
799832 OtlpValue :: IntValue ( i) => {
@@ -803,6 +836,7 @@ fn map_attribute_generic(
803836 metrics,
804837 ignore_missing_fields,
805838 interner,
839+ string_builder,
806840 ) ;
807841 }
808842 OtlpValue :: DoubleValue ( d) => {
@@ -812,6 +846,7 @@ fn map_attribute_generic(
812846 metrics,
813847 ignore_missing_fields,
814848 interner,
849+ string_builder,
815850 ) ;
816851 }
817852 _ => {
@@ -1041,9 +1076,9 @@ pub(super) fn otlp_value_to_string(value: &OtlpValue) -> Option<String> {
10411076
10421077fn conditionally_map_otlp_attribute_to_meta (
10431078 key : & str , value : & str , meta : & mut FastHashMap < MetaString , MetaString > , metrics : & mut FastHashMap < MetaString , f64 > ,
1044- ignore_missing_fields : bool , interner : & GenericMapInterner ,
1079+ ignore_missing_fields : bool , interner : & GenericMapInterner , string_builder : & mut StringBuilder < GenericMapInterner > ,
10451080) {
1046- if let Some ( mapped_key) = get_dd_key_for_otlp_attribute ( key, interner) {
1081+ if let Some ( mapped_key) = get_dd_key_for_otlp_attribute ( key, interner, string_builder ) {
10471082 if meta. contains_key ( & mapped_key) {
10481083 return ;
10491084 }
@@ -1056,9 +1091,9 @@ fn conditionally_map_otlp_attribute_to_meta(
10561091
10571092fn conditionally_map_otlp_attribute_to_metric (
10581093 key : & str , value : f64 , metrics : & mut FastHashMap < MetaString , f64 > , ignore_missing_fields : bool ,
1059- interner : & GenericMapInterner ,
1094+ interner : & GenericMapInterner , string_builder : & mut StringBuilder < GenericMapInterner > ,
10601095) {
1061- if let Some ( mapped_key) = get_dd_key_for_otlp_attribute ( key, interner) {
1096+ if let Some ( mapped_key) = get_dd_key_for_otlp_attribute ( key, interner, string_builder ) {
10621097 if metrics. contains_key ( & mapped_key) {
10631098 return ;
10641099 }
@@ -1108,15 +1143,17 @@ fn set_metric_field_otlp_if_empty(key: MetaString, value: f64, metrics: &mut Fas
11081143// OTLP HTTP convention. Otherwise, check if it is a Datadog APM convention key - if it is, it will be handled with
11091144// specialized logic elsewhere, so return None. If it isn't, return the original key.
11101145// based on the logic from the agent code https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/transform/transform.go#L179
1111- fn get_dd_key_for_otlp_attribute ( key : & str , interner : & GenericMapInterner ) -> Option < MetaString > {
1146+ fn get_dd_key_for_otlp_attribute (
1147+ key : & str , interner : & GenericMapInterner , string_builder : & mut StringBuilder < GenericMapInterner > ,
1148+ ) -> Option < MetaString > {
11121149 if let Some ( mapped) = HTTP_MAPPINGS . get ( key) {
11131150 return Some ( MetaString :: from_static ( mapped) ) ;
11141151 }
11151152 if let Some ( header_suffix) = key. strip_prefix ( HTTP_REQUEST_HEADER_PREFIX ) {
1116- let mut builder = StringBuilder :: new ( ) ;
1117- let _ = builder . push_str ( HTTP_REQUEST_HEADERS_PREFIX ) ;
1118- let _ = builder . push_str ( header_suffix) ;
1119- return Some ( MetaString :: from_interner ( builder . as_str ( ) , interner) ) ;
1153+ string_builder . clear ( ) ;
1154+ let _ = string_builder . push_str ( HTTP_REQUEST_HEADERS_PREFIX ) ;
1155+ let _ = string_builder . push_str ( header_suffix) ;
1156+ return Some ( MetaString :: from_interner ( string_builder . as_str ( ) , interner) ) ;
11201157 }
11211158 if !is_datadog_apm_convention_key ( key) {
11221159 return Some ( MetaString :: from_interner ( key, interner) ) ;
@@ -1488,27 +1525,56 @@ mod tests {
14881525 let mut meta = FastHashMap :: default ( ) ;
14891526 let mut metrics = FastHashMap :: default ( ) ;
14901527 let interner = test_interner ( ) ;
1528+ let mut string_builder = StringBuilder :: new ( ) . with_interner ( interner. clone ( ) ) ;
14911529
14921530 let http_attr = kv_str ( "http.request.method" , "GET" ) ;
1493- map_attribute_generic ( & http_attr, & mut meta, & mut metrics, false , & interner) ;
1531+ map_attribute_generic (
1532+ & http_attr,
1533+ & mut meta,
1534+ & mut metrics,
1535+ false ,
1536+ & interner,
1537+ & mut string_builder,
1538+ ) ;
14941539 assert_eq ! ( meta. get( "http.method" ) . map( |v| v. as_ref( ) ) , Some ( "GET" ) ) ;
14951540
14961541 let sampling_attr = kv_int ( "sampling.priority" , 2 ) ;
1497- map_attribute_generic ( & sampling_attr, & mut meta, & mut metrics, false , & interner) ;
1542+ map_attribute_generic (
1543+ & sampling_attr,
1544+ & mut meta,
1545+ & mut metrics,
1546+ false ,
1547+ & interner,
1548+ & mut string_builder,
1549+ ) ;
14981550 assert_eq ! ( metrics. get( SAMPLING_PRIORITY_METRIC_KEY ) , Some ( & 2.0 ) ) ;
14991551
15001552 let analytics_attr = kv_bool ( ANALYTICS_EVENT_KEY , true ) ;
1501- map_attribute_generic ( & analytics_attr, & mut meta, & mut metrics, false , & interner) ;
1553+ map_attribute_generic (
1554+ & analytics_attr,
1555+ & mut meta,
1556+ & mut metrics,
1557+ false ,
1558+ & interner,
1559+ & mut string_builder,
1560+ ) ;
15021561 assert_eq ! ( metrics. get( EVENT_EXTRACTION_METRIC_KEY ) , Some ( & 1.0 ) ) ;
15031562
15041563 let dd_attr = kv_str ( "datadog.service" , "svc" ) ;
1505- map_attribute_generic ( & dd_attr, & mut meta, & mut metrics, false , & interner) ;
1564+ map_attribute_generic ( & dd_attr, & mut meta, & mut metrics, false , & interner, & mut string_builder ) ;
15061565 assert ! ( !meta. contains_key( "datadog.service" ) ) ;
15071566
15081567 let mut meta_ignore = FastHashMap :: default ( ) ;
15091568 let mut metrics_ignore = FastHashMap :: default ( ) ;
15101569 let env_attr = kv_str ( "env" , "prod" ) ;
1511- map_attribute_generic ( & env_attr, & mut meta_ignore, & mut metrics_ignore, true , & interner) ;
1570+ map_attribute_generic (
1571+ & env_attr,
1572+ & mut meta_ignore,
1573+ & mut metrics_ignore,
1574+ true ,
1575+ & interner,
1576+ & mut string_builder,
1577+ ) ;
15121578 assert ! ( meta_ignore. is_empty( ) ) ;
15131579 }
15141580
@@ -1812,6 +1878,7 @@ mod tests {
18121878 ] ;
18131879
18141880 let interner = test_interner ( ) ;
1881+ let mut string_builder = StringBuilder :: new ( ) . with_interner ( interner. clone ( ) ) ;
18151882 for tc in test_cases {
18161883 let span = OtlpSpan {
18171884 name : "test-span" . to_string ( ) ,
@@ -1823,7 +1890,7 @@ mod tests {
18231890 ..Default :: default ( )
18241891 } ;
18251892
1826- let dd_span = otel_span_to_dd_span ( & span, & resource, None , false , true , & interner) ;
1893+ let dd_span = otel_span_to_dd_span ( & span, & resource, None , false , true , & interner, & mut string_builder ) ;
18271894 let meta = dd_span. meta ( ) ;
18281895
18291896 if tc. should_map {
0 commit comments