-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
NH-26751 NH-25139 Adjust propagator and sampler responsibilities #175
Changes from all commits
4e9efbf
259bfff
d63b43b
d946c60
57cc409
03a9211
61d997c
43dfc65
dfc5959
82c2998
50131b3
7118964
943db93
eca9631
806d4a5
71dddd0
4136b71
56449da
c5e0858
0f3c67f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,10 +13,10 @@ | |
from opentelemetry.trace.span import TraceState | ||
|
||
from solarwinds_apm.apm_constants import ( | ||
INTL_SWO_SIGNATURE_KEY, | ||
INTL_SWO_TRACESTATE_KEY, | ||
INTL_SWO_X_OPTIONS_KEY, | ||
) | ||
from solarwinds_apm.traceoptions import XTraceOptions | ||
from solarwinds_apm.w3c_transformer import W3CTransformer | ||
|
||
logger = logging.getLogger(__name__) | ||
|
@@ -46,27 +46,16 @@ def extract( | |
|
||
xtraceoptions_header = getter.get( | ||
carrier, self._XTRACEOPTIONS_HEADER_NAME | ||
) | ||
if xtraceoptions_header: | ||
context.update({INTL_SWO_X_OPTIONS_KEY: xtraceoptions_header[0]}) | ||
logger.debug( | ||
"Extracted %s as %s: %s", | ||
self._XTRACEOPTIONS_HEADER_NAME, | ||
INTL_SWO_X_OPTIONS_KEY, | ||
xtraceoptions_header[0], | ||
) | ||
|
||
) or [""] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Kind of a shame that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed!!! 🙃 |
||
signature_header = getter.get( | ||
carrier, self._XTRACEOPTIONS_SIGNATURE_HEADER_NAME | ||
) or [""] | ||
xtraceoptions = XTraceOptions( | ||
xtraceoptions_header[0], | ||
signature_header[0], | ||
) | ||
if signature_header: | ||
context.update({INTL_SWO_SIGNATURE_KEY: signature_header[0]}) | ||
logger.debug( | ||
"Extracted %s as %s: %s", | ||
self._XTRACEOPTIONS_SIGNATURE_HEADER_NAME, | ||
INTL_SWO_SIGNATURE_KEY, | ||
signature_header[0], | ||
) | ||
|
||
context.update({INTL_SWO_X_OPTIONS_KEY: xtraceoptions}) | ||
return context | ||
|
||
def inject( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,7 @@ | |
INTL_SWO_COMMA_W3C_SANITIZED, | ||
INTL_SWO_EQUALS_W3C_SANITIZED, | ||
INTL_SWO_TRACESTATE_KEY, | ||
INTL_SWO_X_OPTIONS_KEY, | ||
INTL_SWO_X_OPTIONS_RESPONSE_KEY, | ||
) | ||
from solarwinds_apm.traceoptions import XTraceOptions | ||
|
@@ -316,79 +317,32 @@ def create_xtraceoptions_response_value( | |
|
||
return ";".join(response) | ||
|
||
def create_new_trace_state( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
def calculate_trace_state( | ||
self, | ||
decision: dict, | ||
parent_span_context: SpanContext, | ||
xtraceoptions: Optional[XTraceOptions] = None, | ||
) -> TraceState: | ||
"""Creates new TraceState based on trace decision, parent span id, | ||
and x-trace-options if provided""" | ||
trace_state = TraceState( | ||
[ | ||
( | ||
INTL_SWO_TRACESTATE_KEY, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The sampler no longer sets There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This makes me so happy |
||
W3CTransformer.sw_from_span_and_decision( | ||
parent_span_context.span_id, | ||
W3CTransformer.trace_flags_from_int( | ||
decision["do_sample"] | ||
), | ||
), | ||
) | ||
] | ||
) | ||
if xtraceoptions and xtraceoptions.options_header: | ||
"""Calculates trace_state based on x-trace-options if provided -- for non-existent or remote parent spans only.""" | ||
# No valid parent i.e. root span, or parent is remote | ||
if ( | ||
not parent_span_context | ||
or not parent_span_context.is_valid | ||
or not parent_span_context.trace_state | ||
): | ||
trace_state = TraceState() | ||
else: | ||
trace_state = parent_span_context.trace_state | ||
# Update with x-trace-options-response if applicable | ||
# Not a propagated header, so always add at should_sample | ||
if xtraceoptions and xtraceoptions.include_response: | ||
trace_state = trace_state.add( | ||
INTL_SWO_X_OPTIONS_RESPONSE_KEY, | ||
self.create_xtraceoptions_response_value( | ||
decision, parent_span_context, xtraceoptions | ||
), | ||
) | ||
logger.debug("Created new trace_state: %s", trace_state) | ||
return trace_state | ||
|
||
def calculate_trace_state( | ||
self, | ||
decision: dict, | ||
parent_span_context: SpanContext, | ||
xtraceoptions: Optional[XTraceOptions] = None, | ||
) -> TraceState: | ||
"""Calculates trace_state based on parent span context, trace decision, | ||
and x-trace-options if provided -- for non-existent or remote parent | ||
spans only.""" | ||
# No valid parent i.e. root span, or parent is remote | ||
if not parent_span_context.is_valid: | ||
trace_state = self.create_new_trace_state( | ||
decision, parent_span_context, xtraceoptions | ||
) | ||
else: | ||
trace_state = parent_span_context.trace_state | ||
if not trace_state: | ||
# tracestate nonexistent/non-parsable | ||
trace_state = self.create_new_trace_state( | ||
decision, parent_span_context, xtraceoptions | ||
) | ||
else: | ||
# Update trace_state with span_id and sw trace_flags | ||
trace_state = trace_state.update( | ||
INTL_SWO_TRACESTATE_KEY, | ||
W3CTransformer.sw_from_span_and_decision( | ||
parent_span_context.span_id, # NOTE: This is a placeholder before propagator inject | ||
W3CTransformer.trace_flags_from_int( | ||
decision["do_sample"] | ||
), | ||
), | ||
) | ||
# Update trace_state with x-trace-options-response | ||
# Not a propagated header, so always an add | ||
if xtraceoptions and xtraceoptions.options_header: | ||
trace_state = trace_state.add( | ||
INTL_SWO_X_OPTIONS_RESPONSE_KEY, | ||
self.create_xtraceoptions_response_value( | ||
decision, parent_span_context, xtraceoptions | ||
), | ||
) | ||
logger.debug("Updated trace_state: %s", trace_state) | ||
logger.debug("Calculated trace_state: %s", trace_state) | ||
return trace_state | ||
|
||
def add_tracestate_capture_to_attributes_dict( | ||
|
@@ -541,7 +495,13 @@ def should_sample( | |
parent_span_context = get_current_span( | ||
parent_context | ||
).get_span_context() | ||
xtraceoptions = XTraceOptions(parent_context) | ||
|
||
if not parent_context or not parent_context.get( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. An optimization for this block might be: xtraceoptions = XTraceOptions()
if parent_context and parent_context.is_valid():
xtraceoptions = parent_context.get(INTL_SWO_OPTIONS_KEY) or xtraceoptions This would remove the first There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For Otel Python there isn't a When the Otel SDK calls If Otel SpanContext ( |
||
INTL_SWO_X_OPTIONS_KEY | ||
): | ||
xtraceoptions = XTraceOptions() | ||
else: | ||
xtraceoptions = parent_context.get(INTL_SWO_X_OPTIONS_KEY) | ||
|
||
liboboe_decision = self.calculate_liboboe_decision( | ||
parent_span_context, name, kind, attributes, xtraceoptions | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,14 +6,6 @@ | |
|
||
import logging | ||
import re | ||
import typing | ||
|
||
from opentelemetry.context.context import Context | ||
|
||
from solarwinds_apm.apm_constants import ( | ||
INTL_SWO_SIGNATURE_KEY, | ||
INTL_SWO_X_OPTIONS_KEY, | ||
) | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
@@ -31,28 +23,34 @@ class XTraceOptions: | |
# pylint: disable=too-many-branches,too-many-statements | ||
def __init__( | ||
self, | ||
context: typing.Optional[Context] = None, | ||
xtraceoptions_header: str = "", | ||
signature_header: str = "", | ||
): | ||
""" | ||
Args: | ||
context: OTel context that may contain OTEL_CONTEXT_SW_OPTIONS_KEY,OTEL_CONTEXT_SW_SIGNATURE_KEY | ||
xtraceoptions_header: extracted request header value | ||
signature_header: extracted request header value | ||
""" | ||
self.ignored = [] | ||
self.options_header = "" | ||
self.signature = None | ||
self.signature = "" | ||
self.custom_kvs = {} | ||
self.sw_keys = "" | ||
self.trigger_trace = 0 | ||
self.timestamp = 0 | ||
self.include_response = False | ||
|
||
if not context: | ||
return | ||
options_header = context.get(INTL_SWO_X_OPTIONS_KEY, "") | ||
# store original header for sample decision later | ||
self.options_header = options_header | ||
if signature_header: | ||
self.signature = signature_header | ||
# store original header for c-lib sample decision later | ||
# only if signature_header exists | ||
self.options_header = xtraceoptions_header | ||
Comment on lines
+45
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was Raph's nice suggestion to save on space because options_header only comes in play for a signed TT request. |
||
|
||
if options_header: | ||
traceoptions = re.split(r";+", options_header) | ||
if xtraceoptions_header: | ||
# If x-trace-options header given, set response header | ||
self.include_response = True | ||
|
||
traceoptions = re.split(r";+", xtraceoptions_header) | ||
for option in traceoptions: | ||
# KVs (e.g. sw-keys or custom-key1) are assigned by equals | ||
option_kv = option.split("=", 1) | ||
|
@@ -117,7 +115,3 @@ def __init__( | |
"Some x-trace-options were ignored: %s", | ||
", ".join(self.ignored), | ||
) | ||
|
||
options_signature = context.get(INTL_SWO_SIGNATURE_KEY, None) | ||
if options_signature: | ||
self.signature = options_signature |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All xto info is in the
XTraceOptions
struct underINTL_SWO_X_OPTIONS_KEY
key in Otel context, so we no longer store under a separateINTL_SWO_SIGNATURE_KEY
.