Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions solarwinds_apm/propagator.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@


class SolarWindsPropagator(TraceContextTextMapPropagator):
"""Extracts and injects SolarWinds headers and W3C trace context
headers for trace propagation.
"""
"""Extract and inject SolarWinds headers and W3C trace context for trace propagation."""

_INVALID_SPAN_ID = 0x0000000000000000
_TRACESTATE_HEADER_NAME = "tracestate"
Expand All @@ -44,8 +42,15 @@ def extract(
context: Context | None = None,
getter: textmap.Getter = textmap.default_getter,
) -> Context:
"""Extracts traceparent, tracestate, sw trace options, and signature
from carrier into OTel Context.
"""Extract traceparent, tracestate, sw trace options, and signature from carrier into OTel Context.

Parameters:
carrier (textmap.CarrierT): The carrier to extract context from.
context (Context | None): Optional existing context to update. Defaults to None.
getter (textmap.Getter): Getter for extracting headers from carrier. Defaults to default_getter.

Returns:
Context: Updated context with extracted trace information.
"""
context = super().extract(carrier, context, getter)

Expand All @@ -69,9 +74,14 @@ def inject(
context: Context | None = None,
setter: textmap.Setter = textmap.default_setter,
) -> None:
"""Injects traceparent, tracestate with valid sw into carrier
for HTTP request. Excludes any xtraceoptions_response if in
tracestate.
"""Inject traceparent and tracestate with valid sw into carrier for HTTP request.

Excludes any xtraceoptions_response if in tracestate.

Parameters:
carrier (textmap.CarrierT): The carrier to inject context into.
context (Context | None): Optional context to inject from. Defaults to None.
setter (textmap.Setter): Setter for injecting headers into carrier. Defaults to default_setter.
"""
super().inject(carrier, context, setter)

Expand Down Expand Up @@ -130,5 +140,10 @@ def inject(
def fields(
self,
) -> typing.Set[str]: # pylint: disable=deprecated-typing-alias
"""Returns a set with the fields set in `inject`"""
"""
Return the set of fields injected by this propagator.

Returns:
typing.Set[str]: Set of header field names.
"""
return super().fields
22 changes: 18 additions & 4 deletions solarwinds_apm/response_propagator.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@


class SolarWindsTraceResponsePropagator(ResponsePropagator):
"""Propagator that injects SW values into HTTP responses"""
"""Inject SolarWinds trace values into HTTP responses."""

_HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS = (
"Access-Control-Expose-Headers"
Expand All @@ -42,7 +42,13 @@ def inject(
context: Context | None = None,
setter: textmap.Setter = textmap.default_setter,
) -> None:
"""Injects x-trace and options-response into the HTTP response carrier."""
"""Inject x-trace and options-response into the HTTP response carrier.

Parameters:
carrier (textmap.CarrierT): The HTTP response carrier to inject headers into.
context (Context | None): Optional context to inject from. Defaults to None.
setter (textmap.Setter): Setter for injecting headers into carrier. Defaults to default_setter.
"""
span = trace.get_current_span(context)
span_context = span.get_span_context()
if span_context == trace.INVALID_SPAN_CONTEXT:
Expand Down Expand Up @@ -75,10 +81,18 @@ def inject(
)

def recover_response_from_tracestate(self, tracestate: TraceState) -> str:
"""Use tracestate to recover xtraceoptions response by
converting delimiters:
"""
Recover xtraceoptions response from tracestate by converting delimiters.

Converts W3C-sanitized delimiters back to original format:
EQUALS_W3C_SANITIZED becomes EQUALS
COMMA_W3C_SANITIZED becomes COMMA

Parameters:
tracestate (TraceState): The tracestate to extract response from.

Returns:
str: The recovered xtraceoptions response string.
"""
sanitized = tracestate.get(INTL_SWO_X_OPTIONS_RESPONSE_KEY, None)
if not sanitized:
Expand Down
25 changes: 17 additions & 8 deletions solarwinds_apm/sampler.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,25 @@


class ParentBasedSwSampler(ParentBased):
"""
Sampler that respects its parent span's sampling decision, but otherwise
samples according to the configurations from the NH/AO backend.
"""Respect parent span's sampling decision or use SolarWinds backend configuration.

Requires "SolarWindsApmConfig".
Requires SolarWindsApmConfig.
"""

def __init__(
self,
apm_config: SolarWindsApmConfig,
):
) -> None:
"""
Initialize ParentBasedSwSampler with SolarWinds APM configuration.

Uses HttpSampler for non-Lambda environments or JsonSampler for Lambda.
Uses HttpSampler/JsonSampler if no parent span.
Uses HttpSampler/JsonSampler if parent span is_remote.
Uses OTEL defaults if parent span is_local.
Uses OpenTelemetry defaults if parent span is_local.

Parameters:
apm_config (SolarWindsApmConfig): The SolarWinds APM configuration.
"""
configuration = SolarWindsApmConfig.to_configuration(apm_config)
self.sampler = None
Expand All @@ -58,7 +62,12 @@ def __init__(
# should_sample defined by ParentBased

def wait_until_ready(self, timeout: int) -> bool:
"""
Waits until the sampler is ready.
"""Wait until the sampler is ready.

Parameters:
timeout (int): Maximum time to wait in seconds.

Returns:
bool: True if sampler is ready, False if timeout occurred.
"""
return self.sampler.wait_until_ready(timeout)
17 changes: 12 additions & 5 deletions solarwinds_apm/traceoptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@
#
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

"""X-Trace-Options header parsing and formatting for trigger tracing."""

import logging
import re

logger = logging.getLogger(__name__)


class XTraceOptions:
"""Formats X-Trace-Options and signature for trigger tracing"""
"""Parse and format X-Trace-Options and signature headers for trigger tracing.

Handles parsing of trigger-trace, sw-keys, custom-*, and ts options.
"""

_XTRACEOPTIONS_CUSTOM = r"^custom-[^\s]*$"
_XTRACEOPTIONS_CUSTOM_RE = re.compile(_XTRACEOPTIONS_CUSTOM)
Expand All @@ -25,11 +30,13 @@ def __init__(
self,
xtraceoptions_header: str = "",
signature_header: str = "",
):
) -> None:
"""
Args:
xtraceoptions_header: extracted request header value
signature_header: extracted request header value
Initialize XTraceOptions from request headers.

Parameters:
xtraceoptions_header (str): X-Trace-Options header value. Defaults to "".
signature_header (str): X-Trace-Options-Signature header value. Defaults to "".
"""
self.ignored = []
self.options_header = ""
Expand Down