@@ -5,6 +5,7 @@ use crate::appsec::processor::Processor as AppSecProcessor;
55use crate :: appsec:: processor:: context:: HoldArguments ;
66use crate :: config;
77use crate :: lifecycle:: invocation:: processor:: S_TO_MS ;
8+ use crate :: lifecycle:: invocation:: triggers:: get_default_service_name;
89use crate :: tags:: lambda:: tags:: COMPUTE_STATS_KEY ;
910use crate :: tags:: provider;
1011use crate :: traces:: span_pointers:: { SpanPointer , attach_span_pointers_to_meta} ;
@@ -70,8 +71,25 @@ impl TraceChunkProcessor for ChunkProcessor {
7071 span. service . clone_from ( service) ;
7172 }
7273
73- // Remove the _dd.base_service tag for unintentional service name override
74- span. meta . remove ( "_dd.base_service" ) ;
74+ // For inferred spans, set _dd.base_service to the resolved execution
75+ // span service (same get_default_service_name used in processor.rs).
76+ // This covers extension-only languages (Go, .NET, Java, Ruby).
77+ if span. meta . contains_key ( "_inferred_span.tag_source" )
78+ && !span. meta . contains_key ( "_dd.base_service" )
79+ {
80+ let base_service = get_default_service_name (
81+ & self
82+ . config
83+ . service
84+ . clone ( )
85+ . or_else ( || self . tags_provider . get_canonical_resource_name ( ) )
86+ . unwrap_or_else ( || "aws.lambda" . to_string ( ) ) ,
87+ "aws.lambda" ,
88+ self . config . trace_aws_service_representation_enabled ,
89+ ) ;
90+ span. meta
91+ . insert ( "_dd.base_service" . to_string ( ) , base_service) ;
92+ }
7593
7694 self . tags_provider . get_tags_map ( ) . iter ( ) . for_each ( |( k, v) | {
7795 span. meta . insert ( k. clone ( ) , v. clone ( ) ) ;
@@ -1546,4 +1564,195 @@ mod tests {
15461564 // Both extension and tracer compute stats → tracer takes precedence, tag "0", no double-count.
15471565 check_compute_stats_behavior ( true , true ) . await ;
15481566 }
1567+
1568+ fn create_inferred_span ( ) -> pb:: Span {
1569+ let mut meta = HashMap :: new ( ) ;
1570+ meta. insert (
1571+ "_inferred_span.tag_source" . to_string ( ) ,
1572+ "self" . to_string ( ) ,
1573+ ) ;
1574+ pb:: Span {
1575+ name : "aws.sqs" . to_string ( ) ,
1576+ service : "sqs" . to_string ( ) ,
1577+ resource : "my-queue" . to_string ( ) ,
1578+ trace_id : 1 ,
1579+ span_id : 2 ,
1580+ parent_id : 0 ,
1581+ start : 1000 ,
1582+ duration : 500 ,
1583+ error : 0 ,
1584+ meta,
1585+ metrics : HashMap :: new ( ) ,
1586+ r#type : "web" . to_string ( ) ,
1587+ span_links : vec ! [ ] ,
1588+ meta_struct : HashMap :: new ( ) ,
1589+ span_events : vec ! [ ] ,
1590+ }
1591+ }
1592+
1593+ fn create_chunk_processor ( config : Arc < Config > ) -> ChunkProcessor {
1594+ let tags_provider = create_tags_provider ( config. clone ( ) ) ;
1595+ ChunkProcessor {
1596+ config,
1597+ obfuscation_config : Arc :: new (
1598+ ObfuscationConfig :: new ( ) . expect ( "Failed to create ObfuscationConfig" ) ,
1599+ ) ,
1600+ tags_provider,
1601+ span_pointers : None ,
1602+ }
1603+ }
1604+
1605+ #[ test]
1606+ fn test_base_service_uses_function_name_when_no_dd_service ( ) {
1607+ let config = Arc :: new ( Config {
1608+ service : None ,
1609+ trace_aws_service_representation_enabled : true ,
1610+ ..Config :: default ( )
1611+ } ) ;
1612+ let mut processor = create_chunk_processor ( config) ;
1613+
1614+ let inferred_span = create_inferred_span ( ) ;
1615+ let mut chunk = pb:: TraceChunk {
1616+ priority : 1 ,
1617+ origin : "lambda" . to_string ( ) ,
1618+ spans : vec ! [ inferred_span] ,
1619+ tags : HashMap :: new ( ) ,
1620+ dropped_trace : false ,
1621+ } ;
1622+
1623+ processor. process ( & mut chunk, 0 ) ;
1624+
1625+ assert_eq ! (
1626+ chunk. spans[ 0 ] . meta. get( "_dd.base_service" ) . unwrap( ) ,
1627+ "my-function" ,
1628+ "base_service should be the function name when DD_SERVICE is not set"
1629+ ) ;
1630+ }
1631+
1632+ #[ test]
1633+ fn test_base_service_uses_dd_service_when_set ( ) {
1634+ let config = Arc :: new ( Config {
1635+ service : Some ( "my-payments-api" . to_string ( ) ) ,
1636+ trace_aws_service_representation_enabled : true ,
1637+ ..Config :: default ( )
1638+ } ) ;
1639+ let mut processor = create_chunk_processor ( config) ;
1640+
1641+ let inferred_span = create_inferred_span ( ) ;
1642+ let mut chunk = pb:: TraceChunk {
1643+ priority : 1 ,
1644+ origin : "lambda" . to_string ( ) ,
1645+ spans : vec ! [ inferred_span] ,
1646+ tags : HashMap :: new ( ) ,
1647+ dropped_trace : false ,
1648+ } ;
1649+
1650+ processor. process ( & mut chunk, 0 ) ;
1651+
1652+ assert_eq ! (
1653+ chunk. spans[ 0 ] . meta. get( "_dd.base_service" ) . unwrap( ) ,
1654+ "my-payments-api" ,
1655+ "base_service should be DD_SERVICE when set"
1656+ ) ;
1657+ }
1658+
1659+ #[ test]
1660+ fn test_base_service_is_aws_lambda_when_representation_disabled ( ) {
1661+ let config = Arc :: new ( Config {
1662+ service : Some ( "my-payments-api" . to_string ( ) ) ,
1663+ trace_aws_service_representation_enabled : false ,
1664+ ..Config :: default ( )
1665+ } ) ;
1666+ let mut processor = create_chunk_processor ( config) ;
1667+
1668+ let inferred_span = create_inferred_span ( ) ;
1669+ let mut chunk = pb:: TraceChunk {
1670+ priority : 1 ,
1671+ origin : "lambda" . to_string ( ) ,
1672+ spans : vec ! [ inferred_span] ,
1673+ tags : HashMap :: new ( ) ,
1674+ dropped_trace : false ,
1675+ } ;
1676+
1677+ processor. process ( & mut chunk, 0 ) ;
1678+
1679+ assert_eq ! (
1680+ chunk. spans[ 0 ] . meta. get( "_dd.base_service" ) . unwrap( ) ,
1681+ "aws.lambda" ,
1682+ "base_service should be 'aws.lambda' when representation is disabled"
1683+ ) ;
1684+ }
1685+
1686+ #[ test]
1687+ fn test_base_service_not_set_on_non_inferred_spans ( ) {
1688+ let config = Arc :: new ( Config {
1689+ service : Some ( "my-service" . to_string ( ) ) ,
1690+ trace_aws_service_representation_enabled : true ,
1691+ ..Config :: default ( )
1692+ } ) ;
1693+ let mut processor = create_chunk_processor ( config) ;
1694+
1695+ let regular_span = pb:: Span {
1696+ name : "http.request" . to_string ( ) ,
1697+ service : "my-service" . to_string ( ) ,
1698+ resource : "GET /users" . to_string ( ) ,
1699+ trace_id : 1 ,
1700+ span_id : 3 ,
1701+ parent_id : 0 ,
1702+ start : 1000 ,
1703+ duration : 500 ,
1704+ error : 0 ,
1705+ meta : HashMap :: new ( ) ,
1706+ metrics : HashMap :: new ( ) ,
1707+ r#type : "web" . to_string ( ) ,
1708+ span_links : vec ! [ ] ,
1709+ meta_struct : HashMap :: new ( ) ,
1710+ span_events : vec ! [ ] ,
1711+ } ;
1712+ let mut chunk = pb:: TraceChunk {
1713+ priority : 1 ,
1714+ origin : "lambda" . to_string ( ) ,
1715+ spans : vec ! [ regular_span] ,
1716+ tags : HashMap :: new ( ) ,
1717+ dropped_trace : false ,
1718+ } ;
1719+
1720+ processor. process ( & mut chunk, 0 ) ;
1721+
1722+ assert ! (
1723+ !chunk. spans[ 0 ] . meta. contains_key( "_dd.base_service" ) ,
1724+ "base_service should not be set on non-inferred spans"
1725+ ) ;
1726+ }
1727+
1728+ #[ test]
1729+ fn test_base_service_not_overwritten_when_already_set ( ) {
1730+ let config = Arc :: new ( Config {
1731+ service : Some ( "my-service" . to_string ( ) ) ,
1732+ trace_aws_service_representation_enabled : true ,
1733+ ..Config :: default ( )
1734+ } ) ;
1735+ let mut processor = create_chunk_processor ( config) ;
1736+
1737+ let mut inferred_span = create_inferred_span ( ) ;
1738+ inferred_span. meta . insert (
1739+ "_dd.base_service" . to_string ( ) ,
1740+ "tracer-set-value" . to_string ( ) ,
1741+ ) ;
1742+ let mut chunk = pb:: TraceChunk {
1743+ priority : 1 ,
1744+ origin : "lambda" . to_string ( ) ,
1745+ spans : vec ! [ inferred_span] ,
1746+ tags : HashMap :: new ( ) ,
1747+ dropped_trace : false ,
1748+ } ;
1749+
1750+ processor. process ( & mut chunk, 0 ) ;
1751+
1752+ assert_eq ! (
1753+ chunk. spans[ 0 ] . meta. get( "_dd.base_service" ) . unwrap( ) ,
1754+ "tracer-set-value" ,
1755+ "base_service should not be overwritten when already set by the tracer"
1756+ ) ;
1757+ }
15491758}
0 commit comments