From 9029ce25b0806e96c8edd762f37224ff1296fef8 Mon Sep 17 00:00:00 2001 From: Munir Abdinur Date: Fri, 17 Jan 2025 14:25:41 -0500 Subject: [PATCH 1/6] chore(trace_utils): move implementation details to internal --- ddtrace/_trace/trace_handlers.py | 2 +- ddtrace/_trace/utils_redis.py | 2 +- ddtrace/appsec/_handlers.py | 4 +- ddtrace/appsec/_processor.py | 2 +- ddtrace/appsec/_utils.py | 2 +- ddtrace/contrib/__init__.py | 15 + ddtrace/contrib/internal/aioredis/patch.py | 2 +- ddtrace/contrib/internal/aredis/patch.py | 2 +- ddtrace/contrib/internal/django/patch.py | 2 +- ddtrace/contrib/internal/mysql/patch.py | 2 +- ddtrace/contrib/internal/mysqldb/patch.py | 2 +- ddtrace/contrib/internal/pymysql/patch.py | 2 +- .../contrib/internal/redis/asyncio_patch.py | 2 +- ddtrace/contrib/internal/redis_utils.py | 84 ++ ddtrace/contrib/internal/trace_utils.py | 707 +++++++++++++++++ ddtrace/contrib/internal/trace_utils_async.py | 39 + ddtrace/contrib/internal/yaaredis/patch.py | 2 +- ddtrace/contrib/redis_utils.py | 91 +-- ddtrace/contrib/trace_utils.py | 743 +----------------- ddtrace/contrib/trace_utils_async.py | 46 +- ddtrace/contrib/trace_utils_redis.py | 9 +- tests/tracer/test_trace_utils.py | 2 +- 22 files changed, 922 insertions(+), 842 deletions(-) create mode 100644 ddtrace/contrib/internal/redis_utils.py create mode 100644 ddtrace/contrib/internal/trace_utils.py create mode 100644 ddtrace/contrib/internal/trace_utils_async.py diff --git a/ddtrace/_trace/trace_handlers.py b/ddtrace/_trace/trace_handlers.py index 7c2ba02d6b4..96c33da8c6b 100644 --- a/ddtrace/_trace/trace_handlers.py +++ b/ddtrace/_trace/trace_handlers.py @@ -22,7 +22,7 @@ from ddtrace.constants import SPAN_MEASURED_KEY from ddtrace.contrib import trace_utils from ddtrace.contrib.internal.botocore.constants import BOTOCORE_STEPFUNCTIONS_INPUT_KEY -from ddtrace.contrib.trace_utils import _set_url_tag +from ddtrace.contrib.internal.trace_utils import _set_url_tag from ddtrace.ext import SpanKind from ddtrace.ext import db from ddtrace.ext import http diff --git a/ddtrace/_trace/utils_redis.py b/ddtrace/_trace/utils_redis.py index 0ea1388e255..433d8578fbb 100644 --- a/ddtrace/_trace/utils_redis.py +++ b/ddtrace/_trace/utils_redis.py @@ -10,7 +10,7 @@ from ddtrace.constants import SPAN_KIND from ddtrace.constants import SPAN_MEASURED_KEY from ddtrace.contrib import trace_utils -from ddtrace.contrib.redis_utils import _extract_conn_tags +from ddtrace.contrib.internal.redis_utils import _extract_conn_tags from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes from ddtrace.ext import db diff --git a/ddtrace/appsec/_handlers.py b/ddtrace/appsec/_handlers.py index 42464f9fe40..d34b2da3517 100644 --- a/ddtrace/appsec/_handlers.py +++ b/ddtrace/appsec/_handlers.py @@ -6,8 +6,8 @@ from ddtrace.appsec._asm_request_context import get_blocked from ddtrace.appsec._constants import SPAN_DATA_NAMES from ddtrace.contrib import trace_utils -from ddtrace.contrib.trace_utils import _get_request_header_user_agent -from ddtrace.contrib.trace_utils import _set_url_tag +from ddtrace.contrib.internal.trace_utils import _get_request_header_user_agent +from ddtrace.contrib.internal.trace_utils import _set_url_tag from ddtrace.ext import SpanTypes from ddtrace.ext import http from ddtrace.internal import core diff --git a/ddtrace/appsec/_processor.py b/ddtrace/appsec/_processor.py index 030399f8a50..b9ed07c7959 100644 --- a/ddtrace/appsec/_processor.py +++ b/ddtrace/appsec/_processor.py @@ -106,7 +106,7 @@ def get_rules() -> str: def _set_headers(span: Span, headers: Any, kind: str, only_asm_enabled: bool = False) -> None: - from ddtrace.contrib.trace_utils import _normalize_tag_name + from ddtrace.contrib.internal.trace_utils import _normalize_tag_name for k in headers: if isinstance(k, tuple): diff --git a/ddtrace/appsec/_utils.py b/ddtrace/appsec/_utils.py index bb8739654c5..79f8f8b5311 100644 --- a/ddtrace/appsec/_utils.py +++ b/ddtrace/appsec/_utils.py @@ -22,7 +22,7 @@ def parse_response_body(raw_body): from ddtrace.appsec import _asm_request_context from ddtrace.appsec._constants import SPAN_DATA_NAMES - from ddtrace.contrib.trace_utils import _get_header_value_case_insensitive + from ddtrace.contrib.internal.trace_utils import _get_header_value_case_insensitive if not raw_body: return diff --git a/ddtrace/contrib/__init__.py b/ddtrace/contrib/__init__.py index 1c75b9d0e44..5ed52efa04e 100644 --- a/ddtrace/contrib/__init__.py +++ b/ddtrace/contrib/__init__.py @@ -1,4 +1,19 @@ from ddtrace._trace import trace_handlers # noqa:F401 +from ddtrace.internal.utils.deprecations import DDTraceDeprecationWarning from ddtrace.internal.utils.importlib import func_name # noqa:F401 from ddtrace.internal.utils.importlib import module_name # noqa:F401 from ddtrace.internal.utils.importlib import require_modules # noqa:F401 +from ddtrace.vendor.debtcollector import deprecate + + +def __getattr__(name): + if name in ("trace_handlers", "func_name", "module_name", "require_modules"): + deprecate( + ("%s.%s is deprecated" % (__name__, name)), + category=DDTraceDeprecationWarning, + removal_version="3.0.0", + ) + + if name in globals(): + return globals()[name] + raise AttributeError("%s has no attribute %s", __name__, name) diff --git a/ddtrace/contrib/internal/aioredis/patch.py b/ddtrace/contrib/internal/aioredis/patch.py index dc6004b9caa..fbadc9d5535 100644 --- a/ddtrace/contrib/internal/aioredis/patch.py +++ b/ddtrace/contrib/internal/aioredis/patch.py @@ -13,7 +13,7 @@ from ddtrace.constants import SPAN_MEASURED_KEY from ddtrace.contrib import trace_utils from ddtrace.contrib.redis_utils import ROW_RETURNING_COMMANDS -from ddtrace.contrib.redis_utils import _run_redis_command_async +from ddtrace.contrib.internal.redis_utils import _run_redis_command_async from ddtrace.contrib.redis_utils import determine_row_count from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes diff --git a/ddtrace/contrib/internal/aredis/patch.py b/ddtrace/contrib/internal/aredis/patch.py index bd8c5b4c750..4374f98c601 100644 --- a/ddtrace/contrib/internal/aredis/patch.py +++ b/ddtrace/contrib/internal/aredis/patch.py @@ -6,7 +6,7 @@ from ddtrace import config from ddtrace._trace.utils_redis import _instrument_redis_cmd from ddtrace._trace.utils_redis import _instrument_redis_execute_pipeline -from ddtrace.contrib.redis_utils import _run_redis_command_async +from ddtrace.contrib.internal.redis_utils import _run_redis_command_async from ddtrace.internal.schema import schematize_service_name from ddtrace.internal.utils.formats import CMD_MAX_LEN from ddtrace.internal.utils.formats import asbool diff --git a/ddtrace/contrib/internal/django/patch.py b/ddtrace/contrib/internal/django/patch.py index 8bc523dd1c1..a55bab550cd 100644 --- a/ddtrace/contrib/internal/django/patch.py +++ b/ddtrace/contrib/internal/django/patch.py @@ -23,7 +23,7 @@ from ddtrace.constants import SPAN_KIND from ddtrace.contrib import dbapi from ddtrace.contrib import trace_utils -from ddtrace.contrib.trace_utils import _get_request_header_user_agent +from ddtrace.contrib.internal.trace_utils import _get_request_header_user_agent from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes from ddtrace.ext import db diff --git a/ddtrace/contrib/internal/mysql/patch.py b/ddtrace/contrib/internal/mysql/patch.py index d18d357d107..6425bd33766 100644 --- a/ddtrace/contrib/internal/mysql/patch.py +++ b/ddtrace/contrib/internal/mysql/patch.py @@ -7,7 +7,7 @@ from ddtrace.appsec._iast._metrics import _set_metric_iast_instrumented_sink from ddtrace.appsec._iast.constants import VULN_SQL_INJECTION from ddtrace.contrib.dbapi import TracedConnection -from ddtrace.contrib.trace_utils import _convert_to_string +from ddtrace.contrib.internal.trace_utils import _convert_to_string from ddtrace.ext import db from ddtrace.ext import net from ddtrace.internal.schema import schematize_database_operation diff --git a/ddtrace/contrib/internal/mysqldb/patch.py b/ddtrace/contrib/internal/mysqldb/patch.py index 8b6aa7bb7f2..0294e5a2bc6 100644 --- a/ddtrace/contrib/internal/mysqldb/patch.py +++ b/ddtrace/contrib/internal/mysqldb/patch.py @@ -9,7 +9,7 @@ from ddtrace.constants import SPAN_KIND from ddtrace.constants import SPAN_MEASURED_KEY from ddtrace.contrib.dbapi import TracedConnection -from ddtrace.contrib.trace_utils import _convert_to_string +from ddtrace.contrib.internal.trace_utils import _convert_to_string from ddtrace.contrib.trace_utils import ext_service from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes diff --git a/ddtrace/contrib/internal/pymysql/patch.py b/ddtrace/contrib/internal/pymysql/patch.py index a9a16d50608..eab651bb784 100644 --- a/ddtrace/contrib/internal/pymysql/patch.py +++ b/ddtrace/contrib/internal/pymysql/patch.py @@ -5,7 +5,7 @@ from ddtrace import config from ddtrace.contrib.dbapi import TracedConnection -from ddtrace.contrib.trace_utils import _convert_to_string +from ddtrace.contrib.internal.trace_utils import _convert_to_string from ddtrace.ext import db from ddtrace.ext import net from ddtrace.internal.schema import schematize_database_operation diff --git a/ddtrace/contrib/internal/redis/asyncio_patch.py b/ddtrace/contrib/internal/redis/asyncio_patch.py index 7c5bad354ab..66a0fa8ba4c 100644 --- a/ddtrace/contrib/internal/redis/asyncio_patch.py +++ b/ddtrace/contrib/internal/redis/asyncio_patch.py @@ -2,7 +2,7 @@ from ddtrace._trace.utils_redis import _instrument_redis_cmd from ddtrace._trace.utils_redis import _instrument_redis_execute_async_cluster_pipeline from ddtrace._trace.utils_redis import _instrument_redis_execute_pipeline -from ddtrace.contrib.redis_utils import _run_redis_command_async +from ddtrace.contrib.internal.redis_utils import _run_redis_command_async from ddtrace.internal.utils.formats import stringify_cache_args from ddtrace.trace import Pin diff --git a/ddtrace/contrib/internal/redis_utils.py b/ddtrace/contrib/internal/redis_utils.py new file mode 100644 index 00000000000..63d950389c8 --- /dev/null +++ b/ddtrace/contrib/internal/redis_utils.py @@ -0,0 +1,84 @@ +from typing import Dict +from typing import List +from typing import Optional +from typing import Union + +from ddtrace.ext import net +from ddtrace.ext import redis as redisx +from ddtrace.internal import core +from ddtrace.internal.utils.formats import stringify_cache_args + + +SINGLE_KEY_COMMANDS = [ + "GET", + "GETDEL", + "GETEX", + "GETRANGE", + "GETSET", + "LINDEX", + "LRANGE", + "RPOP", + "LPOP", + "HGET", + "HGETALL", + "HKEYS", + "HMGET", + "HRANDFIELD", + "HVALS", +] +MULTI_KEY_COMMANDS = ["MGET"] +ROW_RETURNING_COMMANDS = SINGLE_KEY_COMMANDS + MULTI_KEY_COMMANDS + + +def _extract_conn_tags(conn_kwargs): + """Transform redis conn info into dogtrace metas""" + try: + conn_tags = { + net.TARGET_HOST: conn_kwargs["host"], + net.TARGET_PORT: conn_kwargs["port"], + net.SERVER_ADDRESS: conn_kwargs["host"], + redisx.DB: conn_kwargs.get("db") or 0, + } + client_name = conn_kwargs.get("client_name") + if client_name: + conn_tags[redisx.CLIENT_NAME] = client_name + return conn_tags + except Exception: + return {} + + +def determine_row_count(redis_command: str, result: Optional[Union[List, Dict, str]]) -> int: + empty_results = [b"", [], {}, None] + # result can be an empty list / dict / string + if result not in empty_results: + if redis_command == "MGET": + # only include valid key results within count + result = [x for x in result if x not in empty_results] + return len(result) + elif redis_command == "HMGET": + # only include valid key results within count + result = [x for x in result if x not in empty_results] + return 1 if len(result) > 0 else 0 + else: + return 1 + else: + return 0 + + +async def _run_redis_command_async(ctx: core.ExecutionContext, func, args, kwargs): + parsed_command = stringify_cache_args(args) + redis_command = parsed_command.split(" ")[0] + rowcount = None + result = None + try: + result = await func(*args, **kwargs) + return result + except BaseException: + rowcount = 0 + raise + finally: + if rowcount is None: + rowcount = determine_row_count(redis_command=redis_command, result=result) + if redis_command not in ROW_RETURNING_COMMANDS: + rowcount = None + core.dispatch("redis.async_command.post", [ctx, rowcount]) diff --git a/ddtrace/contrib/internal/trace_utils.py b/ddtrace/contrib/internal/trace_utils.py new file mode 100644 index 00000000000..56901934e83 --- /dev/null +++ b/ddtrace/contrib/internal/trace_utils.py @@ -0,0 +1,707 @@ +""" +This module contains utility functions for writing ddtrace integrations. +""" + +from collections import deque +import ipaddress +import re +from typing import TYPE_CHECKING # noqa:F401 +from typing import Any # noqa:F401 +from typing import Callable # noqa:F401 +from typing import Dict # noqa:F401 +from typing import Generator # noqa:F401 +from typing import Iterator # noqa:F401 +from typing import List # noqa:F401 +from typing import Mapping # noqa:F401 +from typing import Optional # noqa:F401 +from typing import Tuple # noqa:F401 +from typing import Union # noqa:F401 +from typing import cast # noqa:F401 + +import wrapt + +from ddtrace import config +from ddtrace.ext import http +from ddtrace.ext import net +from ddtrace.ext import user +from ddtrace.internal import core +from ddtrace.internal.compat import ensure_text +from ddtrace.internal.compat import ip_is_global +from ddtrace.internal.compat import parse +from ddtrace.internal.logger import get_logger +from ddtrace.internal.utils.cache import cached +from ddtrace.internal.utils.http import normalize_header_name +from ddtrace.internal.utils.http import redact_url +from ddtrace.internal.utils.http import strip_query_string +import ddtrace.internal.utils.wrappers +from ddtrace.propagation.http import HTTPPropagator +from ddtrace.settings.asm import config as asm_config +from ddtrace.trace import Pin + + +if TYPE_CHECKING: # pragma: no cover + from ddtrace import Span # noqa:F401 + from ddtrace import Tracer # noqa:F401 + from ddtrace.settings import IntegrationConfig # noqa:F401 + + +log = get_logger(__name__) + +wrap = wrapt.wrap_function_wrapper +unwrap = ddtrace.internal.utils.wrappers.unwrap +iswrapped = ddtrace.internal.utils.wrappers.iswrapped + +REQUEST = "request" +RESPONSE = "response" + +# Tag normalization based on: https://docs.datadoghq.com/tagging/#defining-tags +# With the exception of '.' in header names which are replaced with '_' to avoid +# starting a "new object" on the UI. +NORMALIZE_PATTERN = re.compile(r"([^a-z0-9_\-:/]){1}") + +# Possible User Agent header. +USER_AGENT_PATTERNS = ("http-user-agent", "user-agent") + +IP_PATTERNS = ( + "x-forwarded-for", + "x-real-ip", + "true-client-ip", + "x-client-ip", + "forwarded-for", + "x-cluster-client-ip", + "fastly-client-ip", + "cf-connecting-ip", + "cf-connecting-ipv6", +) + + +@cached() +def _normalized_header_name(header_name): + # type: (str) -> str + return NORMALIZE_PATTERN.sub("_", normalize_header_name(header_name)) + + +def _get_header_value_case_insensitive(headers, keyname): + # type: (Mapping[str, str], str) -> Optional[str] + """ + Get a header in a case insensitive way. This function is meant for frameworks + like Django < 2.2 that don't store the headers in a case insensitive mapping. + """ + # just in case we are lucky + shortcut_value = headers.get(keyname) + if shortcut_value is not None: + return shortcut_value + + for key, value in headers.items(): + if key.lower().replace("_", "-") == keyname: + return value + + return None + + +def _normalize_tag_name(request_or_response, header_name): + # type: (str, str) -> str + """ + Given a tag name, e.g. 'Content-Type', returns a corresponding normalized tag name, i.e + 'http.request.headers.content_type'. Rules applied actual header name are: + - any letter is converted to lowercase + - any digit is left unchanged + - any block of any length of different ASCII chars is converted to a single underscore '_' + :param request_or_response: The context of the headers: request|response + :param header_name: The header's name + :type header_name: str + :rtype: str + """ + # Looking at: + # - http://www.iana.org/assignments/message-headers/message-headers.xhtml + # - https://tools.ietf.org/html/rfc6648 + # and for consistency with other language integrations seems safe to assume the following algorithm for header + # names normalization: + # - any letter is converted to lowercase + # - any digit is left unchanged + # - any block of any length of different ASCII chars is converted to a single underscore '_' + normalized_name = _normalized_header_name(header_name) + return "http.{}.headers.{}".format(request_or_response, normalized_name) + + +def _store_headers(headers, span, integration_config, request_or_response): + # type: (Dict[str, str], Span, IntegrationConfig, str) -> None + """ + :param headers: A dict of http headers to be stored in the span + :type headers: dict or list + :param span: The Span instance where tags will be stored + :type span: ddtrace._trace.span.Span + :param integration_config: An integration specific config object. + :type integration_config: ddtrace.settings.IntegrationConfig + """ + if not isinstance(headers, dict): + try: + headers = dict(headers) + except Exception: + return + + if integration_config is None: + log.debug("Skipping headers tracing as no integration config was provided") + return + + for header_name, header_value in headers.items(): + # config._header_tag_name gets an element of the dictionary in config._trace_http_header_tags + # which gets the value from DD_TRACE_HEADER_TAGS environment variable.""" + tag_name = integration_config._header_tag_name(header_name) + if tag_name is None: + continue + # An empty tag defaults to a http..headers.
tag + span.set_tag_str(tag_name or _normalize_tag_name(request_or_response, header_name), header_value) + + +def _get_request_header_user_agent(headers, headers_are_case_sensitive=False): + # type: (Mapping[str, str], bool) -> str + """Get user agent from request headers + :param headers: A dict of http headers to be stored in the span + :type headers: dict or list + """ + for key_pattern in USER_AGENT_PATTERNS: + if not headers_are_case_sensitive: + user_agent = headers.get(key_pattern) + else: + user_agent = _get_header_value_case_insensitive(headers, key_pattern) + + if user_agent: + return user_agent + return "" + + +def _get_request_header_client_ip(headers, peer_ip=None, headers_are_case_sensitive=False): + # type: (Optional[Mapping[str, str]], Optional[str], bool) -> str + + def get_header_value(key): # type: (str) -> Optional[str] + if not headers_are_case_sensitive: + return headers.get(key) + + return _get_header_value_case_insensitive(headers, key) + + if not headers: + try: + _ = ipaddress.ip_address(str(peer_ip)) + except ValueError: + return "" + return peer_ip + + ip_header_value = "" + user_configured_ip_header = config._client_ip_header + if user_configured_ip_header: + # Used selected the header to use to get the IP + ip_header_value = get_header_value( + user_configured_ip_header.lower().replace("_", "-") + if headers_are_case_sensitive + else user_configured_ip_header + ) + if not ip_header_value: + log.debug("DD_TRACE_CLIENT_IP_HEADER configured but '%s' header missing", user_configured_ip_header) + return "" + + try: + _ = ipaddress.ip_address(str(ip_header_value)) + except ValueError: + log.debug("Invalid IP address from configured %s header: %s", user_configured_ip_header, ip_header_value) + return "" + + else: + if headers_are_case_sensitive: + new_headers = {k.lower().replace("_", "-"): v for k, v in headers.items()} + for ip_header in IP_PATTERNS: + if ip_header in new_headers: + ip_header_value = new_headers[ip_header] + break + else: + for ip_header in IP_PATTERNS: + if ip_header in headers: + ip_header_value = headers[ip_header] + break + + private_ip_from_headers = "" + + if ip_header_value: + # At this point, we have one IP header, check its value and retrieve the first public IP + ip_list = ip_header_value.split(",") + for ip in ip_list: + ip = ip.strip() + if not ip: + continue + + try: + if ip_is_global(ip): + return ip + elif not private_ip_from_headers: + # IP is private, store it just in case we don't find a public one later + private_ip_from_headers = ip + except ValueError: # invalid IP + continue + + # At this point we have none or maybe one private ip from the headers: check the peer ip in + # case it's public and, if not, return either the private_ip from the headers (if we have one) + # or the peer private ip + try: + if ip_is_global(peer_ip) or not private_ip_from_headers: + return peer_ip + except ValueError: + pass + + return private_ip_from_headers + + +def _store_request_headers(headers, span, integration_config): + # type: (Dict[str, str], Span, IntegrationConfig) -> None + """ + Store request headers as a span's tags + :param headers: All the request's http headers, will be filtered through the whitelist + :type headers: dict or list + :param span: The Span instance where tags will be stored + :type span: ddtrace.Span + :param integration_config: An integration specific config object. + :type integration_config: ddtrace.settings.IntegrationConfig + """ + _store_headers(headers, span, integration_config, REQUEST) + + +def _store_response_headers(headers, span, integration_config): + # type: (Dict[str, str], Span, IntegrationConfig) -> None + """ + Store response headers as a span's tags + :param headers: All the response's http headers, will be filtered through the whitelist + :type headers: dict or list + :param span: The Span instance where tags will be stored + :type span: ddtrace.Span + :param integration_config: An integration specific config object. + :type integration_config: ddtrace.settings.IntegrationConfig + """ + _store_headers(headers, span, integration_config, RESPONSE) + + +def _sanitized_url(url): + # type: (str) -> str + """ + Sanitize url by removing parts with potential auth info + """ + if "@" in url: + parsed = parse.urlparse(url) + netloc = parsed.netloc + + if "@" not in netloc: + # Safe url, `@` not in netloc + return url + + netloc = netloc[netloc.index("@") + 1 :] + return parse.urlunparse( + ( + parsed.scheme, + netloc, + parsed.path, + "", + parsed.query, + "", + ) + ) + + return url + + +def with_traced_module(func): + """Helper for providing tracing essentials (module and pin) for tracing + wrappers. + + This helper enables tracing wrappers to dynamically be disabled when the + corresponding pin is disabled. + + Usage:: + + @with_traced_module + def my_traced_wrapper(django, pin, func, instance, args, kwargs): + # Do tracing stuff + pass + + def patch(): + import django + wrap(django.somefunc, my_traced_wrapper(django)) + """ + + def with_mod(mod): + def wrapper(wrapped, instance, args, kwargs): + pin = Pin._find(instance, mod) + if pin and not pin.enabled(): + return wrapped(*args, **kwargs) + elif not pin: + log.debug("Pin not found for traced method %r", wrapped) + return wrapped(*args, **kwargs) + return func(mod, pin, wrapped, instance, args, kwargs) + + return wrapper + + return with_mod + + +def distributed_tracing_enabled(int_config, default=False): + # type: (IntegrationConfig, bool) -> bool + """Returns whether distributed tracing is enabled for this integration config""" + if "distributed_tracing_enabled" in int_config and int_config.distributed_tracing_enabled is not None: + return int_config.distributed_tracing_enabled + elif "distributed_tracing" in int_config and int_config.distributed_tracing is not None: + return int_config.distributed_tracing + return default + + +def int_service(pin, int_config, default=None): + # type: (Optional[Pin], IntegrationConfig, Optional[str]) -> Optional[str] + """Returns the service name for an integration which is internal + to the application. Internal meaning that the work belongs to the + user's application. Eg. Web framework, sqlalchemy, web servers. + + For internal integrations we prioritize overrides, then global defaults and + lastly the default provided by the integration. + """ + # Pin has top priority since it is user defined in code + if pin is not None and pin.service: + return pin.service + + # Config is next since it is also configured via code + # Note that both service and service_name are used by + # integrations. + if "service" in int_config and int_config.service is not None: + return cast(str, int_config.service) + if "service_name" in int_config and int_config.service_name is not None: + return cast(str, int_config.service_name) + + global_service = int_config.global_config._get_service() + # We check if global_service != _inferred_base_service since global service (config.service) + # defaults to _inferred_base_service when no DD_SERVICE is set. In this case, we want to not + # use the inferred base service value, and instead use the integration default service. If we + # didn't do this, we would have a massive breaking change from adding inferred_base_service. + if global_service and global_service != int_config.global_config._inferred_base_service: + return cast(str, global_service) + + if "_default_service" in int_config and int_config._default_service is not None: + return cast(str, int_config._default_service) + + if default is None and global_service: + return cast(str, global_service) + + return default + + +def ext_service(pin, int_config, default=None): + # type: (Optional[Pin], IntegrationConfig, Optional[str]) -> Optional[str] + """Returns the service name for an integration which is external + to the application. External meaning that the integration generates + spans wrapping code that is outside the scope of the user's application. Eg. A database, RPC, cache, etc. + """ + if pin is not None and pin.service: + return pin.service + + if "service" in int_config and int_config.service is not None: + return cast(str, int_config.service) + if "service_name" in int_config and int_config.service_name is not None: + return cast(str, int_config.service_name) + + if "_default_service" in int_config and int_config._default_service is not None: + return cast(str, int_config._default_service) + + # A default is required since it's an external service. + return default + + +def _set_url_tag(integration_config, span, url, query): + # type: (IntegrationConfig, Span, str, str) -> None + if not integration_config.http_tag_query_string: + span.set_tag_str(http.URL, strip_query_string(url)) + elif config._global_query_string_obfuscation_disabled: + # TODO(munir): This case exists for backwards compatibility. To remove query strings from URLs, + # users should set ``DD_TRACE_HTTP_CLIENT_TAG_QUERY_STRING=False``. This case should be + # removed when config.global_query_string_obfuscation_disabled is removed (v3.0). + span.set_tag_str(http.URL, url) + elif getattr(config._obfuscation_query_string_pattern, "pattern", None) == b"": + # obfuscation is disabled when DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP="" + span.set_tag_str(http.URL, strip_query_string(url)) + else: + span.set_tag_str(http.URL, redact_url(url, config._obfuscation_query_string_pattern, query)) + + +def set_http_meta( + span, # type: Span + integration_config, # type: IntegrationConfig + method=None, # type: Optional[str] + url=None, # type: Optional[str] + target_host=None, # type: Optional[str] + server_address=None, # type: Optional[str] + status_code=None, # type: Optional[Union[int, str]] + status_msg=None, # type: Optional[str] + query=None, # type: Optional[str] + parsed_query=None, # type: Optional[Mapping[str, str]] + request_headers=None, # type: Optional[Mapping[str, str]] + response_headers=None, # type: Optional[Mapping[str, str]] + retries_remain=None, # type: Optional[Union[int, str]] + raw_uri=None, # type: Optional[str] + request_cookies=None, # type: Optional[Dict[str, str]] + request_path_params=None, # type: Optional[Dict[str, str]] + request_body=None, # type: Optional[Union[str, Dict[str, List[str]]]] + peer_ip=None, # type: Optional[str] + headers_are_case_sensitive=False, # type: bool + route=None, # type: Optional[str] + response_cookies=None, # type: Optional[Dict[str, str]] +): + # type: (...) -> None + """ + Set HTTP metas on the span + + :param method: the HTTP method + :param url: the HTTP URL + :param status_code: the HTTP status code + :param status_msg: the HTTP status message + :param query: the HTTP query part of the URI as a string + :param parsed_query: the HTTP query part of the URI as parsed by the framework and forwarded to the user code + :param request_headers: the HTTP request headers + :param response_headers: the HTTP response headers + :param raw_uri: the full raw HTTP URI (including ports and query) + :param request_cookies: the HTTP request cookies as a dict + :param request_path_params: the parameters of the HTTP URL as set by the framework: /posts/ would give us + { "id": } + """ + if method is not None: + span.set_tag_str(http.METHOD, method) + + if url is not None: + url = _sanitized_url(url) + _set_url_tag(integration_config, span, url, query) + + if target_host is not None: + span.set_tag_str(net.TARGET_HOST, target_host) + + if server_address is not None: + span.set_tag_str(net.SERVER_ADDRESS, server_address) + + if status_code is not None: + try: + int_status_code = int(status_code) + except (TypeError, ValueError): + log.debug("failed to convert http status code %r to int", status_code) + else: + span.set_tag_str(http.STATUS_CODE, str(status_code)) + if config.http_server.is_error_code(int_status_code): + span.error = 1 + + if status_msg is not None: + span.set_tag_str(http.STATUS_MSG, status_msg) + + if query is not None and integration_config.trace_query_string: + span.set_tag_str(http.QUERY_STRING, query) + + request_ip = peer_ip + if request_headers: + user_agent = _get_request_header_user_agent(request_headers, headers_are_case_sensitive) + if user_agent: + span.set_tag_str(http.USER_AGENT, user_agent) + + # We always collect the IP if appsec is enabled to report it on potential vulnerabilities. + # https://datadoghq.atlassian.net/wiki/spaces/APS/pages/2118779066/Client+IP+addresses+resolution + if asm_config._asm_enabled or config._retrieve_client_ip: + # Retrieve the IP if it was calculated on AppSecProcessor.on_span_start + request_ip = core.get_item("http.request.remote_ip", span=span) + + if not request_ip: + # Not calculated: framework does not support IP blocking or testing env + request_ip = ( + _get_request_header_client_ip(request_headers, peer_ip, headers_are_case_sensitive) or peer_ip + ) + + if request_ip: + span.set_tag_str(http.CLIENT_IP, request_ip) + span.set_tag_str("network.client.ip", request_ip) + + if integration_config.is_header_tracing_configured: + """We should store both http..headers. and + http.. The last one + is the DD standardized tag for user-agent""" + _store_request_headers(dict(request_headers), span, integration_config) + + if response_headers is not None and integration_config.is_header_tracing_configured: + _store_response_headers(dict(response_headers), span, integration_config) + + if retries_remain is not None: + span.set_tag_str(http.RETRIES_REMAIN, str(retries_remain)) + + core.dispatch( + "set_http_meta_for_asm", + [ + span, + request_ip, + raw_uri, + route, + method, + request_headers, + request_cookies, + parsed_query, + request_path_params, + request_body, + status_code, + response_headers, + response_cookies, + ], + ) + + if route is not None: + span.set_tag_str(http.ROUTE, route) + + +def activate_distributed_headers(tracer, int_config=None, request_headers=None, override=None): + # type: (Tracer, Optional[IntegrationConfig], Optional[Dict[str, str]], Optional[bool]) -> None + """ + Helper for activating a distributed trace headers' context if enabled in integration config. + int_config will be used to check if distributed trace headers context will be activated, but + override will override whatever value is set in int_config if passed any value other than None. + """ + if override is False: + return None + + if override or (int_config and distributed_tracing_enabled(int_config)): + context = HTTPPropagator.extract(request_headers) + + # Only need to activate the new context if something was propagated + # The new context must have one of these values in order for it to be activated + if not context.trace_id and not context._baggage and not context._span_links: + return None + # Do not reactivate a context with the same trace id + # DEV: An example could be nested web frameworks, when one layer already + # parsed request headers and activated them. + # + # Example:: + # + # app = Flask(__name__) # Traced via Flask instrumentation + # app = DDWSGIMiddleware(app) # Extra layer on top for WSGI + current_context = tracer.current_trace_context() + + # We accept incoming contexts with only baggage or only span_links, however if we + # already have a current_context then an incoming context not + # containing a trace_id or containing the same trace_id + # should not be activated. + if current_context and ( + not context.trace_id or (context.trace_id and context.trace_id == current_context.trace_id) + ): + log.debug( + "will not activate extracted Context(trace_id=%r, span_id=%r), a context with that trace id is already active", # noqa: E501 + context.trace_id, + context.span_id, + ) + return None + + # We have parsed a trace id from headers, and we do not already + # have a context with the same trace id active + tracer.context_provider.activate(context) + + +def _flatten( + obj, # type: Any + sep=".", # type: str + prefix="", # type: str + exclude_policy=None, # type: Optional[Callable[[str], bool]] +): + # type: (...) -> Generator[Tuple[str, Any], None, None] + s = deque() # type: ignore + s.append((prefix, obj)) + while s: + p, v = s.pop() + if exclude_policy is not None and exclude_policy(p): + continue + if isinstance(v, dict): + s.extend((sep.join((p, k)) if p else k, v) for k, v in v.items()) + else: + yield p, v + + +def set_flattened_tags( + span, # type: Span + items, # type: Iterator[Tuple[str, Any]] + sep=".", # type: str + exclude_policy=None, # type: Optional[Callable[[str], bool]] + processor=None, # type: Optional[Callable[[Any], Any]] +): + # type: (...) -> None + for prefix, value in items: + for tag, v in _flatten(value, sep, prefix, exclude_policy): + span.set_tag(tag, processor(v) if processor is not None else v) + + +def set_user( + tracer, # type: Tracer + user_id, # type: str + name=None, # type: Optional[str] + email=None, # type: Optional[str] + scope=None, # type: Optional[str] + role=None, # type: Optional[str] + session_id=None, # type: Optional[str] + propagate=False, # type bool + span=None, # type: Optional[Span] +): + # type: (...) -> None + """Set user tags. + https://docs.datadoghq.com/logs/log_configuration/attributes_naming_convention/#user-related-attributes + https://docs.datadoghq.com/security_platform/application_security/setup_and_configure/?tab=set_tag&code-lang=python + """ + if span is None: + span = tracer.current_root_span() + if span: + if user_id: + str_user_id = str(user_id) + span.set_tag_str(user.ID, str_user_id) + if propagate: + span.context.dd_user_id = str_user_id + + # All other fields are optional + if name: + span.set_tag_str(user.NAME, name) + if email: + span.set_tag_str(user.EMAIL, email) + if scope: + span.set_tag_str(user.SCOPE, scope) + if role: + span.set_tag_str(user.ROLE, role) + if session_id: + span.set_tag_str(user.SESSION_ID, session_id) + + if asm_config._asm_enabled: + exc = core.dispatch_with_results("set_user_for_asm", [tracer, user_id]).block_user.exception + if exc: + raise exc + + else: + log.warning( + "No root span in the current execution. Skipping set_user tags. " + "See https://docs.datadoghq.com/security_platform/application_security/setup_and_configure/" + "?tab=set_user&code-lang=python for more information.", + ) + + +def extract_netloc_and_query_info_from_url(url): + # type: (str) -> Tuple[str, str] + parse_result = parse.urlparse(url) + query = parse_result.query + + # Relative URLs don't have a netloc, so we force them + if not parse_result.netloc: + parse_result = parse.urlparse("//{url}".format(url=url)) + + netloc = parse_result.netloc.split("@", 1)[-1] # Discard auth info + netloc = netloc.split(":", 1)[0] # Discard port information + return netloc, query + + +class InterruptException(Exception): + pass + + +def _convert_to_string(attr): + # ensures attribute is converted to a string + if attr: + if isinstance(attr, int) or isinstance(attr, float): + return str(attr) + else: + return ensure_text(attr) + return attr diff --git a/ddtrace/contrib/internal/trace_utils_async.py b/ddtrace/contrib/internal/trace_utils_async.py new file mode 100644 index 00000000000..f58cc4e34bb --- /dev/null +++ b/ddtrace/contrib/internal/trace_utils_async.py @@ -0,0 +1,39 @@ +""" +async tracing utils + +Note that this module should only be imported in Python 3.5+. +""" +from ddtrace.internal.logger import get_logger +from ddtrace.trace import Pin + + +log = get_logger(__name__) + + +def with_traced_module(func): + """Async version of trace_utils.with_traced_module. + Usage:: + + @with_traced_module + async def my_traced_wrapper(django, pin, func, instance, args, kwargs): + # Do tracing stuff + pass + + def patch(): + import django + wrap(django.somefunc, my_traced_wrapper(django)) + """ + + def with_mod(mod): + async def wrapper(wrapped, instance, args, kwargs): + pin = Pin._find(instance, mod) + if pin and not pin.enabled(): + return await wrapped(*args, **kwargs) + elif not pin: + log.debug("Pin not found for traced method %r", wrapped) + return await wrapped(*args, **kwargs) + return await func(mod, pin, wrapped, instance, args, kwargs) + + return wrapper + + return with_mod diff --git a/ddtrace/contrib/internal/yaaredis/patch.py b/ddtrace/contrib/internal/yaaredis/patch.py index 58c5a47bda4..f9cba77b5bb 100644 --- a/ddtrace/contrib/internal/yaaredis/patch.py +++ b/ddtrace/contrib/internal/yaaredis/patch.py @@ -6,7 +6,7 @@ from ddtrace import config from ddtrace._trace.utils_redis import _instrument_redis_cmd from ddtrace._trace.utils_redis import _instrument_redis_execute_pipeline -from ddtrace.contrib.redis_utils import _run_redis_command_async +from ddtrace.contrib.internal.redis_utils import _run_redis_command_async from ddtrace.internal.schema import schematize_service_name from ddtrace.internal.utils.deprecations import DDTraceDeprecationWarning from ddtrace.internal.utils.formats import CMD_MAX_LEN diff --git a/ddtrace/contrib/redis_utils.py b/ddtrace/contrib/redis_utils.py index 63d950389c8..3e743273d41 100644 --- a/ddtrace/contrib/redis_utils.py +++ b/ddtrace/contrib/redis_utils.py @@ -1,84 +1,11 @@ -from typing import Dict -from typing import List -from typing import Optional -from typing import Union +from ddtrace.contrib.internal.redis_utils import * # noqa: F403 +from ddtrace.internal.utils.deprecations import DDTraceDeprecationWarning +from ddtrace.vendor.debtcollector import deprecate -from ddtrace.ext import net -from ddtrace.ext import redis as redisx -from ddtrace.internal import core -from ddtrace.internal.utils.formats import stringify_cache_args - -SINGLE_KEY_COMMANDS = [ - "GET", - "GETDEL", - "GETEX", - "GETRANGE", - "GETSET", - "LINDEX", - "LRANGE", - "RPOP", - "LPOP", - "HGET", - "HGETALL", - "HKEYS", - "HMGET", - "HRANDFIELD", - "HVALS", -] -MULTI_KEY_COMMANDS = ["MGET"] -ROW_RETURNING_COMMANDS = SINGLE_KEY_COMMANDS + MULTI_KEY_COMMANDS - - -def _extract_conn_tags(conn_kwargs): - """Transform redis conn info into dogtrace metas""" - try: - conn_tags = { - net.TARGET_HOST: conn_kwargs["host"], - net.TARGET_PORT: conn_kwargs["port"], - net.SERVER_ADDRESS: conn_kwargs["host"], - redisx.DB: conn_kwargs.get("db") or 0, - } - client_name = conn_kwargs.get("client_name") - if client_name: - conn_tags[redisx.CLIENT_NAME] = client_name - return conn_tags - except Exception: - return {} - - -def determine_row_count(redis_command: str, result: Optional[Union[List, Dict, str]]) -> int: - empty_results = [b"", [], {}, None] - # result can be an empty list / dict / string - if result not in empty_results: - if redis_command == "MGET": - # only include valid key results within count - result = [x for x in result if x not in empty_results] - return len(result) - elif redis_command == "HMGET": - # only include valid key results within count - result = [x for x in result if x not in empty_results] - return 1 if len(result) > 0 else 0 - else: - return 1 - else: - return 0 - - -async def _run_redis_command_async(ctx: core.ExecutionContext, func, args, kwargs): - parsed_command = stringify_cache_args(args) - redis_command = parsed_command.split(" ")[0] - rowcount = None - result = None - try: - result = await func(*args, **kwargs) - return result - except BaseException: - rowcount = 0 - raise - finally: - if rowcount is None: - rowcount = determine_row_count(redis_command=redis_command, result=result) - if redis_command not in ROW_RETURNING_COMMANDS: - rowcount = None - core.dispatch("redis.async_command.post", [ctx, rowcount]) +deprecate( + "The ddtrace.contrib.redis_utils module is deprecated", + message="Import from ``ddtrace.contrib.trace_utils`` instead.", + category=DDTraceDeprecationWarning, + removal_version="3.0.0", +) diff --git a/ddtrace/contrib/trace_utils.py b/ddtrace/contrib/trace_utils.py index 56901934e83..c1d2054d23e 100644 --- a/ddtrace/contrib/trace_utils.py +++ b/ddtrace/contrib/trace_utils.py @@ -1,707 +1,42 @@ -""" -This module contains utility functions for writing ddtrace integrations. -""" - -from collections import deque -import ipaddress -import re -from typing import TYPE_CHECKING # noqa:F401 -from typing import Any # noqa:F401 -from typing import Callable # noqa:F401 -from typing import Dict # noqa:F401 -from typing import Generator # noqa:F401 -from typing import Iterator # noqa:F401 -from typing import List # noqa:F401 -from typing import Mapping # noqa:F401 -from typing import Optional # noqa:F401 -from typing import Tuple # noqa:F401 -from typing import Union # noqa:F401 -from typing import cast # noqa:F401 - -import wrapt - -from ddtrace import config -from ddtrace.ext import http -from ddtrace.ext import net -from ddtrace.ext import user -from ddtrace.internal import core -from ddtrace.internal.compat import ensure_text -from ddtrace.internal.compat import ip_is_global -from ddtrace.internal.compat import parse -from ddtrace.internal.logger import get_logger -from ddtrace.internal.utils.cache import cached -from ddtrace.internal.utils.http import normalize_header_name -from ddtrace.internal.utils.http import redact_url -from ddtrace.internal.utils.http import strip_query_string -import ddtrace.internal.utils.wrappers -from ddtrace.propagation.http import HTTPPropagator -from ddtrace.settings.asm import config as asm_config -from ddtrace.trace import Pin - - -if TYPE_CHECKING: # pragma: no cover - from ddtrace import Span # noqa:F401 - from ddtrace import Tracer # noqa:F401 - from ddtrace.settings import IntegrationConfig # noqa:F401 - - -log = get_logger(__name__) - -wrap = wrapt.wrap_function_wrapper -unwrap = ddtrace.internal.utils.wrappers.unwrap -iswrapped = ddtrace.internal.utils.wrappers.iswrapped - -REQUEST = "request" -RESPONSE = "response" - -# Tag normalization based on: https://docs.datadoghq.com/tagging/#defining-tags -# With the exception of '.' in header names which are replaced with '_' to avoid -# starting a "new object" on the UI. -NORMALIZE_PATTERN = re.compile(r"([^a-z0-9_\-:/]){1}") - -# Possible User Agent header. -USER_AGENT_PATTERNS = ("http-user-agent", "user-agent") - -IP_PATTERNS = ( - "x-forwarded-for", - "x-real-ip", - "true-client-ip", - "x-client-ip", - "forwarded-for", - "x-cluster-client-ip", - "fastly-client-ip", - "cf-connecting-ip", - "cf-connecting-ipv6", -) - - -@cached() -def _normalized_header_name(header_name): - # type: (str) -> str - return NORMALIZE_PATTERN.sub("_", normalize_header_name(header_name)) - - -def _get_header_value_case_insensitive(headers, keyname): - # type: (Mapping[str, str], str) -> Optional[str] - """ - Get a header in a case insensitive way. This function is meant for frameworks - like Django < 2.2 that don't store the headers in a case insensitive mapping. - """ - # just in case we are lucky - shortcut_value = headers.get(keyname) - if shortcut_value is not None: - return shortcut_value - - for key, value in headers.items(): - if key.lower().replace("_", "-") == keyname: - return value - - return None - - -def _normalize_tag_name(request_or_response, header_name): - # type: (str, str) -> str - """ - Given a tag name, e.g. 'Content-Type', returns a corresponding normalized tag name, i.e - 'http.request.headers.content_type'. Rules applied actual header name are: - - any letter is converted to lowercase - - any digit is left unchanged - - any block of any length of different ASCII chars is converted to a single underscore '_' - :param request_or_response: The context of the headers: request|response - :param header_name: The header's name - :type header_name: str - :rtype: str - """ - # Looking at: - # - http://www.iana.org/assignments/message-headers/message-headers.xhtml - # - https://tools.ietf.org/html/rfc6648 - # and for consistency with other language integrations seems safe to assume the following algorithm for header - # names normalization: - # - any letter is converted to lowercase - # - any digit is left unchanged - # - any block of any length of different ASCII chars is converted to a single underscore '_' - normalized_name = _normalized_header_name(header_name) - return "http.{}.headers.{}".format(request_or_response, normalized_name) - - -def _store_headers(headers, span, integration_config, request_or_response): - # type: (Dict[str, str], Span, IntegrationConfig, str) -> None - """ - :param headers: A dict of http headers to be stored in the span - :type headers: dict or list - :param span: The Span instance where tags will be stored - :type span: ddtrace._trace.span.Span - :param integration_config: An integration specific config object. - :type integration_config: ddtrace.settings.IntegrationConfig - """ - if not isinstance(headers, dict): - try: - headers = dict(headers) - except Exception: - return - - if integration_config is None: - log.debug("Skipping headers tracing as no integration config was provided") - return - - for header_name, header_value in headers.items(): - # config._header_tag_name gets an element of the dictionary in config._trace_http_header_tags - # which gets the value from DD_TRACE_HEADER_TAGS environment variable.""" - tag_name = integration_config._header_tag_name(header_name) - if tag_name is None: - continue - # An empty tag defaults to a http..headers.
tag - span.set_tag_str(tag_name or _normalize_tag_name(request_or_response, header_name), header_value) - - -def _get_request_header_user_agent(headers, headers_are_case_sensitive=False): - # type: (Mapping[str, str], bool) -> str - """Get user agent from request headers - :param headers: A dict of http headers to be stored in the span - :type headers: dict or list - """ - for key_pattern in USER_AGENT_PATTERNS: - if not headers_are_case_sensitive: - user_agent = headers.get(key_pattern) - else: - user_agent = _get_header_value_case_insensitive(headers, key_pattern) - - if user_agent: - return user_agent - return "" - - -def _get_request_header_client_ip(headers, peer_ip=None, headers_are_case_sensitive=False): - # type: (Optional[Mapping[str, str]], Optional[str], bool) -> str - - def get_header_value(key): # type: (str) -> Optional[str] - if not headers_are_case_sensitive: - return headers.get(key) - - return _get_header_value_case_insensitive(headers, key) - - if not headers: - try: - _ = ipaddress.ip_address(str(peer_ip)) - except ValueError: - return "" - return peer_ip - - ip_header_value = "" - user_configured_ip_header = config._client_ip_header - if user_configured_ip_header: - # Used selected the header to use to get the IP - ip_header_value = get_header_value( - user_configured_ip_header.lower().replace("_", "-") - if headers_are_case_sensitive - else user_configured_ip_header - ) - if not ip_header_value: - log.debug("DD_TRACE_CLIENT_IP_HEADER configured but '%s' header missing", user_configured_ip_header) - return "" - - try: - _ = ipaddress.ip_address(str(ip_header_value)) - except ValueError: - log.debug("Invalid IP address from configured %s header: %s", user_configured_ip_header, ip_header_value) - return "" - - else: - if headers_are_case_sensitive: - new_headers = {k.lower().replace("_", "-"): v for k, v in headers.items()} - for ip_header in IP_PATTERNS: - if ip_header in new_headers: - ip_header_value = new_headers[ip_header] - break - else: - for ip_header in IP_PATTERNS: - if ip_header in headers: - ip_header_value = headers[ip_header] - break - - private_ip_from_headers = "" - - if ip_header_value: - # At this point, we have one IP header, check its value and retrieve the first public IP - ip_list = ip_header_value.split(",") - for ip in ip_list: - ip = ip.strip() - if not ip: - continue - - try: - if ip_is_global(ip): - return ip - elif not private_ip_from_headers: - # IP is private, store it just in case we don't find a public one later - private_ip_from_headers = ip - except ValueError: # invalid IP - continue - - # At this point we have none or maybe one private ip from the headers: check the peer ip in - # case it's public and, if not, return either the private_ip from the headers (if we have one) - # or the peer private ip - try: - if ip_is_global(peer_ip) or not private_ip_from_headers: - return peer_ip - except ValueError: - pass - - return private_ip_from_headers - - -def _store_request_headers(headers, span, integration_config): - # type: (Dict[str, str], Span, IntegrationConfig) -> None - """ - Store request headers as a span's tags - :param headers: All the request's http headers, will be filtered through the whitelist - :type headers: dict or list - :param span: The Span instance where tags will be stored - :type span: ddtrace.Span - :param integration_config: An integration specific config object. - :type integration_config: ddtrace.settings.IntegrationConfig - """ - _store_headers(headers, span, integration_config, REQUEST) - - -def _store_response_headers(headers, span, integration_config): - # type: (Dict[str, str], Span, IntegrationConfig) -> None - """ - Store response headers as a span's tags - :param headers: All the response's http headers, will be filtered through the whitelist - :type headers: dict or list - :param span: The Span instance where tags will be stored - :type span: ddtrace.Span - :param integration_config: An integration specific config object. - :type integration_config: ddtrace.settings.IntegrationConfig - """ - _store_headers(headers, span, integration_config, RESPONSE) - - -def _sanitized_url(url): - # type: (str) -> str - """ - Sanitize url by removing parts with potential auth info - """ - if "@" in url: - parsed = parse.urlparse(url) - netloc = parsed.netloc - - if "@" not in netloc: - # Safe url, `@` not in netloc - return url - - netloc = netloc[netloc.index("@") + 1 :] - return parse.urlunparse( - ( - parsed.scheme, - netloc, - parsed.path, - "", - parsed.query, - "", - ) - ) - - return url - - -def with_traced_module(func): - """Helper for providing tracing essentials (module and pin) for tracing - wrappers. - - This helper enables tracing wrappers to dynamically be disabled when the - corresponding pin is disabled. - - Usage:: - - @with_traced_module - def my_traced_wrapper(django, pin, func, instance, args, kwargs): - # Do tracing stuff - pass - - def patch(): - import django - wrap(django.somefunc, my_traced_wrapper(django)) - """ - - def with_mod(mod): - def wrapper(wrapped, instance, args, kwargs): - pin = Pin._find(instance, mod) - if pin and not pin.enabled(): - return wrapped(*args, **kwargs) - elif not pin: - log.debug("Pin not found for traced method %r", wrapped) - return wrapped(*args, **kwargs) - return func(mod, pin, wrapped, instance, args, kwargs) - - return wrapper - - return with_mod - - -def distributed_tracing_enabled(int_config, default=False): - # type: (IntegrationConfig, bool) -> bool - """Returns whether distributed tracing is enabled for this integration config""" - if "distributed_tracing_enabled" in int_config and int_config.distributed_tracing_enabled is not None: - return int_config.distributed_tracing_enabled - elif "distributed_tracing" in int_config and int_config.distributed_tracing is not None: - return int_config.distributed_tracing - return default - - -def int_service(pin, int_config, default=None): - # type: (Optional[Pin], IntegrationConfig, Optional[str]) -> Optional[str] - """Returns the service name for an integration which is internal - to the application. Internal meaning that the work belongs to the - user's application. Eg. Web framework, sqlalchemy, web servers. - - For internal integrations we prioritize overrides, then global defaults and - lastly the default provided by the integration. - """ - # Pin has top priority since it is user defined in code - if pin is not None and pin.service: - return pin.service - - # Config is next since it is also configured via code - # Note that both service and service_name are used by - # integrations. - if "service" in int_config and int_config.service is not None: - return cast(str, int_config.service) - if "service_name" in int_config and int_config.service_name is not None: - return cast(str, int_config.service_name) - - global_service = int_config.global_config._get_service() - # We check if global_service != _inferred_base_service since global service (config.service) - # defaults to _inferred_base_service when no DD_SERVICE is set. In this case, we want to not - # use the inferred base service value, and instead use the integration default service. If we - # didn't do this, we would have a massive breaking change from adding inferred_base_service. - if global_service and global_service != int_config.global_config._inferred_base_service: - return cast(str, global_service) - - if "_default_service" in int_config and int_config._default_service is not None: - return cast(str, int_config._default_service) - - if default is None and global_service: - return cast(str, global_service) - - return default - - -def ext_service(pin, int_config, default=None): - # type: (Optional[Pin], IntegrationConfig, Optional[str]) -> Optional[str] - """Returns the service name for an integration which is external - to the application. External meaning that the integration generates - spans wrapping code that is outside the scope of the user's application. Eg. A database, RPC, cache, etc. - """ - if pin is not None and pin.service: - return pin.service - - if "service" in int_config and int_config.service is not None: - return cast(str, int_config.service) - if "service_name" in int_config and int_config.service_name is not None: - return cast(str, int_config.service_name) - - if "_default_service" in int_config and int_config._default_service is not None: - return cast(str, int_config._default_service) - - # A default is required since it's an external service. - return default - - -def _set_url_tag(integration_config, span, url, query): - # type: (IntegrationConfig, Span, str, str) -> None - if not integration_config.http_tag_query_string: - span.set_tag_str(http.URL, strip_query_string(url)) - elif config._global_query_string_obfuscation_disabled: - # TODO(munir): This case exists for backwards compatibility. To remove query strings from URLs, - # users should set ``DD_TRACE_HTTP_CLIENT_TAG_QUERY_STRING=False``. This case should be - # removed when config.global_query_string_obfuscation_disabled is removed (v3.0). - span.set_tag_str(http.URL, url) - elif getattr(config._obfuscation_query_string_pattern, "pattern", None) == b"": - # obfuscation is disabled when DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP="" - span.set_tag_str(http.URL, strip_query_string(url)) - else: - span.set_tag_str(http.URL, redact_url(url, config._obfuscation_query_string_pattern, query)) - - -def set_http_meta( - span, # type: Span - integration_config, # type: IntegrationConfig - method=None, # type: Optional[str] - url=None, # type: Optional[str] - target_host=None, # type: Optional[str] - server_address=None, # type: Optional[str] - status_code=None, # type: Optional[Union[int, str]] - status_msg=None, # type: Optional[str] - query=None, # type: Optional[str] - parsed_query=None, # type: Optional[Mapping[str, str]] - request_headers=None, # type: Optional[Mapping[str, str]] - response_headers=None, # type: Optional[Mapping[str, str]] - retries_remain=None, # type: Optional[Union[int, str]] - raw_uri=None, # type: Optional[str] - request_cookies=None, # type: Optional[Dict[str, str]] - request_path_params=None, # type: Optional[Dict[str, str]] - request_body=None, # type: Optional[Union[str, Dict[str, List[str]]]] - peer_ip=None, # type: Optional[str] - headers_are_case_sensitive=False, # type: bool - route=None, # type: Optional[str] - response_cookies=None, # type: Optional[Dict[str, str]] -): - # type: (...) -> None - """ - Set HTTP metas on the span - - :param method: the HTTP method - :param url: the HTTP URL - :param status_code: the HTTP status code - :param status_msg: the HTTP status message - :param query: the HTTP query part of the URI as a string - :param parsed_query: the HTTP query part of the URI as parsed by the framework and forwarded to the user code - :param request_headers: the HTTP request headers - :param response_headers: the HTTP response headers - :param raw_uri: the full raw HTTP URI (including ports and query) - :param request_cookies: the HTTP request cookies as a dict - :param request_path_params: the parameters of the HTTP URL as set by the framework: /posts/ would give us - { "id": } - """ - if method is not None: - span.set_tag_str(http.METHOD, method) - - if url is not None: - url = _sanitized_url(url) - _set_url_tag(integration_config, span, url, query) - - if target_host is not None: - span.set_tag_str(net.TARGET_HOST, target_host) - - if server_address is not None: - span.set_tag_str(net.SERVER_ADDRESS, server_address) - - if status_code is not None: - try: - int_status_code = int(status_code) - except (TypeError, ValueError): - log.debug("failed to convert http status code %r to int", status_code) - else: - span.set_tag_str(http.STATUS_CODE, str(status_code)) - if config.http_server.is_error_code(int_status_code): - span.error = 1 - - if status_msg is not None: - span.set_tag_str(http.STATUS_MSG, status_msg) - - if query is not None and integration_config.trace_query_string: - span.set_tag_str(http.QUERY_STRING, query) - - request_ip = peer_ip - if request_headers: - user_agent = _get_request_header_user_agent(request_headers, headers_are_case_sensitive) - if user_agent: - span.set_tag_str(http.USER_AGENT, user_agent) - - # We always collect the IP if appsec is enabled to report it on potential vulnerabilities. - # https://datadoghq.atlassian.net/wiki/spaces/APS/pages/2118779066/Client+IP+addresses+resolution - if asm_config._asm_enabled or config._retrieve_client_ip: - # Retrieve the IP if it was calculated on AppSecProcessor.on_span_start - request_ip = core.get_item("http.request.remote_ip", span=span) - - if not request_ip: - # Not calculated: framework does not support IP blocking or testing env - request_ip = ( - _get_request_header_client_ip(request_headers, peer_ip, headers_are_case_sensitive) or peer_ip - ) - - if request_ip: - span.set_tag_str(http.CLIENT_IP, request_ip) - span.set_tag_str("network.client.ip", request_ip) - - if integration_config.is_header_tracing_configured: - """We should store both http..headers. and - http.. The last one - is the DD standardized tag for user-agent""" - _store_request_headers(dict(request_headers), span, integration_config) - - if response_headers is not None and integration_config.is_header_tracing_configured: - _store_response_headers(dict(response_headers), span, integration_config) - - if retries_remain is not None: - span.set_tag_str(http.RETRIES_REMAIN, str(retries_remain)) - - core.dispatch( - "set_http_meta_for_asm", - [ - span, - request_ip, - raw_uri, - route, - method, - request_headers, - request_cookies, - parsed_query, - request_path_params, - request_body, - status_code, - response_headers, - response_cookies, - ], - ) - - if route is not None: - span.set_tag_str(http.ROUTE, route) - - -def activate_distributed_headers(tracer, int_config=None, request_headers=None, override=None): - # type: (Tracer, Optional[IntegrationConfig], Optional[Dict[str, str]], Optional[bool]) -> None - """ - Helper for activating a distributed trace headers' context if enabled in integration config. - int_config will be used to check if distributed trace headers context will be activated, but - override will override whatever value is set in int_config if passed any value other than None. - """ - if override is False: - return None - - if override or (int_config and distributed_tracing_enabled(int_config)): - context = HTTPPropagator.extract(request_headers) - - # Only need to activate the new context if something was propagated - # The new context must have one of these values in order for it to be activated - if not context.trace_id and not context._baggage and not context._span_links: - return None - # Do not reactivate a context with the same trace id - # DEV: An example could be nested web frameworks, when one layer already - # parsed request headers and activated them. - # - # Example:: - # - # app = Flask(__name__) # Traced via Flask instrumentation - # app = DDWSGIMiddleware(app) # Extra layer on top for WSGI - current_context = tracer.current_trace_context() - - # We accept incoming contexts with only baggage or only span_links, however if we - # already have a current_context then an incoming context not - # containing a trace_id or containing the same trace_id - # should not be activated. - if current_context and ( - not context.trace_id or (context.trace_id and context.trace_id == current_context.trace_id) - ): - log.debug( - "will not activate extracted Context(trace_id=%r, span_id=%r), a context with that trace id is already active", # noqa: E501 - context.trace_id, - context.span_id, - ) - return None - - # We have parsed a trace id from headers, and we do not already - # have a context with the same trace id active - tracer.context_provider.activate(context) - - -def _flatten( - obj, # type: Any - sep=".", # type: str - prefix="", # type: str - exclude_policy=None, # type: Optional[Callable[[str], bool]] -): - # type: (...) -> Generator[Tuple[str, Any], None, None] - s = deque() # type: ignore - s.append((prefix, obj)) - while s: - p, v = s.pop() - if exclude_policy is not None and exclude_policy(p): - continue - if isinstance(v, dict): - s.extend((sep.join((p, k)) if p else k, v) for k, v in v.items()) - else: - yield p, v - - -def set_flattened_tags( - span, # type: Span - items, # type: Iterator[Tuple[str, Any]] - sep=".", # type: str - exclude_policy=None, # type: Optional[Callable[[str], bool]] - processor=None, # type: Optional[Callable[[Any], Any]] -): - # type: (...) -> None - for prefix, value in items: - for tag, v in _flatten(value, sep, prefix, exclude_policy): - span.set_tag(tag, processor(v) if processor is not None else v) - - -def set_user( - tracer, # type: Tracer - user_id, # type: str - name=None, # type: Optional[str] - email=None, # type: Optional[str] - scope=None, # type: Optional[str] - role=None, # type: Optional[str] - session_id=None, # type: Optional[str] - propagate=False, # type bool - span=None, # type: Optional[Span] -): - # type: (...) -> None - """Set user tags. - https://docs.datadoghq.com/logs/log_configuration/attributes_naming_convention/#user-related-attributes - https://docs.datadoghq.com/security_platform/application_security/setup_and_configure/?tab=set_tag&code-lang=python - """ - if span is None: - span = tracer.current_root_span() - if span: - if user_id: - str_user_id = str(user_id) - span.set_tag_str(user.ID, str_user_id) - if propagate: - span.context.dd_user_id = str_user_id - - # All other fields are optional - if name: - span.set_tag_str(user.NAME, name) - if email: - span.set_tag_str(user.EMAIL, email) - if scope: - span.set_tag_str(user.SCOPE, scope) - if role: - span.set_tag_str(user.ROLE, role) - if session_id: - span.set_tag_str(user.SESSION_ID, session_id) - - if asm_config._asm_enabled: - exc = core.dispatch_with_results("set_user_for_asm", [tracer, user_id]).block_user.exception - if exc: - raise exc - - else: - log.warning( - "No root span in the current execution. Skipping set_user tags. " - "See https://docs.datadoghq.com/security_platform/application_security/setup_and_configure/" - "?tab=set_user&code-lang=python for more information.", +from ddtrace.contrib.internal.redis_utils import MULTI_KEY_COMMANDS # noqa: F401 +from ddtrace.contrib.internal.redis_utils import ROW_RETURNING_COMMANDS # noqa: F401 +from ddtrace.contrib.internal.redis_utils import SINGLE_KEY_COMMANDS # noqa: F401 +from ddtrace.contrib.internal.redis_utils import determine_row_count # noqa: F401 +from ddtrace.contrib.internal.trace_utils import IP_PATTERNS # noqa: F401 +from ddtrace.contrib.internal.trace_utils import NORMALIZE_PATTERN # noqa: F401 +from ddtrace.contrib.internal.trace_utils import REQUEST # noqa: F401 +from ddtrace.contrib.internal.trace_utils import RESPONSE # noqa: F401 +from ddtrace.contrib.internal.trace_utils import USER_AGENT_PATTERNS # noqa: F401 +from ddtrace.contrib.internal.trace_utils import activate_distributed_headers # noqa: F401 +from ddtrace.contrib.internal.trace_utils import distributed_tracing_enabled # noqa: F401 +from ddtrace.contrib.internal.trace_utils import ext_service # noqa: F401 +from ddtrace.contrib.internal.trace_utils import extract_netloc_and_query_info_from_url # noqa: F401 +from ddtrace.contrib.internal.trace_utils import int_service # noqa: F401 +from ddtrace.contrib.internal.trace_utils import iswrapped # noqa: F401 +from ddtrace.contrib.internal.trace_utils import set_flattened_tags # noqa: F401 +from ddtrace.contrib.internal.trace_utils import set_http_meta # noqa: F401 +from ddtrace.contrib.internal.trace_utils import set_user # noqa: F401 +from ddtrace.contrib.internal.trace_utils import unwrap # noqa: F401 +from ddtrace.contrib.internal.trace_utils import with_traced_module # noqa: F401 +from ddtrace.contrib.internal.trace_utils import wrap # noqa: F401 +from ddtrace.contrib.internal.trace_utils_async import with_traced_module as with_traced_module_async # noqa: F401 +from ddtrace.internal.utils.deprecations import DDTraceDeprecationWarning +from ddtrace.vendor.debtcollector import deprecate + + +# TODO: Add trace utils that should not be public to the list below +_DEPRECATED_MODULE_ATTRIBUTES = [] + + +def __getattr__(name): + if name in _DEPRECATED_MODULE_ATTRIBUTES: + deprecate( + ("%s.%s is deprecated" % (__name__, name)), + category=DDTraceDeprecationWarning, + removal_version="3.0.0", ) + if name in globals(): + return globals()[name] -def extract_netloc_and_query_info_from_url(url): - # type: (str) -> Tuple[str, str] - parse_result = parse.urlparse(url) - query = parse_result.query - - # Relative URLs don't have a netloc, so we force them - if not parse_result.netloc: - parse_result = parse.urlparse("//{url}".format(url=url)) - - netloc = parse_result.netloc.split("@", 1)[-1] # Discard auth info - netloc = netloc.split(":", 1)[0] # Discard port information - return netloc, query - - -class InterruptException(Exception): - pass - - -def _convert_to_string(attr): - # ensures attribute is converted to a string - if attr: - if isinstance(attr, int) or isinstance(attr, float): - return str(attr) - else: - return ensure_text(attr) - return attr + raise AttributeError("%s has no attribute %s", __name__, name) diff --git a/ddtrace/contrib/trace_utils_async.py b/ddtrace/contrib/trace_utils_async.py index f58cc4e34bb..ad74c474a04 100644 --- a/ddtrace/contrib/trace_utils_async.py +++ b/ddtrace/contrib/trace_utils_async.py @@ -1,39 +1,11 @@ -""" -async tracing utils +from ddtrace.contrib.internal.trace_utils_async import * # noqa: F403 +from ddtrace.internal.utils.deprecations import DDTraceDeprecationWarning +from ddtrace.vendor.debtcollector import deprecate -Note that this module should only be imported in Python 3.5+. -""" -from ddtrace.internal.logger import get_logger -from ddtrace.trace import Pin - -log = get_logger(__name__) - - -def with_traced_module(func): - """Async version of trace_utils.with_traced_module. - Usage:: - - @with_traced_module - async def my_traced_wrapper(django, pin, func, instance, args, kwargs): - # Do tracing stuff - pass - - def patch(): - import django - wrap(django.somefunc, my_traced_wrapper(django)) - """ - - def with_mod(mod): - async def wrapper(wrapped, instance, args, kwargs): - pin = Pin._find(instance, mod) - if pin and not pin.enabled(): - return await wrapped(*args, **kwargs) - elif not pin: - log.debug("Pin not found for traced method %r", wrapped) - return await wrapped(*args, **kwargs) - return await func(mod, pin, wrapped, instance, args, kwargs) - - return wrapper - - return with_mod +deprecate( + "The ddtrace.contrib.trace_utils_async module is deprecated", + message="Import from ``ddtrace.contrib.trace_utils`` instead.", + category=DDTraceDeprecationWarning, + removal_version="3.0.0", +) diff --git a/ddtrace/contrib/trace_utils_redis.py b/ddtrace/contrib/trace_utils_redis.py index 8df16c3ce4d..151f5d90e12 100644 --- a/ddtrace/contrib/trace_utils_redis.py +++ b/ddtrace/contrib/trace_utils_redis.py @@ -1,13 +1,14 @@ -from ddtrace.contrib.redis_utils import determine_row_count -from ddtrace.contrib.redis_utils import stringify_cache_args +from ddtrace.contrib.internal.redis_utils import determine_row_count +from ddtrace.contrib.internal.redis_utils import stringify_cache_args from ddtrace.internal.utils.deprecations import DDTraceDeprecationWarning from ddtrace.vendor.debtcollector import deprecate deprecate( - "The ddtrace.contrib.trace_utils_redis module is deprecated and will be removed.", - message="A new interface will be provided by the ddtrace.contrib.redis_utils module", + "The ddtrace.contrib.trace_utils_redis module is deprecated", + message="Import from ``ddtrace.contrib.trace_utils`` instead.", category=DDTraceDeprecationWarning, + removal_version="3.0.0", ) diff --git a/tests/tracer/test_trace_utils.py b/tests/tracer/test_trace_utils.py index a7604636b62..b2cdabb91d0 100644 --- a/tests/tracer/test_trace_utils.py +++ b/tests/tracer/test_trace_utils.py @@ -18,7 +18,7 @@ from ddtrace._trace.context import Context from ddtrace._trace.span import Span from ddtrace.contrib import trace_utils -from ddtrace.contrib.trace_utils import _get_request_header_client_ip +from ddtrace.contrib.internal.trace_utils import _get_request_header_client_ip from ddtrace.ext import SpanTypes from ddtrace.ext import http from ddtrace.ext import net From 33e0ee4708be2ed0691b5c65d3b2ede7d6ec4fc4 Mon Sep 17 00:00:00 2001 From: Munir Abdinur Date: Fri, 17 Jan 2025 15:32:39 -0500 Subject: [PATCH 2/6] fmt --- ddtrace/contrib/internal/aioredis/patch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ddtrace/contrib/internal/aioredis/patch.py b/ddtrace/contrib/internal/aioredis/patch.py index fbadc9d5535..3e1f0c062a3 100644 --- a/ddtrace/contrib/internal/aioredis/patch.py +++ b/ddtrace/contrib/internal/aioredis/patch.py @@ -12,8 +12,8 @@ from ddtrace.constants import SPAN_KIND from ddtrace.constants import SPAN_MEASURED_KEY from ddtrace.contrib import trace_utils -from ddtrace.contrib.redis_utils import ROW_RETURNING_COMMANDS from ddtrace.contrib.internal.redis_utils import _run_redis_command_async +from ddtrace.contrib.redis_utils import ROW_RETURNING_COMMANDS from ddtrace.contrib.redis_utils import determine_row_count from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes From 95e4ed76bd6a4e7be134107da0406332d230c901 Mon Sep 17 00:00:00 2001 From: Munir Abdinur Date: Fri, 17 Jan 2025 15:51:52 -0500 Subject: [PATCH 3/6] use internal.trace_utils* internally --- ddtrace/appsec/_trace_utils.py | 2 +- ddtrace/contrib/dbapi/__init__.py | 4 ++-- ddtrace/contrib/dbapi_async/__init__.py | 4 ++-- ddtrace/contrib/internal/aiobotocore/patch.py | 4 ++-- ddtrace/contrib/internal/aiohttp/patch.py | 14 ++++++------- .../contrib/internal/aiohttp_jinja2/patch.py | 6 +++--- ddtrace/contrib/internal/anthropic/patch.py | 6 +++--- ddtrace/contrib/internal/asyncpg/patch.py | 8 ++++---- .../contrib/internal/azure_functions/patch.py | 4 ++-- ddtrace/contrib/internal/botocore/patch.py | 6 +++--- .../internal/botocore/services/bedrock.py | 2 +- .../internal/botocore/services/kinesis.py | 2 +- .../contrib/internal/botocore/services/sqs.py | 2 +- .../botocore/services/stepfunctions.py | 2 +- ddtrace/contrib/internal/celery/utils.py | 2 +- .../contrib/internal/django/restframework.py | 4 ++-- .../contrib/internal/elasticsearch/patch.py | 4 ++-- ddtrace/contrib/internal/flask/patch.py | 2 +- .../internal/google_generativeai/patch.py | 6 +++--- ddtrace/contrib/internal/grpc/patch.py | 2 +- ddtrace/contrib/internal/httplib/patch.py | 2 +- ddtrace/contrib/internal/httpx/patch.py | 6 +++--- ddtrace/contrib/internal/jinja2/patch.py | 2 +- ddtrace/contrib/internal/langchain/patch.py | 6 +++--- ddtrace/contrib/internal/logbook/patch.py | 2 +- ddtrace/contrib/internal/logging/patch.py | 2 +- ddtrace/contrib/internal/loguru/patch.py | 2 +- ddtrace/contrib/internal/mako/patch.py | 6 +++--- ddtrace/contrib/internal/molten/patch.py | 2 +- ddtrace/contrib/internal/mysqldb/patch.py | 2 +- .../internal/psycopg/async_connection.py | 2 +- .../contrib/internal/psycopg/connection.py | 2 +- ddtrace/contrib/internal/pynamodb/patch.py | 2 +- ddtrace/contrib/internal/pyodbc/patch.py | 4 ++-- ddtrace/contrib/internal/redis/patch.py | 2 +- ddtrace/contrib/internal/requests/patch.py | 2 +- ddtrace/contrib/internal/snowflake/patch.py | 2 +- ddtrace/contrib/internal/sqlalchemy/patch.py | 2 +- ddtrace/contrib/internal/starlette/patch.py | 2 +- ddtrace/contrib/internal/structlog/patch.py | 4 ++-- ddtrace/contrib/internal/tornado/handlers.py | 2 +- ddtrace/contrib/internal/urllib/patch.py | 2 +- ddtrace/contrib/internal/vertexai/patch.py | 6 +++--- ddtrace/contrib/internal/webbrowser/patch.py | 2 +- ddtrace/contrib/redis_utils.py | 2 +- ddtrace/contrib/trace_utils_async.py | 4 ++-- ddtrace/contrib/trace_utils_redis.py | 4 ++-- ddtrace/llmobs/_integrations/base.py | 2 +- .../appsec/appsec/test_appsec_trace_utils.py | 2 +- tests/appsec/appsec/test_asm_standalone.py | 2 +- tests/appsec/appsec/test_processor.py | 2 +- .../appsec/appsec/test_remoteconfiguration.py | 2 +- tests/appsec/appsec/test_telemetry.py | 2 +- tests/contrib/asyncpg/test_asyncpg.py | 2 +- tests/contrib/django/views.py | 2 +- tests/contrib/flask/app.py | 2 +- tests/contrib/openai/test_openai_v0.py | 2 +- tests/contrib/openai/test_openai_v1.py | 2 +- tests/internal/test_module.py | 6 +++--- tests/tracer/test_trace_utils.py | 20 +++++++++---------- tests/tracer/test_tracer.py | 2 +- 61 files changed, 107 insertions(+), 107 deletions(-) diff --git a/ddtrace/appsec/_trace_utils.py b/ddtrace/appsec/_trace_utils.py index 77cb1aaca3a..56f34d81f99 100644 --- a/ddtrace/appsec/_trace_utils.py +++ b/ddtrace/appsec/_trace_utils.py @@ -11,7 +11,7 @@ from ddtrace.appsec._constants import LOGIN_EVENTS_MODE from ddtrace.appsec._constants import WAF_ACTIONS from ddtrace.appsec._utils import _hash_user_id -from ddtrace.contrib.trace_utils import set_user +from ddtrace.contrib.internal.trace_utils import set_user from ddtrace.ext import SpanTypes from ddtrace.ext import user from ddtrace.internal import core diff --git a/ddtrace/contrib/dbapi/__init__.py b/ddtrace/contrib/dbapi/__init__.py index 0b772ac04ec..e511fc31657 100644 --- a/ddtrace/contrib/dbapi/__init__.py +++ b/ddtrace/contrib/dbapi/__init__.py @@ -21,8 +21,8 @@ from ...ext import db from ...ext import sql from ...trace import Pin -from ..trace_utils import ext_service -from ..trace_utils import iswrapped +from ..internal.trace_utils import ext_service +from ..internal.trace_utils import iswrapped log = get_logger(__name__) diff --git a/ddtrace/contrib/dbapi_async/__init__.py b/ddtrace/contrib/dbapi_async/__init__.py index d0c43fc1c2b..9ebf36847b0 100644 --- a/ddtrace/contrib/dbapi_async/__init__.py +++ b/ddtrace/contrib/dbapi_async/__init__.py @@ -16,8 +16,8 @@ from ...trace import Pin from ..dbapi import TracedConnection from ..dbapi import TracedCursor -from ..trace_utils import ext_service -from ..trace_utils import iswrapped +from ..internal.trace_utils import ext_service +from ..internal.trace_utils import iswrapped log = get_logger(__name__) diff --git a/ddtrace/contrib/internal/aiobotocore/patch.py b/ddtrace/contrib/internal/aiobotocore/patch.py index 7431bd5c592..0df89927ece 100644 --- a/ddtrace/contrib/internal/aiobotocore/patch.py +++ b/ddtrace/contrib/internal/aiobotocore/patch.py @@ -7,8 +7,8 @@ from ddtrace.constants import _ANALYTICS_SAMPLE_RATE_KEY from ddtrace.constants import SPAN_KIND from ddtrace.constants import SPAN_MEASURED_KEY -from ddtrace.contrib.trace_utils import ext_service -from ddtrace.contrib.trace_utils import unwrap +from ddtrace.contrib.internal.trace_utils import ext_service +from ddtrace.contrib.internal.trace_utils import unwrap from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes from ddtrace.ext import aws diff --git a/ddtrace/contrib/internal/aiohttp/patch.py b/ddtrace/contrib/internal/aiohttp/patch.py index e0f0bc869e9..900a8d26e41 100644 --- a/ddtrace/contrib/internal/aiohttp/patch.py +++ b/ddtrace/contrib/internal/aiohttp/patch.py @@ -6,13 +6,13 @@ from ddtrace import config from ddtrace.constants import SPAN_KIND -from ddtrace.contrib.trace_utils import ext_service -from ddtrace.contrib.trace_utils import extract_netloc_and_query_info_from_url -from ddtrace.contrib.trace_utils import set_http_meta -from ddtrace.contrib.trace_utils import unwrap -from ddtrace.contrib.trace_utils import with_traced_module as with_traced_module_sync -from ddtrace.contrib.trace_utils import wrap -from ddtrace.contrib.trace_utils_async import with_traced_module +from ddtrace.contrib.internal.trace_utils import ext_service +from ddtrace.contrib.internal.trace_utils import extract_netloc_and_query_info_from_url +from ddtrace.contrib.internal.trace_utils import set_http_meta +from ddtrace.contrib.internal.trace_utils import unwrap +from ddtrace.contrib.internal.trace_utils import with_traced_module as with_traced_module_sync +from ddtrace.contrib.internal.trace_utils import wrap +from ddtrace.contrib.internal.trace_utils_async import with_traced_module from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes from ddtrace.internal.constants import COMPONENT diff --git a/ddtrace/contrib/internal/aiohttp_jinja2/patch.py b/ddtrace/contrib/internal/aiohttp_jinja2/patch.py index 84553899c39..74987483a20 100644 --- a/ddtrace/contrib/internal/aiohttp_jinja2/patch.py +++ b/ddtrace/contrib/internal/aiohttp_jinja2/patch.py @@ -1,9 +1,9 @@ import aiohttp_jinja2 from ddtrace import config -from ddtrace.contrib.trace_utils import unwrap -from ddtrace.contrib.trace_utils import with_traced_module -from ddtrace.contrib.trace_utils import wrap +from ddtrace.contrib.internal.trace_utils import unwrap +from ddtrace.contrib.internal.trace_utils import with_traced_module +from ddtrace.contrib.internal.trace_utils import wrap from ddtrace.ext import SpanTypes from ddtrace.internal.constants import COMPONENT from ddtrace.internal.utils import get_argument_value diff --git a/ddtrace/contrib/internal/anthropic/patch.py b/ddtrace/contrib/internal/anthropic/patch.py index 24f72f2b511..2bb35836580 100644 --- a/ddtrace/contrib/internal/anthropic/patch.py +++ b/ddtrace/contrib/internal/anthropic/patch.py @@ -11,9 +11,9 @@ from ddtrace.contrib.internal.anthropic.utils import tag_params_on_span from ddtrace.contrib.internal.anthropic.utils import tag_tool_result_input_on_span from ddtrace.contrib.internal.anthropic.utils import tag_tool_use_input_on_span -from ddtrace.contrib.trace_utils import unwrap -from ddtrace.contrib.trace_utils import with_traced_module -from ddtrace.contrib.trace_utils import wrap +from ddtrace.contrib.internal.trace_utils import unwrap +from ddtrace.contrib.internal.trace_utils import with_traced_module +from ddtrace.contrib.internal.trace_utils import wrap from ddtrace.internal.logger import get_logger from ddtrace.internal.utils import get_argument_value from ddtrace.llmobs._integrations import AnthropicIntegration diff --git a/ddtrace/contrib/internal/asyncpg/patch.py b/ddtrace/contrib/internal/asyncpg/patch.py index ac1347a7de6..538af434e0b 100644 --- a/ddtrace/contrib/internal/asyncpg/patch.py +++ b/ddtrace/contrib/internal/asyncpg/patch.py @@ -19,10 +19,10 @@ from ddtrace.internal.schema import schematize_service_name from ddtrace.internal.utils import get_argument_value from ddtrace.propagation._database_monitoring import _DBM_Propagator -from ddtrace.contrib.trace_utils import ext_service -from ddtrace.contrib.trace_utils import unwrap -from ddtrace.contrib.trace_utils import wrap -from ddtrace.contrib.trace_utils_async import with_traced_module +from ddtrace.contrib.internal.trace_utils import ext_service +from ddtrace.contrib.internal.trace_utils import unwrap +from ddtrace.contrib.internal.trace_utils import wrap +from ddtrace.contrib.internal.trace_utils_async import with_traced_module if TYPE_CHECKING: # pragma: no cover diff --git a/ddtrace/contrib/internal/azure_functions/patch.py b/ddtrace/contrib/internal/azure_functions/patch.py index 1c0c658a9eb..7690368a852 100644 --- a/ddtrace/contrib/internal/azure_functions/patch.py +++ b/ddtrace/contrib/internal/azure_functions/patch.py @@ -2,8 +2,8 @@ from wrapt import wrap_function_wrapper as _w from ddtrace import config -from ddtrace.contrib.trace_utils import int_service -from ddtrace.contrib.trace_utils import unwrap as _u +from ddtrace.contrib.internal.trace_utils import int_service +from ddtrace.contrib.internal.trace_utils import unwrap as _u from ddtrace.ext import SpanTypes from ddtrace.internal import core from ddtrace.internal.schema import schematize_cloud_faas_operation diff --git a/ddtrace/contrib/internal/botocore/patch.py b/ddtrace/contrib/internal/botocore/patch.py index 07c0bd403e4..734c429d798 100644 --- a/ddtrace/contrib/internal/botocore/patch.py +++ b/ddtrace/contrib/internal/botocore/patch.py @@ -17,9 +17,9 @@ from ddtrace import config from ddtrace.constants import SPAN_KIND -from ddtrace.contrib.trace_utils import ext_service -from ddtrace.contrib.trace_utils import unwrap -from ddtrace.contrib.trace_utils import with_traced_module +from ddtrace.contrib.internal.trace_utils import ext_service +from ddtrace.contrib.internal.trace_utils import unwrap +from ddtrace.contrib.internal.trace_utils import with_traced_module from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes from ddtrace.internal import core diff --git a/ddtrace/contrib/internal/botocore/services/bedrock.py b/ddtrace/contrib/internal/botocore/services/bedrock.py index 00e9aa5756f..4f237b67342 100644 --- a/ddtrace/contrib/internal/botocore/services/bedrock.py +++ b/ddtrace/contrib/internal/botocore/services/bedrock.py @@ -7,7 +7,7 @@ import wrapt from ddtrace import config -from ddtrace.contrib.trace_utils import ext_service +from ddtrace.contrib.internal.trace_utils import ext_service from ddtrace.ext import SpanTypes from ddtrace.internal import core from ddtrace.internal.logger import get_logger diff --git a/ddtrace/contrib/internal/botocore/services/kinesis.py b/ddtrace/contrib/internal/botocore/services/kinesis.py index 0287c29d2bc..f1f71d0b819 100644 --- a/ddtrace/contrib/internal/botocore/services/kinesis.py +++ b/ddtrace/contrib/internal/botocore/services/kinesis.py @@ -10,7 +10,7 @@ import botocore.exceptions from ddtrace import config -from ddtrace.contrib.trace_utils import ext_service +from ddtrace.contrib.internal.trace_utils import ext_service from ddtrace.ext import SpanTypes from ddtrace.internal import core from ddtrace.internal.logger import get_logger diff --git a/ddtrace/contrib/internal/botocore/services/sqs.py b/ddtrace/contrib/internal/botocore/services/sqs.py index 084a6f77dc7..5bf238c8cfd 100644 --- a/ddtrace/contrib/internal/botocore/services/sqs.py +++ b/ddtrace/contrib/internal/botocore/services/sqs.py @@ -7,7 +7,7 @@ import botocore.exceptions from ddtrace import config -from ddtrace.contrib.trace_utils import ext_service +from ddtrace.contrib.internal.trace_utils import ext_service from ddtrace.ext import SpanTypes from ddtrace.internal import core from ddtrace.internal.logger import get_logger diff --git a/ddtrace/contrib/internal/botocore/services/stepfunctions.py b/ddtrace/contrib/internal/botocore/services/stepfunctions.py index 9e3c1d2db13..8bf2c1433ee 100644 --- a/ddtrace/contrib/internal/botocore/services/stepfunctions.py +++ b/ddtrace/contrib/internal/botocore/services/stepfunctions.py @@ -6,7 +6,7 @@ from ddtrace import config from ddtrace.contrib.internal.botocore.constants import BOTOCORE_STEPFUNCTIONS_INPUT_KEY -from ddtrace.contrib.trace_utils import ext_service +from ddtrace.contrib.internal.trace_utils import ext_service from ddtrace.ext import SpanTypes from ddtrace.internal import core from ddtrace.internal.logger import get_logger diff --git a/ddtrace/contrib/internal/celery/utils.py b/ddtrace/contrib/internal/celery/utils.py index 8945fb5857b..5c7a4c077e8 100644 --- a/ddtrace/contrib/internal/celery/utils.py +++ b/ddtrace/contrib/internal/celery/utils.py @@ -3,7 +3,7 @@ from weakref import WeakValueDictionary from ddtrace._trace.span import Span -from ddtrace.contrib.trace_utils import set_flattened_tags +from ddtrace.contrib.internal.trace_utils import set_flattened_tags from ddtrace.propagation.http import HTTPPropagator from .constants import CTX_KEY diff --git a/ddtrace/contrib/internal/django/restframework.py b/ddtrace/contrib/internal/django/restframework.py index d9a7266fb03..e1bebeaea73 100644 --- a/ddtrace/contrib/internal/django/restframework.py +++ b/ddtrace/contrib/internal/django/restframework.py @@ -1,8 +1,8 @@ import rest_framework.views from wrapt import wrap_function_wrapper as wrap -from ddtrace.contrib.trace_utils import iswrapped -from ddtrace.contrib.trace_utils import with_traced_module +from ddtrace.contrib.internal.trace_utils import iswrapped +from ddtrace.contrib.internal.trace_utils import with_traced_module @with_traced_module diff --git a/ddtrace/contrib/internal/elasticsearch/patch.py b/ddtrace/contrib/internal/elasticsearch/patch.py index 7c408db55a5..09eff66dbca 100644 --- a/ddtrace/contrib/internal/elasticsearch/patch.py +++ b/ddtrace/contrib/internal/elasticsearch/patch.py @@ -9,8 +9,8 @@ from ddtrace.constants import SPAN_KIND from ddtrace.constants import SPAN_MEASURED_KEY from ddtrace.contrib.internal.elasticsearch.quantize import quantize -from ddtrace.contrib.trace_utils import ext_service -from ddtrace.contrib.trace_utils import extract_netloc_and_query_info_from_url +from ddtrace.contrib.internal.trace_utils import ext_service +from ddtrace.contrib.internal.trace_utils import extract_netloc_and_query_info_from_url from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes from ddtrace.ext import elasticsearch as metadata diff --git a/ddtrace/contrib/internal/flask/patch.py b/ddtrace/contrib/internal/flask/patch.py index 010df5218c5..7111310b6ef 100644 --- a/ddtrace/contrib/internal/flask/patch.py +++ b/ddtrace/contrib/internal/flask/patch.py @@ -30,8 +30,8 @@ from wrapt import wrap_function_wrapper as _w from ddtrace import config +from ddtrace.contrib.internal.trace_utils import unwrap as _u from ddtrace.contrib.internal.wsgi.wsgi import _DDWSGIMiddlewareBase -from ddtrace.contrib.trace_utils import unwrap as _u from ddtrace.internal.logger import get_logger from ddtrace.internal.utils import get_argument_value from ddtrace.internal.utils.importlib import func_name diff --git a/ddtrace/contrib/internal/google_generativeai/patch.py b/ddtrace/contrib/internal/google_generativeai/patch.py index 3564f9ec1ec..9cd28a531f4 100644 --- a/ddtrace/contrib/internal/google_generativeai/patch.py +++ b/ddtrace/contrib/internal/google_generativeai/patch.py @@ -9,9 +9,9 @@ from ddtrace.contrib.internal.google_generativeai._utils import _extract_api_key from ddtrace.contrib.internal.google_generativeai._utils import tag_request from ddtrace.contrib.internal.google_generativeai._utils import tag_response -from ddtrace.contrib.trace_utils import unwrap -from ddtrace.contrib.trace_utils import with_traced_module -from ddtrace.contrib.trace_utils import wrap +from ddtrace.contrib.internal.trace_utils import unwrap +from ddtrace.contrib.internal.trace_utils import with_traced_module +from ddtrace.contrib.internal.trace_utils import wrap from ddtrace.llmobs._integrations import GeminiIntegration from ddtrace.llmobs._integrations.utils import extract_model_name_google from ddtrace.trace import Pin diff --git a/ddtrace/contrib/internal/grpc/patch.py b/ddtrace/contrib/internal/grpc/patch.py index 122893b030f..5e1053af28a 100644 --- a/ddtrace/contrib/internal/grpc/patch.py +++ b/ddtrace/contrib/internal/grpc/patch.py @@ -7,7 +7,7 @@ from ddtrace.contrib.internal.grpc.client_interceptor import create_client_interceptor from ddtrace.contrib.internal.grpc.client_interceptor import intercept_channel from ddtrace.contrib.internal.grpc.server_interceptor import create_server_interceptor -from ddtrace.contrib.trace_utils import unwrap as _u +from ddtrace.contrib.internal.trace_utils import unwrap as _u from ddtrace.internal.logger import get_logger from ddtrace.internal.schema import schematize_service_name from ddtrace.internal.utils import get_argument_value diff --git a/ddtrace/contrib/internal/httplib/patch.py b/ddtrace/contrib/internal/httplib/patch.py index 3e354aeedea..a1e367af3a1 100644 --- a/ddtrace/contrib/internal/httplib/patch.py +++ b/ddtrace/contrib/internal/httplib/patch.py @@ -9,7 +9,7 @@ from ddtrace.constants import _ANALYTICS_SAMPLE_RATE_KEY from ddtrace.constants import SPAN_KIND from ddtrace.contrib import trace_utils -from ddtrace.contrib.trace_utils import unwrap as _u +from ddtrace.contrib.internal.trace_utils import unwrap as _u from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes from ddtrace.internal.compat import httplib diff --git a/ddtrace/contrib/internal/httpx/patch.py b/ddtrace/contrib/internal/httpx/patch.py index 8a9e4eebc3a..a3a677c14ac 100644 --- a/ddtrace/contrib/internal/httpx/patch.py +++ b/ddtrace/contrib/internal/httpx/patch.py @@ -8,9 +8,9 @@ from ddtrace.constants import _ANALYTICS_SAMPLE_RATE_KEY from ddtrace.constants import SPAN_KIND from ddtrace.constants import SPAN_MEASURED_KEY -from ddtrace.contrib.trace_utils import distributed_tracing_enabled -from ddtrace.contrib.trace_utils import ext_service -from ddtrace.contrib.trace_utils import set_http_meta +from ddtrace.contrib.internal.trace_utils import distributed_tracing_enabled +from ddtrace.contrib.internal.trace_utils import ext_service +from ddtrace.contrib.internal.trace_utils import set_http_meta from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes from ddtrace.internal.compat import ensure_binary diff --git a/ddtrace/contrib/internal/jinja2/patch.py b/ddtrace/contrib/internal/jinja2/patch.py index cdf1254527d..67c704c415d 100644 --- a/ddtrace/contrib/internal/jinja2/patch.py +++ b/ddtrace/contrib/internal/jinja2/patch.py @@ -5,7 +5,7 @@ from ddtrace import config from ddtrace.constants import SPAN_MEASURED_KEY -from ddtrace.contrib.trace_utils import unwrap as _u +from ddtrace.contrib.internal.trace_utils import unwrap as _u from ddtrace.ext import SpanTypes from ddtrace.internal.constants import COMPONENT from ddtrace.internal.utils import ArgumentError diff --git a/ddtrace/contrib/internal/langchain/patch.py b/ddtrace/contrib/internal/langchain/patch.py index f9c58249cb2..430700e2782 100644 --- a/ddtrace/contrib/internal/langchain/patch.py +++ b/ddtrace/contrib/internal/langchain/patch.py @@ -51,9 +51,9 @@ from ddtrace.contrib.internal.langchain.constants import vectorstore_classes from ddtrace.contrib.internal.langchain.utils import shared_stream from ddtrace.contrib.internal.langchain.utils import tag_general_message_input -from ddtrace.contrib.trace_utils import unwrap -from ddtrace.contrib.trace_utils import with_traced_module -from ddtrace.contrib.trace_utils import wrap +from ddtrace.contrib.internal.trace_utils import unwrap +from ddtrace.contrib.internal.trace_utils import with_traced_module +from ddtrace.contrib.internal.trace_utils import wrap from ddtrace.internal.logger import get_logger from ddtrace.internal.utils import ArgumentError from ddtrace.internal.utils import get_argument_value diff --git a/ddtrace/contrib/internal/logbook/patch.py b/ddtrace/contrib/internal/logbook/patch.py index 075b00787bc..7a1c1ae1484 100644 --- a/ddtrace/contrib/internal/logbook/patch.py +++ b/ddtrace/contrib/internal/logbook/patch.py @@ -9,7 +9,7 @@ from ddtrace.contrib.internal.logging.constants import RECORD_ATTR_TRACE_ID from ddtrace.contrib.internal.logging.constants import RECORD_ATTR_VALUE_EMPTY from ddtrace.contrib.internal.logging.constants import RECORD_ATTR_VERSION -from ddtrace.contrib.trace_utils import unwrap as _u +from ddtrace.contrib.internal.trace_utils import unwrap as _u from ddtrace.internal.utils import get_argument_value diff --git a/ddtrace/contrib/internal/logging/patch.py b/ddtrace/contrib/internal/logging/patch.py index 51508bae28f..11edd5d938f 100644 --- a/ddtrace/contrib/internal/logging/patch.py +++ b/ddtrace/contrib/internal/logging/patch.py @@ -4,7 +4,7 @@ import ddtrace from ddtrace import config -from ddtrace.contrib.trace_utils import unwrap as _u +from ddtrace.contrib.internal.trace_utils import unwrap as _u from ddtrace.internal.utils import get_argument_value from .constants import RECORD_ATTR_ENV diff --git a/ddtrace/contrib/internal/loguru/patch.py b/ddtrace/contrib/internal/loguru/patch.py index e25aba4cff4..1dff8046f80 100644 --- a/ddtrace/contrib/internal/loguru/patch.py +++ b/ddtrace/contrib/internal/loguru/patch.py @@ -3,7 +3,7 @@ import ddtrace from ddtrace import config -from ddtrace.contrib.trace_utils import unwrap as _u +from ddtrace.contrib.internal.trace_utils import unwrap as _u from ..logging.constants import RECORD_ATTR_ENV from ..logging.constants import RECORD_ATTR_SERVICE diff --git a/ddtrace/contrib/internal/mako/patch.py b/ddtrace/contrib/internal/mako/patch.py index d39a51238a2..ca569477773 100644 --- a/ddtrace/contrib/internal/mako/patch.py +++ b/ddtrace/contrib/internal/mako/patch.py @@ -4,9 +4,9 @@ from ddtrace import config from ddtrace.constants import SPAN_MEASURED_KEY -from ddtrace.contrib.trace_utils import int_service -from ddtrace.contrib.trace_utils import unwrap as _u -from ddtrace.contrib.trace_utils import wrap as _w +from ddtrace.contrib.internal.trace_utils import int_service +from ddtrace.contrib.internal.trace_utils import unwrap as _u +from ddtrace.contrib.internal.trace_utils import wrap as _w from ddtrace.ext import SpanTypes from ddtrace.internal.constants import COMPONENT from ddtrace.internal.schema import schematize_service_name diff --git a/ddtrace/contrib/internal/molten/patch.py b/ddtrace/contrib/internal/molten/patch.py index 7c60d37d0d6..6fc237a5b58 100644 --- a/ddtrace/contrib/internal/molten/patch.py +++ b/ddtrace/contrib/internal/molten/patch.py @@ -9,7 +9,7 @@ from ddtrace.constants import SPAN_KIND from ddtrace.constants import SPAN_MEASURED_KEY from ddtrace.contrib import trace_utils -from ddtrace.contrib.trace_utils import unwrap as _u +from ddtrace.contrib.internal.trace_utils import unwrap as _u from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes from ddtrace.internal.compat import urlencode diff --git a/ddtrace/contrib/internal/mysqldb/patch.py b/ddtrace/contrib/internal/mysqldb/patch.py index 0294e5a2bc6..e4d124ee1f6 100644 --- a/ddtrace/contrib/internal/mysqldb/patch.py +++ b/ddtrace/contrib/internal/mysqldb/patch.py @@ -10,7 +10,7 @@ from ddtrace.constants import SPAN_MEASURED_KEY from ddtrace.contrib.dbapi import TracedConnection from ddtrace.contrib.internal.trace_utils import _convert_to_string -from ddtrace.contrib.trace_utils import ext_service +from ddtrace.contrib.internal.trace_utils import ext_service from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes from ddtrace.ext import db diff --git a/ddtrace/contrib/internal/psycopg/async_connection.py b/ddtrace/contrib/internal/psycopg/async_connection.py index 72c8d70e7ec..8d400330ff5 100644 --- a/ddtrace/contrib/internal/psycopg/async_connection.py +++ b/ddtrace/contrib/internal/psycopg/async_connection.py @@ -5,7 +5,7 @@ from ddtrace.contrib.internal.psycopg.async_cursor import Psycopg3FetchTracedAsyncCursor from ddtrace.contrib.internal.psycopg.async_cursor import Psycopg3TracedAsyncCursor from ddtrace.contrib.internal.psycopg.connection import patch_conn -from ddtrace.contrib.trace_utils import ext_service +from ddtrace.contrib.internal.trace_utils import ext_service from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes from ddtrace.ext import db diff --git a/ddtrace/contrib/internal/psycopg/connection.py b/ddtrace/contrib/internal/psycopg/connection.py index a5e5353ad13..c3b7caef2c4 100644 --- a/ddtrace/contrib/internal/psycopg/connection.py +++ b/ddtrace/contrib/internal/psycopg/connection.py @@ -7,7 +7,7 @@ from ddtrace.contrib.internal.psycopg.cursor import Psycopg3FetchTracedCursor from ddtrace.contrib.internal.psycopg.cursor import Psycopg3TracedCursor from ddtrace.contrib.internal.psycopg.extensions import _patch_extensions -from ddtrace.contrib.trace_utils import ext_service +from ddtrace.contrib.internal.trace_utils import ext_service from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes from ddtrace.ext import db diff --git a/ddtrace/contrib/internal/pynamodb/patch.py b/ddtrace/contrib/internal/pynamodb/patch.py index be4ba00c893..93171d176bd 100644 --- a/ddtrace/contrib/internal/pynamodb/patch.py +++ b/ddtrace/contrib/internal/pynamodb/patch.py @@ -10,7 +10,7 @@ from ddtrace.constants import SPAN_KIND from ddtrace.constants import SPAN_MEASURED_KEY from ddtrace.contrib import trace_utils -from ddtrace.contrib.trace_utils import unwrap +from ddtrace.contrib.internal.trace_utils import unwrap from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes from ddtrace.ext import db diff --git a/ddtrace/contrib/internal/pyodbc/patch.py b/ddtrace/contrib/internal/pyodbc/patch.py index 180895a202e..8b51c5b425a 100644 --- a/ddtrace/contrib/internal/pyodbc/patch.py +++ b/ddtrace/contrib/internal/pyodbc/patch.py @@ -5,8 +5,8 @@ from ddtrace import config from ddtrace.contrib.dbapi import TracedConnection from ddtrace.contrib.dbapi import TracedCursor -from ddtrace.contrib.trace_utils import unwrap -from ddtrace.contrib.trace_utils import wrap +from ddtrace.contrib.internal.trace_utils import unwrap +from ddtrace.contrib.internal.trace_utils import wrap from ddtrace.ext import db from ddtrace.internal.schema import schematize_service_name from ddtrace.internal.utils.formats import asbool diff --git a/ddtrace/contrib/internal/redis/patch.py b/ddtrace/contrib/internal/redis/patch.py index 33520e5894d..28beeb4d979 100644 --- a/ddtrace/contrib/internal/redis/patch.py +++ b/ddtrace/contrib/internal/redis/patch.py @@ -6,9 +6,9 @@ from ddtrace import config from ddtrace._trace.utils_redis import _instrument_redis_cmd from ddtrace._trace.utils_redis import _instrument_redis_execute_pipeline +from ddtrace.contrib.internal.trace_utils import unwrap from ddtrace.contrib.redis_utils import ROW_RETURNING_COMMANDS from ddtrace.contrib.redis_utils import determine_row_count -from ddtrace.contrib.trace_utils import unwrap from ddtrace.internal import core from ddtrace.internal.schema import schematize_service_name from ddtrace.internal.utils.formats import CMD_MAX_LEN diff --git a/ddtrace/contrib/internal/requests/patch.py b/ddtrace/contrib/internal/requests/patch.py index d4ec1f5182d..6485bb7aed8 100644 --- a/ddtrace/contrib/internal/requests/patch.py +++ b/ddtrace/contrib/internal/requests/patch.py @@ -7,7 +7,7 @@ from ddtrace.appsec._common_module_patches import wrapped_request_D8CB81E472AF98A2 as _wrap_request from ddtrace.appsec._iast._metrics import _set_metric_iast_instrumented_sink from ddtrace.appsec._iast.constants import VULN_SSRF -from ddtrace.contrib.trace_utils import unwrap as _u +from ddtrace.contrib.internal.trace_utils import unwrap as _u from ddtrace.internal.schema import schematize_service_name from ddtrace.internal.utils.formats import asbool from ddtrace.settings.asm import config as asm_config diff --git a/ddtrace/contrib/internal/snowflake/patch.py b/ddtrace/contrib/internal/snowflake/patch.py index d28844ea992..86d11bb9372 100644 --- a/ddtrace/contrib/internal/snowflake/patch.py +++ b/ddtrace/contrib/internal/snowflake/patch.py @@ -5,7 +5,7 @@ from ddtrace import config from ddtrace.contrib.dbapi import TracedConnection from ddtrace.contrib.dbapi import TracedCursor -from ddtrace.contrib.trace_utils import unwrap +from ddtrace.contrib.internal.trace_utils import unwrap from ddtrace.ext import db from ddtrace.ext import net from ddtrace.internal.schema import schematize_service_name diff --git a/ddtrace/contrib/internal/sqlalchemy/patch.py b/ddtrace/contrib/internal/sqlalchemy/patch.py index 399a3bfdb32..916cc53daa4 100644 --- a/ddtrace/contrib/internal/sqlalchemy/patch.py +++ b/ddtrace/contrib/internal/sqlalchemy/patch.py @@ -3,7 +3,7 @@ from ddtrace.appsec._iast._metrics import _set_metric_iast_instrumented_sink from ddtrace.appsec._iast.constants import VULN_SQL_INJECTION -from ddtrace.contrib.trace_utils import unwrap +from ddtrace.contrib.internal.trace_utils import unwrap from ddtrace.settings.asm import config as asm_config from .engine import _wrap_create_engine diff --git a/ddtrace/contrib/internal/starlette/patch.py b/ddtrace/contrib/internal/starlette/patch.py index 064722b67f1..e2ea8f9f4dc 100644 --- a/ddtrace/contrib/internal/starlette/patch.py +++ b/ddtrace/contrib/internal/starlette/patch.py @@ -17,7 +17,7 @@ from ddtrace.appsec._iast import _is_iast_enabled from ddtrace.contrib import trace_utils from ddtrace.contrib.asgi import TraceMiddleware -from ddtrace.contrib.trace_utils import with_traced_module +from ddtrace.contrib.internal.trace_utils import with_traced_module from ddtrace.ext import http from ddtrace.internal import core from ddtrace.internal._exceptions import BlockingException diff --git a/ddtrace/contrib/internal/structlog/patch.py b/ddtrace/contrib/internal/structlog/patch.py index b0ccd3c88db..d529b8d33a6 100644 --- a/ddtrace/contrib/internal/structlog/patch.py +++ b/ddtrace/contrib/internal/structlog/patch.py @@ -8,8 +8,8 @@ from ddtrace.contrib.internal.logging.constants import RECORD_ATTR_TRACE_ID from ddtrace.contrib.internal.logging.constants import RECORD_ATTR_VALUE_EMPTY from ddtrace.contrib.internal.logging.constants import RECORD_ATTR_VERSION -from ddtrace.contrib.trace_utils import unwrap as _u -from ddtrace.contrib.trace_utils import wrap as _w +from ddtrace.contrib.internal.trace_utils import unwrap as _u +from ddtrace.contrib.internal.trace_utils import wrap as _w from ddtrace.internal.utils import get_argument_value from ddtrace.internal.utils import set_argument_value diff --git a/ddtrace/contrib/internal/tornado/handlers.py b/ddtrace/contrib/internal/tornado/handlers.py index f5d6955cdee..ff3a97cf2b8 100644 --- a/ddtrace/contrib/internal/tornado/handlers.py +++ b/ddtrace/contrib/internal/tornado/handlers.py @@ -7,7 +7,7 @@ from ddtrace.constants import SPAN_KIND from ddtrace.constants import SPAN_MEASURED_KEY from ddtrace.contrib import trace_utils -from ddtrace.contrib.trace_utils import set_http_meta +from ddtrace.contrib.internal.trace_utils import set_http_meta from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes from ddtrace.internal.constants import COMPONENT diff --git a/ddtrace/contrib/internal/urllib/patch.py b/ddtrace/contrib/internal/urllib/patch.py index b71b2a85392..ed5e2891f06 100644 --- a/ddtrace/contrib/internal/urllib/patch.py +++ b/ddtrace/contrib/internal/urllib/patch.py @@ -5,7 +5,7 @@ from ddtrace.appsec._common_module_patches import wrapped_request_D8CB81E472AF98A2 as _wrap_open from ddtrace.appsec._iast._metrics import _set_metric_iast_instrumented_sink from ddtrace.appsec._iast.constants import VULN_SSRF -from ddtrace.contrib.trace_utils import unwrap as _u +from ddtrace.contrib.internal.trace_utils import unwrap as _u from ddtrace.settings.asm import config as asm_config diff --git a/ddtrace/contrib/internal/vertexai/patch.py b/ddtrace/contrib/internal/vertexai/patch.py index bc6e46903c3..82ae00e1a77 100644 --- a/ddtrace/contrib/internal/vertexai/patch.py +++ b/ddtrace/contrib/internal/vertexai/patch.py @@ -4,13 +4,13 @@ import vertexai from ddtrace import config +from ddtrace.contrib.internal.trace_utils import unwrap +from ddtrace.contrib.internal.trace_utils import with_traced_module +from ddtrace.contrib.internal.trace_utils import wrap from ddtrace.contrib.internal.vertexai._utils import TracedAsyncVertexAIStreamResponse from ddtrace.contrib.internal.vertexai._utils import TracedVertexAIStreamResponse from ddtrace.contrib.internal.vertexai._utils import tag_request from ddtrace.contrib.internal.vertexai._utils import tag_response -from ddtrace.contrib.trace_utils import unwrap -from ddtrace.contrib.trace_utils import with_traced_module -from ddtrace.contrib.trace_utils import wrap from ddtrace.llmobs._integrations import VertexAIIntegration from ddtrace.llmobs._integrations.utils import extract_model_name_google from ddtrace.trace import Pin diff --git a/ddtrace/contrib/internal/webbrowser/patch.py b/ddtrace/contrib/internal/webbrowser/patch.py index 965425d9ad5..1387df37ac9 100644 --- a/ddtrace/contrib/internal/webbrowser/patch.py +++ b/ddtrace/contrib/internal/webbrowser/patch.py @@ -5,7 +5,7 @@ from ddtrace.appsec._common_module_patches import wrapped_request_D8CB81E472AF98A2 as _wrap_open from ddtrace.appsec._iast._metrics import _set_metric_iast_instrumented_sink from ddtrace.appsec._iast.constants import VULN_SSRF -from ddtrace.contrib.trace_utils import unwrap as _u +from ddtrace.contrib.internal.trace_utils import unwrap as _u from ddtrace.settings.asm import config as asm_config diff --git a/ddtrace/contrib/redis_utils.py b/ddtrace/contrib/redis_utils.py index 3e743273d41..f802b198b81 100644 --- a/ddtrace/contrib/redis_utils.py +++ b/ddtrace/contrib/redis_utils.py @@ -5,7 +5,7 @@ deprecate( "The ddtrace.contrib.redis_utils module is deprecated", - message="Import from ``ddtrace.contrib.trace_utils`` instead.", + message="Import from ``ddtrace.contrib.internal.trace_utils`` instead.", category=DDTraceDeprecationWarning, removal_version="3.0.0", ) diff --git a/ddtrace/contrib/trace_utils_async.py b/ddtrace/contrib/trace_utils_async.py index ad74c474a04..73258af46b9 100644 --- a/ddtrace/contrib/trace_utils_async.py +++ b/ddtrace/contrib/trace_utils_async.py @@ -4,8 +4,8 @@ deprecate( - "The ddtrace.contrib.trace_utils_async module is deprecated", - message="Import from ``ddtrace.contrib.trace_utils`` instead.", + "The ddtrace.contrib.internal.trace_utils_async module is deprecated", + message="Import from ``ddtrace.contrib.internal.trace_utils`` instead.", category=DDTraceDeprecationWarning, removal_version="3.0.0", ) diff --git a/ddtrace/contrib/trace_utils_redis.py b/ddtrace/contrib/trace_utils_redis.py index 151f5d90e12..ad30d38a86e 100644 --- a/ddtrace/contrib/trace_utils_redis.py +++ b/ddtrace/contrib/trace_utils_redis.py @@ -5,8 +5,8 @@ deprecate( - "The ddtrace.contrib.trace_utils_redis module is deprecated", - message="Import from ``ddtrace.contrib.trace_utils`` instead.", + "The ddtrace.contrib.internal.trace_utils_redis module is deprecated", + message="Import from ``ddtrace.contrib.internal.trace_utils`` instead.", category=DDTraceDeprecationWarning, removal_version="3.0.0", ) diff --git a/ddtrace/llmobs/_integrations/base.py b/ddtrace/llmobs/_integrations/base.py index 2e892904720..c8f743fa4de 100644 --- a/ddtrace/llmobs/_integrations/base.py +++ b/ddtrace/llmobs/_integrations/base.py @@ -10,7 +10,7 @@ from ddtrace._trace.sampler import RateSampler from ddtrace._trace.span import Span from ddtrace.constants import SPAN_MEASURED_KEY -from ddtrace.contrib.trace_utils import int_service +from ddtrace.contrib.internal.trace_utils import int_service from ddtrace.ext import SpanTypes from ddtrace.internal.agent import get_stats_url from ddtrace.internal.dogstatsd import get_dogstatsd_client diff --git a/tests/appsec/appsec/test_appsec_trace_utils.py b/tests/appsec/appsec/test_appsec_trace_utils.py index b550b103782..b4dbf97c8e2 100644 --- a/tests/appsec/appsec/test_appsec_trace_utils.py +++ b/tests/appsec/appsec/test_appsec_trace_utils.py @@ -11,7 +11,7 @@ from ddtrace.appsec.trace_utils import track_user_login_failure_event from ddtrace.appsec.trace_utils import track_user_login_success_event from ddtrace.appsec.trace_utils import track_user_signup_event -from ddtrace.contrib.trace_utils import set_user +from ddtrace.contrib.internal.trace_utils import set_user from ddtrace.ext import user import tests.appsec.rules as rules from tests.appsec.utils import asm_context diff --git a/tests/appsec/appsec/test_asm_standalone.py b/tests/appsec/appsec/test_asm_standalone.py index 3c2ed58caf6..fc59b1ae3d0 100644 --- a/tests/appsec/appsec/test_asm_standalone.py +++ b/tests/appsec/appsec/test_asm_standalone.py @@ -4,7 +4,7 @@ import pytest import ddtrace -from ddtrace.contrib.trace_utils import set_http_meta +from ddtrace.contrib.internal.trace_utils import set_http_meta from ddtrace.ext import SpanTypes from tests.utils import override_env diff --git a/tests/appsec/appsec/test_processor.py b/tests/appsec/appsec/test_processor.py index ad3c9e6827a..fef06835086 100644 --- a/tests/appsec/appsec/test_processor.py +++ b/tests/appsec/appsec/test_processor.py @@ -14,7 +14,7 @@ from ddtrace.appsec._processor import _transform_headers from ddtrace.appsec._utils import get_triggers from ddtrace.constants import USER_KEEP -from ddtrace.contrib.trace_utils import set_http_meta +from ddtrace.contrib.internal.trace_utils import set_http_meta from ddtrace.ext import SpanTypes from ddtrace.internal import core import tests.appsec.rules as rules diff --git a/tests/appsec/appsec/test_remoteconfiguration.py b/tests/appsec/appsec/test_remoteconfiguration.py index 1d2c47bc190..319b45b5c1e 100644 --- a/tests/appsec/appsec/test_remoteconfiguration.py +++ b/tests/appsec/appsec/test_remoteconfiguration.py @@ -19,7 +19,7 @@ from ddtrace.appsec._remoteconfiguration import disable_appsec_rc from ddtrace.appsec._remoteconfiguration import enable_appsec_rc from ddtrace.appsec._utils import get_triggers -from ddtrace.contrib.trace_utils import set_http_meta +from ddtrace.contrib.internal.trace_utils import set_http_meta from ddtrace.internal import core from ddtrace.internal.remoteconfig.client import AgentPayload from ddtrace.internal.remoteconfig.client import ConfigMetadata diff --git a/tests/appsec/appsec/test_telemetry.py b/tests/appsec/appsec/test_telemetry.py index 6932a7bdba0..06a1c790f2b 100644 --- a/tests/appsec/appsec/test_telemetry.py +++ b/tests/appsec/appsec/test_telemetry.py @@ -10,7 +10,7 @@ import ddtrace.appsec._ddwaf.ddwaf_types from ddtrace.appsec._deduplications import deduplication from ddtrace.appsec._processor import AppSecSpanProcessor -from ddtrace.contrib.trace_utils import set_http_meta +from ddtrace.contrib.internal.trace_utils import set_http_meta from ddtrace.ext import SpanTypes from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE from ddtrace.internal.telemetry.constants import TELEMETRY_TYPE_DISTRIBUTION diff --git a/tests/contrib/asyncpg/test_asyncpg.py b/tests/contrib/asyncpg/test_asyncpg.py index 032a0f91731..c60bfae1456 100644 --- a/tests/contrib/asyncpg/test_asyncpg.py +++ b/tests/contrib/asyncpg/test_asyncpg.py @@ -8,7 +8,7 @@ from ddtrace import tracer from ddtrace.contrib.internal.asyncpg.patch import patch from ddtrace.contrib.internal.asyncpg.patch import unpatch -from ddtrace.contrib.trace_utils import iswrapped +from ddtrace.contrib.internal.trace_utils import iswrapped from ddtrace.trace import Pin from tests.contrib.asyncio.utils import AsyncioTestCase from tests.contrib.asyncio.utils import mark_asyncio diff --git a/tests/contrib/django/views.py b/tests/contrib/django/views.py index b908fa2eee0..f1989c374d7 100644 --- a/tests/contrib/django/views.py +++ b/tests/contrib/django/views.py @@ -16,7 +16,7 @@ from django.views.generic import View from ddtrace import tracer -from ddtrace.contrib.trace_utils import set_user +from ddtrace.contrib.internal.trace_utils import set_user class UserList(ListView): diff --git a/tests/contrib/flask/app.py b/tests/contrib/flask/app.py index 82059ce0eaa..e2b99ab513a 100644 --- a/tests/contrib/flask/app.py +++ b/tests/contrib/flask/app.py @@ -8,7 +8,7 @@ from ddtrace import tracer from ddtrace.appsec._trace_utils import block_request_if_user_blocked -from ddtrace.contrib.trace_utils import set_user +from ddtrace.contrib.internal.trace_utils import set_user from tests.webclient import PingFilter diff --git a/tests/contrib/openai/test_openai_v0.py b/tests/contrib/openai/test_openai_v0.py index c9ab8aac716..2651a51218a 100644 --- a/tests/contrib/openai/test_openai_v0.py +++ b/tests/contrib/openai/test_openai_v0.py @@ -11,7 +11,7 @@ import ddtrace from ddtrace import patch from ddtrace.contrib.internal.openai.utils import _est_tokens -from ddtrace.contrib.trace_utils import iswrapped +from ddtrace.contrib.internal.trace_utils import iswrapped from ddtrace.internal.utils.version import parse_version from tests.contrib.openai.utils import chat_completion_custom_functions from tests.contrib.openai.utils import chat_completion_input_description diff --git a/tests/contrib/openai/test_openai_v1.py b/tests/contrib/openai/test_openai_v1.py index 9c9738cbb69..b2cc2a4cd6d 100644 --- a/tests/contrib/openai/test_openai_v1.py +++ b/tests/contrib/openai/test_openai_v1.py @@ -7,7 +7,7 @@ import ddtrace from ddtrace import patch from ddtrace.contrib.internal.openai.utils import _est_tokens -from ddtrace.contrib.trace_utils import iswrapped +from ddtrace.contrib.internal.trace_utils import iswrapped from ddtrace.internal.utils.version import parse_version from tests.contrib.openai.utils import chat_completion_custom_functions from tests.contrib.openai.utils import chat_completion_input_description diff --git a/tests/internal/test_module.py b/tests/internal/test_module.py index 885f796af81..348272c7d23 100644 --- a/tests/internal/test_module.py +++ b/tests/internal/test_module.py @@ -575,9 +575,9 @@ def __getattr__(name): # Note: The following ddtrace.contrib modules are expected to be part of the public API # TODO: Revisit whether integration utils should be part of the public API "ddtrace.contrib.redis_utils", - "ddtrace.contrib.trace_utils", - "ddtrace.contrib.trace_utils_async", - "ddtrace.contrib.trace_utils_redis", + "ddtrace.contrib.internal.trace_utils", + "ddtrace.contrib.internal.trace_utils_async", + "ddtrace.contrib.internal.trace_utils_redis", # TODO: The following contrib modules are part of the public API (unlike most integrations). # We should consider privatizing the internals of these integrations. "ddtrace.contrib.unittest.patch", diff --git a/tests/tracer/test_trace_utils.py b/tests/tracer/test_trace_utils.py index b2cdabb91d0..6110494ddac 100644 --- a/tests/tracer/test_trace_utils.py +++ b/tests/tracer/test_trace_utils.py @@ -316,7 +316,7 @@ def test_ext_service(int_config, pin, config_val, default, expected): def test_set_http_meta_with_http_header_tags_config(): from ddtrace import config from ddtrace._trace.span import Span - from ddtrace.contrib.trace_utils import set_http_meta + from ddtrace.contrib.internal.trace_utils import set_http_meta assert config._trace_http_header_tags == { "header1": "", @@ -512,7 +512,7 @@ def test_set_http_meta_custom_errors(mock_log, span, int_config, error_codes, st def test_set_http_meta_custom_errors_via_env(): from ddtrace import config from ddtrace import tracer - from ddtrace.contrib.trace_utils import set_http_meta + from ddtrace.contrib.internal.trace_utils import set_http_meta config._add("myint", dict()) with tracer.trace("error") as span1: @@ -528,7 +528,7 @@ def test_set_http_meta_custom_errors_via_env(): assert span3.error == 0 -@mock.patch("ddtrace.contrib.trace_utils._store_headers") +@mock.patch("ddtrace.contrib.internal.trace_utils._store_headers") def test_set_http_meta_no_headers(mock_store_headers, span, int_config): assert int_config.myint.is_header_tracing_configured is False trace_utils.set_http_meta( @@ -543,7 +543,7 @@ def test_set_http_meta_no_headers(mock_store_headers, span, int_config): mock_store_headers.assert_not_called() -@mock.patch("ddtrace.contrib.trace_utils._store_headers") +@mock.patch("ddtrace.contrib.internal.trace_utils._store_headers") @pytest.mark.parametrize( "user_agent_key,user_agent_value,expected_keys,expected", [ @@ -572,7 +572,7 @@ def test_set_http_meta_headers_useragent( mock_store_headers.assert_called() -@mock.patch("ddtrace.contrib.trace_utils._store_headers") +@mock.patch("ddtrace.contrib.internal.trace_utils._store_headers") def test_set_http_meta_case_sensitive_headers(mock_store_headers, span, int_config): int_config.myint.http._header_tags = {"enabled": True} trace_utils.set_http_meta( @@ -585,7 +585,7 @@ def test_set_http_meta_case_sensitive_headers(mock_store_headers, span, int_conf mock_store_headers.assert_called() -@mock.patch("ddtrace.contrib.trace_utils._store_headers") +@mock.patch("ddtrace.contrib.internal.trace_utils._store_headers") def test_set_http_meta_case_sensitive_headers_notfound(mock_store_headers, span, int_config): int_config.myint.http._header_tags = {"enabled": True} trace_utils.set_http_meta( @@ -810,7 +810,7 @@ def test_ip_subnet_regression(): assert not ip_network(req_ip).subnet_of(ip_network(del_ip)) -@mock.patch("ddtrace.contrib.trace_utils._store_headers") +@mock.patch("ddtrace.contrib.internal.trace_utils._store_headers") @pytest.mark.parametrize( "user_agent_value, expected_keys ,expected", [ @@ -835,7 +835,7 @@ def test_set_http_meta_headers_useragent( # noqa:F811 mock_store_headers.assert_not_called() -@mock.patch("ddtrace.contrib.trace_utils.log") +@mock.patch("ddtrace.contrib.internal.trace_utils.log") @pytest.mark.parametrize( "val, bad", [ @@ -1042,7 +1042,7 @@ def test_sanitized_url_in_http_meta(span, int_config): def test_url_in_http_with_empty_obfuscation_regex(): from ddtrace import config from ddtrace import tracer - from ddtrace.contrib.trace_utils import set_http_meta + from ddtrace.contrib.internal.trace_utils import set_http_meta from ddtrace.ext import http assert config._obfuscation_query_string_pattern.pattern == b"", config._obfuscation_query_string_pattern @@ -1067,7 +1067,7 @@ def test_url_in_http_with_obfuscation_enabled_and_empty_regex(): # and obfuscation is enabled (not disabled xD) from ddtrace import config from ddtrace import tracer - from ddtrace.contrib.trace_utils import set_http_meta + from ddtrace.contrib.internal.trace_utils import set_http_meta from ddtrace.ext import http # assert obfuscation is disabled when the regex is an empty string diff --git a/tests/tracer/test_tracer.py b/tests/tracer/test_tracer.py index 164b04ee5d1..2b030b57161 100644 --- a/tests/tracer/test_tracer.py +++ b/tests/tracer/test_tracer.py @@ -30,7 +30,7 @@ from ddtrace.constants import USER_KEEP from ddtrace.constants import USER_REJECT from ddtrace.constants import VERSION_KEY -from ddtrace.contrib.trace_utils import set_user +from ddtrace.contrib.internal.trace_utils import set_user from ddtrace.ext import user from ddtrace.internal._encoding import MsgpackEncoderV04 from ddtrace.internal._encoding import MsgpackEncoderV05 From fe4ef73bbaecbc8ed409d2a9547d7e033d2886c3 Mon Sep 17 00:00:00 2001 From: Munir Abdinur Date: Fri, 17 Jan 2025 16:12:26 -0500 Subject: [PATCH 4/6] update internal functions --- ddtrace/appsec/_processor.py | 2 +- ddtrace/contrib/internal/aiomysql/patch.py | 3 ++- ddtrace/contrib/internal/django/patch.py | 3 ++- ddtrace/contrib/internal/requests/connection.py | 3 ++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/ddtrace/appsec/_processor.py b/ddtrace/appsec/_processor.py index b9ed07c7959..3b26df50a04 100644 --- a/ddtrace/appsec/_processor.py +++ b/ddtrace/appsec/_processor.py @@ -219,7 +219,7 @@ def rasp_sqli_enabled(self) -> bool: return WAF_DATA_NAMES.SQLI_ADDRESS in self._addresses_to_keep def on_span_start(self, span: Span) -> None: - from ddtrace.contrib import trace_utils + from ddtrace.contrib.internal import trace_utils if span.span_type not in {SpanTypes.WEB, SpanTypes.GRPC}: return diff --git a/ddtrace/contrib/internal/aiomysql/patch.py b/ddtrace/contrib/internal/aiomysql/patch.py index 0053e4f8a5b..193d471d124 100644 --- a/ddtrace/contrib/internal/aiomysql/patch.py +++ b/ddtrace/contrib/internal/aiomysql/patch.py @@ -7,6 +7,7 @@ from ddtrace.constants import SPAN_MEASURED_KEY from ddtrace.contrib import dbapi from ddtrace.contrib import trace_utils +from ddtrace.contrib.internal.trace_utils import _convert_to_string from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes from ddtrace.ext import db @@ -48,7 +49,7 @@ async def patched_connect(connect_func, _, args, kwargs): tags = {} for tag, attr in CONN_ATTR_BY_TAG.items(): if hasattr(conn, attr): - tags[tag] = trace_utils._convert_to_string(getattr(conn, attr, None)) + tags[tag] = _convert_to_string(getattr(conn, attr, None)) tags[db.SYSTEM] = "mysql" c = AIOTracedConnection(conn) diff --git a/ddtrace/contrib/internal/django/patch.py b/ddtrace/contrib/internal/django/patch.py index a55bab550cd..7378d8da31c 100644 --- a/ddtrace/contrib/internal/django/patch.py +++ b/ddtrace/contrib/internal/django/patch.py @@ -23,6 +23,7 @@ from ddtrace.constants import SPAN_KIND from ddtrace.contrib import dbapi from ddtrace.contrib import trace_utils +from ddtrace.contrib.internal.trace_utils import _convert_to_string from ddtrace.contrib.internal.trace_utils import _get_request_header_user_agent from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes @@ -127,7 +128,7 @@ def patch_conn(django, conn): for tag, attr in DB_CONN_ATTR_BY_TAG.items(): if attr in settings_dict: try: - tags[tag] = trace_utils._convert_to_string(conn.settings_dict.get(attr)) + tags[tag] = _convert_to_string(conn.settings_dict.get(attr)) except Exception: tags[tag] = str(conn.settings_dict.get(attr)) conn._datadog_tags = tags diff --git a/ddtrace/contrib/internal/requests/connection.py b/ddtrace/contrib/internal/requests/connection.py index 06d3347f0a1..9ad198faacd 100644 --- a/ddtrace/contrib/internal/requests/connection.py +++ b/ddtrace/contrib/internal/requests/connection.py @@ -6,6 +6,7 @@ from ddtrace.constants import SPAN_KIND from ddtrace.constants import SPAN_MEASURED_KEY from ddtrace.contrib import trace_utils +from ddtrace.contrib.internal.trace_utils import _sanitized_url from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes from ddtrace.internal.compat import parse @@ -67,7 +68,7 @@ def _wrap_send(func, instance, args, kwargs): if not request: return func(*args, **kwargs) - url = trace_utils._sanitized_url(request.url) + url = _sanitized_url(request.url) method = "" if request.method is not None: method = request.method.upper() From e8b34078663426aab4372710cfb9752ac4c98100 Mon Sep 17 00:00:00 2001 From: Munir Abdinur Date: Fri, 17 Jan 2025 16:33:01 -0500 Subject: [PATCH 5/6] rn --- .../notes/munir-refactor-trace-utils-e887e8da8a01430b.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 releasenotes/notes/munir-refactor-trace-utils-e887e8da8a01430b.yaml diff --git a/releasenotes/notes/munir-refactor-trace-utils-e887e8da8a01430b.yaml b/releasenotes/notes/munir-refactor-trace-utils-e887e8da8a01430b.yaml new file mode 100644 index 00000000000..6f0993f1335 --- /dev/null +++ b/releasenotes/notes/munir-refactor-trace-utils-e887e8da8a01430b.yaml @@ -0,0 +1,6 @@ +--- +deprecations: + - | + tracing: Deprecates ``ddtrace.contrib.trace_utils_async`` and ``ddtrace.contrib.redis_utils`` modules. + Moves ``ddtrace.contrib.trace_utils_async.with_traced_module`` to ``ddtrace.contrib.trace_utils.with_traced_module_async``. + Moves ``ddtrace.contrib.redis_utils.*`` to ``ddtrace.contrib.redis.*``. \ No newline at end of file From 4d9b132814c2ebf4cf73982ea01a0d61b21b9897 Mon Sep 17 00:00:00 2001 From: Munir Abdinur Date: Fri, 17 Jan 2025 17:07:29 -0500 Subject: [PATCH 6/6] fmt --- tests/tracer/test_trace_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tracer/test_trace_utils.py b/tests/tracer/test_trace_utils.py index 6110494ddac..38ba097f308 100644 --- a/tests/tracer/test_trace_utils.py +++ b/tests/tracer/test_trace_utils.py @@ -17,7 +17,7 @@ from ddtrace import config from ddtrace._trace.context import Context from ddtrace._trace.span import Span -from ddtrace.contrib import trace_utils +from ddtrace.contrib.internal import trace_utils from ddtrace.contrib.internal.trace_utils import _get_request_header_client_ip from ddtrace.ext import SpanTypes from ddtrace.ext import http