@@ -285,7 +285,8 @@ def __init__(self, application, enabled=None, source=None):
285285        self .tracestate  =  "" 
286286        self ._priority  =  None 
287287        self ._sampled  =  None 
288-         self ._traceparent_sampled  =  None 
288+         # Remote parent sampled is set from the W3C parent header or the Newrelic header if no W3C parent header is present. 
289+         self ._remote_parent_sampled  =  None 
289290
290291        self ._distributed_trace_state  =  0 
291292
@@ -569,7 +570,7 @@ def __exit__(self, exc, value, tb):
569570        if  self ._settings .distributed_tracing .enabled :
570571            # Sampled and priority need to be computed at the end of the 
571572            # transaction when distributed tracing or span events are enabled. 
572-             self ._compute_sampled_and_priority ()
573+             self ._make_sampling_decision ()
573574
574575        self ._cached_path ._name  =  self .path 
575576        agent_attributes  =  self .agent_attributes 
@@ -636,6 +637,7 @@ def __exit__(self, exc, value, tb):
636637            trace_id = self .trace_id ,
637638            loop_time = self ._loop_time ,
638639            root = root_node ,
640+             partial_granularity_sampled = hasattr (self , "partial_granularity_sampled" ),
639641        )
640642
641643        # Clear settings as we are all done and don't need it 
@@ -1004,35 +1006,120 @@ def _update_agent_attributes(self):
10041006    def  user_attributes (self ):
10051007        return  create_attributes (self ._custom_params , DST_ALL , self .attribute_filter )
10061008
1007-     def  sampling_algo_compute_sampled_and_priority (self ):
1008-         if  self ._priority  is  None :
1009+     def  sampling_algo_compute_sampled_and_priority (self , priority , sampled ):
1010+         # self._priority and self._sampled are set when parsing the W3C tracestate 
1011+         # or newrelic DT headers and may be overridden in _make_sampling_decision 
1012+         # based on the configuration. The only time they are set in here is when the 
1013+         # sampling decision must be made by the adaptive sampling algorithm. 
1014+         if  priority  is  None :
10091015            # Truncate priority field to 6 digits past the decimal. 
1010-             self ._priority  =  float (f"{ random .random ():.6f}  " )  # noqa: S311 
1011-         if  self ._sampled  is  None :
1012-             self ._sampled  =  self ._application .compute_sampled ()
1013-             if  self ._sampled :
1014-                 self ._priority  +=  1 
1015- 
1016-     def  _compute_sampled_and_priority (self ):
1017-         if  self ._traceparent_sampled  is  None :
1016+             priority  =  float (f"{ random .random ():.6f}  " )  # noqa: S311 
1017+         if  sampled  is  None :
1018+             _logger .debug ("No trusted account id found. Sampling decision will be made by adaptive sampling algorithm." )
1019+             sampled  =  self ._application .compute_sampled ()
1020+             if  sampled :
1021+                 priority  +=  1 
1022+         return  priority , sampled 
1023+ 
1024+     def  _compute_sampled_and_priority (
1025+         self ,
1026+         priority ,
1027+         sampled ,
1028+         remote_parent_sampled_path ,
1029+         remote_parent_sampled_setting ,
1030+         remote_parent_not_sampled_path ,
1031+         remote_parent_not_sampled_setting ,
1032+     ):
1033+         if  self ._remote_parent_sampled  is  None :
10181034            config  =  "default"   # Use sampling algo. 
1019-         elif  self ._traceparent_sampled :
1020-             setting_path  =  "distributed_tracing.sampler.remote_parent_sampled" 
1021-             config  =  self .settings .distributed_tracing .sampler .remote_parent_sampled 
1022-         else :  # self._traceparent_sampled is False. 
1023-             setting_path  =  "distributed_tracing.sampler.remote_parent_not_sampled" 
1024-             config  =  self .settings .distributed_tracing .sampler .remote_parent_not_sampled 
1025- 
1035+             _logger .debug ("Sampling decision made based on no remote parent sampling decision present." )
1036+         elif  self ._remote_parent_sampled :
1037+             setting_path  =  remote_parent_sampled_path 
1038+             config  =  remote_parent_sampled_setting 
1039+             _logger .debug (
1040+                 "Sampling decision made based on remote_parent_sampled=%s and %s=%s." ,
1041+                 self ._remote_parent_sampled ,
1042+                 setting_path ,
1043+                 config ,
1044+             )
1045+         else :  # self._remote_parent_sampled is False. 
1046+             setting_path  =  remote_parent_not_sampled_path 
1047+             config  =  remote_parent_not_sampled_setting 
1048+             _logger .debug (
1049+                 "Sampling decision made based on remote_parent_sampled=%s and %s=%s." ,
1050+                 self ._remote_parent_sampled ,
1051+                 setting_path ,
1052+                 config ,
1053+             )
10261054        if  config  ==  "always_on" :
1027-             self . _sampled  =  True 
1028-             self . _priority  =  2.0 
1055+             sampled  =  True 
1056+             priority  =  2.0 
10291057        elif  config  ==  "always_off" :
1030-             self . _sampled  =  False 
1031-             self . _priority  =  0 
1058+             sampled  =  False 
1059+             priority  =  0 
10321060        else :
1033-             if  config  !=   "default" :
1061+             if  config  not   in  ( "default" ,  "adaptive" ) :
10341062                _logger .warning ("%s=%s is not a recognized value. Using 'default' instead." , setting_path , config )
1035-             self .sampling_algo_compute_sampled_and_priority ()
1063+ 
1064+             _logger .debug (
1065+                 "Let adaptive sampler algorithm decide based on sampled=%s and priority=%s." , sampled , priority 
1066+             )
1067+             priority , sampled  =  self .sampling_algo_compute_sampled_and_priority (priority , sampled )
1068+         return  priority , sampled 
1069+ 
1070+     def  _make_sampling_decision (self ):
1071+         # The sampling decision is computed each time a DT header is generated for exit spans as it is needed 
1072+         # to send the DT headers. Don't recompute the sampling decision multiple times as it is expensive. 
1073+         if  hasattr (self , "_sampling_decision_made" ):
1074+             return 
1075+         priority  =  self ._priority 
1076+         sampled  =  self ._sampled 
1077+         # Compute sampling decision for full granularity. 
1078+         if  self .settings .distributed_tracing .sampler .full_granularity .enabled :
1079+             _logger .debug (
1080+                 "Full granularity tracing is enabled. Asking if full granularity wants to sample. priority=%s, sampled=%s" ,
1081+                 priority ,
1082+                 sampled ,
1083+             )
1084+             computed_priority , computed_sampled  =  self ._compute_sampled_and_priority (
1085+                 priority ,
1086+                 sampled ,
1087+                 remote_parent_sampled_path = "distributed_tracing.sampler.full_granularity.remote_parent_sampled" ,
1088+                 remote_parent_sampled_setting = self .settings .distributed_tracing .sampler .full_granularity .remote_parent_sampled ,
1089+                 remote_parent_not_sampled_path = "distributed_tracing.sampler.full_granularity.remote_parent_not_sampled" ,
1090+                 remote_parent_not_sampled_setting = self .settings .distributed_tracing .sampler .full_granularity .remote_parent_not_sampled ,
1091+             )
1092+             _logger .debug ("Full granularity sampling decision was %s with priority=%s." , sampled , priority )
1093+             if  computed_sampled  or  not  self .settings .distributed_tracing .sampler .partial_granularity .enabled :
1094+                 self ._priority  =  computed_priority 
1095+                 self ._sampled  =  computed_sampled 
1096+                 self ._sampling_decision_made  =  True 
1097+                 return 
1098+ 
1099+         # If full granularity is not going to sample, let partial granularity decide. 
1100+         if  self .settings .distributed_tracing .sampler .partial_granularity .enabled :
1101+             _logger .debug ("Partial granularity tracing is enabled. Asking if partial granularity wants to sample." )
1102+             self ._priority , self ._sampled  =  self ._compute_sampled_and_priority (
1103+                 priority ,
1104+                 sampled ,
1105+                 remote_parent_sampled_path = "distributed_tracing.sampler.partial_granularity.remote_parent_sampled" ,
1106+                 remote_parent_sampled_setting = self .settings .distributed_tracing .sampler .partial_granularity .remote_parent_sampled ,
1107+                 remote_parent_not_sampled_path = "distributed_tracing.sampler.partial_granularity.remote_parent_not_sampled" ,
1108+                 remote_parent_not_sampled_setting = self .settings .distributed_tracing .sampler .partial_granularity .remote_parent_not_sampled ,
1109+             )
1110+             _logger .debug (
1111+                 "Partial granularity sampling decision was %s with priority=%s." , self ._sampled , self ._priority 
1112+             )
1113+             self ._sampling_decision_made  =  True 
1114+             if  self ._sampled :
1115+                 self .partial_granularity_sampled  =  True 
1116+             return 
1117+ 
1118+         # This is only reachable if both full and partial granularity tracing are off. 
1119+         # Set priority=0 and do not sample. This enables DT headers to still be sent 
1120+         # even if the trace is never sampled. 
1121+         self ._priority  =  0 
1122+         self ._sampled  =  False 
10361123
10371124    def  _freeze_path (self ):
10381125        if  self ._frozen_path  is  None :
@@ -1101,7 +1188,7 @@ def _create_distributed_trace_data(self):
11011188        if  not  (account_id  and  application_id  and  trusted_account_key  and  settings .distributed_tracing .enabled ):
11021189            return 
11031190
1104-         self ._compute_sampled_and_priority ()
1191+         self ._make_sampling_decision ()
11051192        data  =  {
11061193            "ty" : "App" ,
11071194            "ac" : account_id ,
@@ -1204,7 +1291,7 @@ def _accept_distributed_trace_payload(self, payload, transport_type="HTTP"):
12041291            if  not  any (k  in  data  for  k  in  ("id" , "tx" )):
12051292                self ._record_supportability ("Supportability/DistributedTrace/AcceptPayload/ParseException" )
12061293                return  False 
1207- 
1294+              self . _remote_parent_sampled   =   data . get ( "sa" ) 
12081295            settings  =  self ._settings 
12091296            account_id  =  data .get ("ac" )
12101297            trusted_account_key  =  settings .trusted_account_key  or  (
@@ -1254,10 +1341,8 @@ def _accept_distributed_trace_data(self, data, transport_type):
12541341
12551342        self ._trace_id  =  data .get ("tr" )
12561343
1257-         priority  =  data .get ("pr" )
1258-         if  priority  is  not   None :
1259-             self ._priority  =  priority 
1260-             self ._sampled  =  data .get ("sa" )
1344+         self ._priority  =  data .get ("pr" )
1345+         self ._sampled  =  data .get ("sa" )
12611346
12621347        if  "ti"  in  data :
12631348            transport_start  =  data ["ti" ] /  1000.0 
@@ -1297,6 +1382,7 @@ def accept_distributed_trace_headers(self, headers, transport_type="HTTP"):
12971382            try :
12981383                traceparent  =  ensure_str (traceparent ).strip ()
12991384                data  =  W3CTraceParent .decode (traceparent )
1385+                 self ._remote_parent_sampled  =  data .pop ("sa" , None )
13001386            except :
13011387                data  =  None 
13021388
@@ -1332,7 +1418,6 @@ def accept_distributed_trace_headers(self, headers, transport_type="HTTP"):
13321418                    else :
13331419                        self ._record_supportability ("Supportability/TraceContext/TraceState/NoNrEntry" )
13341420
1335-             self ._traceparent_sampled  =  data .get ("sa" )
13361421            self ._accept_distributed_trace_data (data , transport_type )
13371422            self ._record_supportability ("Supportability/TraceContext/Accept/Success" )
13381423            return  True 
0 commit comments