diff --git a/ddtrace/__init__.py b/ddtrace/__init__.py index b555d1117ca..e480851926f 100644 --- a/ddtrace/__init__.py +++ b/ddtrace/__init__.py @@ -17,7 +17,7 @@ # configure ddtrace logger before other modules log configure_ddtrace_logger() # noqa: E402 -from .settings import _config as config +from .settings import _global_config as config # Enable telemetry writer and excepthook as early as possible to ensure we capture any exceptions from initialization diff --git a/ddtrace/_monkey.py b/ddtrace/_monkey.py index 75c70114ef2..8ede9f49ca4 100644 --- a/ddtrace/_monkey.py +++ b/ddtrace/_monkey.py @@ -12,7 +12,7 @@ from .internal import telemetry from .internal.logger import get_logger from .internal.utils import formats -from .settings import _config as config +from .settings import _global_config as config if TYPE_CHECKING: # pragma: no cover diff --git a/ddtrace/_trace/sampler.py b/ddtrace/_trace/sampler.py index 961526e3f26..96d61b9adcf 100644 --- a/ddtrace/_trace/sampler.py +++ b/ddtrace/_trace/sampler.py @@ -22,7 +22,7 @@ from ..internal.rate_limiter import RateLimiter from ..internal.sampling import _get_highest_precedence_rule_matching from ..internal.sampling import _set_sampling_tags -from ..settings import _config as ddconfig +from ..settings import _global_config as ddconfig from .sampling_rule import SamplingRule diff --git a/ddtrace/contrib/flask/__init__.py b/ddtrace/contrib/flask/__init__.py index 0562240d090..4b3c1afbf16 100644 --- a/ddtrace/contrib/flask/__init__.py +++ b/ddtrace/contrib/flask/__init__.py @@ -87,9 +87,6 @@ def index(): # Override service name config.flask['service_name'] = 'custom-service-name' - # Report 401, and 403 responses as errors - config.http_server.error_statuses = '401,403' - .. __: http://flask.pocoo.org/ :ref:`All HTTP tags ` are supported for this integration. diff --git a/ddtrace/contrib/httplib/__init__.py b/ddtrace/contrib/httplib/__init__.py index ae85051517e..7c5247422a1 100644 --- a/ddtrace/contrib/httplib/__init__.py +++ b/ddtrace/contrib/httplib/__init__.py @@ -42,20 +42,8 @@ # Disable distributed tracing globally. config.httplib['distributed_tracing'] = False - - # Change the service distributed tracing option only for this HTTP - # connection. - - # Python 2 - connection = urllib.HTTPConnection('www.datadog.com') - - # Python 3 connection = http.client.HTTPConnection('www.datadog.com') - cfg = config.get_from(connection) - cfg['distributed_tracing'] = True - - :ref:`Headers tracing ` is supported for this integration. """ diff --git a/ddtrace/contrib/internal/aiohttp/middlewares.py b/ddtrace/contrib/internal/aiohttp/middlewares.py index 4f7abe5a12f..ddb2d35fbc6 100644 --- a/ddtrace/contrib/internal/aiohttp/middlewares.py +++ b/ddtrace/contrib/internal/aiohttp/middlewares.py @@ -108,7 +108,7 @@ def finish_request_span(request, response): # DEV: aiohttp is special case maintains separate configuration from config api trace_query_string = request[REQUEST_CONFIG_KEY].get("trace_query_string") if trace_query_string is None: - trace_query_string = config.http.trace_query_string + trace_query_string = config._http.trace_query_string if trace_query_string: request_span.set_tag_str(http.QUERY_STRING, request.query_string) diff --git a/ddtrace/contrib/internal/botocore/patch.py b/ddtrace/contrib/internal/botocore/patch.py index 734c429d798..61353e7b34e 100644 --- a/ddtrace/contrib/internal/botocore/patch.py +++ b/ddtrace/contrib/internal/botocore/patch.py @@ -33,7 +33,7 @@ from ddtrace.internal.utils.formats import asbool from ddtrace.internal.utils.formats import deep_getattr from ddtrace.llmobs._integrations import BedrockIntegration -from ddtrace.settings.config import Config +from ddtrace.settings._config import Config from ddtrace.trace import Pin from .services.bedrock import patched_bedrock_api_call diff --git a/ddtrace/contrib/internal/dramatiq/patch.py b/ddtrace/contrib/internal/dramatiq/patch.py index 08daad9d93c..a6ecbbfd5d4 100644 --- a/ddtrace/contrib/internal/dramatiq/patch.py +++ b/ddtrace/contrib/internal/dramatiq/patch.py @@ -11,7 +11,7 @@ from ddtrace.contrib import trace_utils from ddtrace.ext import SpanKind from ddtrace.ext import SpanTypes -from ddtrace.settings.config import Config +from ddtrace.settings._config import Config def get_version() -> str: diff --git a/ddtrace/contrib/internal/httplib/patch.py b/ddtrace/contrib/internal/httplib/patch.py index a1e367af3a1..79a8ea2816f 100644 --- a/ddtrace/contrib/internal/httplib/patch.py +++ b/ddtrace/contrib/internal/httplib/patch.py @@ -91,7 +91,7 @@ def _wrap_request(func, instance, args, kwargs): if should_skip_request(pin, instance): return func_to_call(*args, **kwargs) - cfg = config.get_from(instance) + cfg = config._get_from(instance) try: # Create a new span and attach to this instance (so we can retrieve/update/close later on the response) diff --git a/ddtrace/contrib/internal/requests/connection.py b/ddtrace/contrib/internal/requests/connection.py index c6d7706ef54..0b58f8b6dc5 100644 --- a/ddtrace/contrib/internal/requests/connection.py +++ b/ddtrace/contrib/internal/requests/connection.py @@ -75,7 +75,7 @@ def _wrap_send(func, instance, args, kwargs): hostname, path = _extract_hostname_and_path(url) host_without_port = hostname.split(":")[0] if hostname is not None else None - cfg = config.get_from(instance) + cfg = config._get_from(instance) service = None if cfg["split_by_domain"] and hostname: service = hostname @@ -97,7 +97,7 @@ def _wrap_send(func, instance, args, kwargs): # Configure trace search sample rate # DEV: analytics enabled on per-session basis - cfg = config.get_from(instance) + cfg = config._get_from(instance) analytics_enabled = cfg.get("analytics_enabled") if analytics_enabled: span.set_tag(_ANALYTICS_SAMPLE_RATE_KEY, cfg.get("analytics_sample_rate", True)) diff --git a/ddtrace/contrib/internal/sanic/patch.py b/ddtrace/contrib/internal/sanic/patch.py index 5d105cf2f32..8e53ed41dc8 100644 --- a/ddtrace/contrib/internal/sanic/patch.py +++ b/ddtrace/contrib/internal/sanic/patch.py @@ -273,7 +273,7 @@ async def sanic_http_lifecycle_exception(request, exception): # Do not attach exception for exceptions not considered as errors # ex: Http 400s # DEV: We still need to set `__dd_span_call_finish` below - if not hasattr(exception, "status_code") or config.http_server.is_error_code(exception.status_code): + if not hasattr(exception, "status_code") or config._http_server.is_error_code(exception.status_code): ex_type = type(exception) ex_tb = getattr(exception, "__traceback__", None) span.set_exc_info(ex_type, exception, ex_tb) diff --git a/ddtrace/contrib/internal/tornado/handlers.py b/ddtrace/contrib/internal/tornado/handlers.py index 3c4a046bfb9..f858e33ee29 100644 --- a/ddtrace/contrib/internal/tornado/handlers.py +++ b/ddtrace/contrib/internal/tornado/handlers.py @@ -140,7 +140,7 @@ def log_exception(func, handler, args, kwargs): # is not a 2xx. In this case we want to check the status code to be sure that # only 5xx are traced as errors, while any other HTTPError exception is handled as # usual. - if config.http_server.is_error_code(value.status_code): + if config._http_server.is_error_code(value.status_code): current_span.set_exc_info(*args) else: # any other uncaught exception should be reported as error diff --git a/ddtrace/contrib/internal/trace_utils.py b/ddtrace/contrib/internal/trace_utils.py index 56901934e83..5781c3f30df 100644 --- a/ddtrace/contrib/internal/trace_utils.py +++ b/ddtrace/contrib/internal/trace_utils.py @@ -485,7 +485,7 @@ def set_http_meta( 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): + if config._http_server.is_error_code(int_status_code): span.error = 1 if status_msg is not None: diff --git a/ddtrace/contrib/jinja2/__init__.py b/ddtrace/contrib/jinja2/__init__.py index cc8c6786b02..8437a51d9de 100644 --- a/ddtrace/contrib/jinja2/__init__.py +++ b/ddtrace/contrib/jinja2/__init__.py @@ -16,13 +16,13 @@ The library can be configured globally and per instance, using the Configuration API:: from ddtrace import config + from ddtrace.trace import Pin # Change service name globally config.jinja2['service_name'] = 'jinja-templates' # change the service name only for this environment - cfg = config.get_from(env) - cfg['service_name'] = 'jinja-templates' + Pin.override(env, service='jinja-templates') By default, the service name is set to None, so it is inherited from the parent span. If there is no parent span and the service name is not overridden the agent will drop the traces. diff --git a/ddtrace/contrib/requests/__init__.py b/ddtrace/contrib/requests/__init__.py index efcb20f1219..7d034ce56bf 100644 --- a/ddtrace/contrib/requests/__init__.py +++ b/ddtrace/contrib/requests/__init__.py @@ -65,12 +65,11 @@ use the config API:: from ddtrace import config + from ddtrace.trace import Pin from requests import Session session = Session() - cfg = config.get_from(session) - cfg['service_name'] = 'auth-api' - cfg['distributed_tracing'] = False + Pin.override(session, service='auth-api') """ diff --git a/ddtrace/internal/remoteconfig/worker.py b/ddtrace/internal/remoteconfig/worker.py index 7ad8c592d2e..5429e599e74 100644 --- a/ddtrace/internal/remoteconfig/worker.py +++ b/ddtrace/internal/remoteconfig/worker.py @@ -13,7 +13,7 @@ from ddtrace.internal.remoteconfig.utils import get_poll_interval_seconds from ddtrace.internal.service import ServiceStatus from ddtrace.internal.utils.time import StopWatch -from ddtrace.settings import _config as ddconfig +from ddtrace.settings import _global_config as ddconfig log = get_logger(__name__) diff --git a/ddtrace/internal/sampling.py b/ddtrace/internal/sampling.py index b1d3f7957f4..e64c0e27bc5 100644 --- a/ddtrace/internal/sampling.py +++ b/ddtrace/internal/sampling.py @@ -27,7 +27,7 @@ from ddtrace.internal.constants import SAMPLING_DECISION_TRACE_TAG_KEY from ddtrace.internal.glob_matching import GlobMatcher from ddtrace.internal.logger import get_logger -from ddtrace.settings import _config as config +from ddtrace.settings import _global_config as config from .rate_limiter import RateLimiter diff --git a/ddtrace/internal/writer/writer.py b/ddtrace/internal/writer/writer.py index c494aa206a2..357fcf3917f 100644 --- a/ddtrace/internal/writer/writer.py +++ b/ddtrace/internal/writer/writer.py @@ -14,7 +14,7 @@ import ddtrace from ddtrace.internal.utils.retry import fibonacci_backoff_with_jitter -from ddtrace.settings import _config as config +from ddtrace.settings import _global_config as config from ddtrace.settings.asm import config as asm_config from ddtrace.vendor.dogstatsd import DogStatsd diff --git a/ddtrace/propagation/_database_monitoring.py b/ddtrace/propagation/_database_monitoring.py index 5b585b13210..817d23c4ebf 100644 --- a/ddtrace/propagation/_database_monitoring.py +++ b/ddtrace/propagation/_database_monitoring.py @@ -10,7 +10,7 @@ from ..internal import compat from ..internal.utils import get_argument_value from ..internal.utils import set_argument_value -from ..settings import _config as dd_config +from ..settings import _global_config as dd_config from ..settings._database_monitoring import dbm_config diff --git a/ddtrace/settings/__init__.py b/ddtrace/settings/__init__.py index 2c3a0bf7807..ebbb0c31f7b 100644 --- a/ddtrace/settings/__init__.py +++ b/ddtrace/settings/__init__.py @@ -1,12 +1,12 @@ from .._hooks import Hooks -from .config import Config +from ._config import Config from .exceptions import ConfigException from .http import HttpConfig from .integration import IntegrationConfig # Default global config -_config = Config() +_global_config = Config() __all__ = [ "Config", diff --git a/ddtrace/settings/_config.py b/ddtrace/settings/_config.py new file mode 100644 index 00000000000..df3fe4177d1 --- /dev/null +++ b/ddtrace/settings/_config.py @@ -0,0 +1,1025 @@ +from copy import deepcopy +import json +import os +import re +import sys +from typing import Any # noqa:F401 +from typing import Callable # noqa:F401 +from typing import Dict # noqa:F401 +from typing import List # noqa:F401 +from typing import Optional # noqa:F401 +from typing import Tuple # noqa:F401 +from typing import Union # noqa:F401 + +from ddtrace.internal._file_queue import File_Queue +from ddtrace.internal.serverless import in_azure_function +from ddtrace.internal.serverless import in_gcp_function +from ddtrace.internal.telemetry import telemetry_writer +from ddtrace.internal.utils.cache import cachedmethod +from ddtrace.internal.utils.deprecations import DDTraceDeprecationWarning +from ddtrace.vendor.debtcollector import deprecate + +from .._trace.pin import Pin +from ..internal import gitmetadata +from ..internal.constants import _PROPAGATION_BEHAVIOR_DEFAULT +from ..internal.constants import _PROPAGATION_BEHAVIOR_IGNORE +from ..internal.constants import _PROPAGATION_STYLE_DEFAULT +from ..internal.constants import _PROPAGATION_STYLE_NONE +from ..internal.constants import DEFAULT_BUFFER_SIZE +from ..internal.constants import DEFAULT_MAX_PAYLOAD_SIZE +from ..internal.constants import DEFAULT_PROCESSING_INTERVAL +from ..internal.constants import DEFAULT_REUSE_CONNECTIONS +from ..internal.constants import DEFAULT_SAMPLING_RATE_LIMIT +from ..internal.constants import DEFAULT_TIMEOUT +from ..internal.constants import PROPAGATION_STYLE_ALL +from ..internal.constants import PROPAGATION_STYLE_B3_SINGLE +from ..internal.logger import get_logger +from ..internal.schema import DEFAULT_SPAN_SERVICE_NAME +from ..internal.serverless import in_aws_lambda +from ..internal.utils.formats import asbool +from ..internal.utils.formats import parse_tags_str +from ._core import get_config as _get_config +from ._inferred_base_service import detect_service +from ._otel_remapper import otel_remapping as _otel_remapping +from .endpoint_config import fetch_config_from_endpoint +from .http import HttpConfig +from .integration import IntegrationConfig + + +if sys.version_info >= (3, 8): + from typing import Literal # noqa:F401 +else: + from typing_extensions import Literal + + +log = get_logger(__name__) + +ENDPOINT_FETCHED_CONFIG = fetch_config_from_endpoint() + +DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP_DEFAULT = ( + r"(?ix)" + r"(?:" # JSON-ish leading quote + r'(?:"|%22)?' + r")" + r"(?:" # common keys" + r"(?:old[-_]?|new[-_]?)?p(?:ass)?w(?:or)?d(?:1|2)?" # pw, password variants + r"|pass(?:[-_]?phrase)?" # pass, passphrase variants + r"|secret" + r"|(?:" # key, key_id variants + r"api[-_]?" + r"|private[-_]?" + r"|public[-_]?" + r"|access[-_]?" + r"|secret[-_]?" + r"|app(?:lica" + r"tion)?[-_]?" + r")key(?:[-_]?id)?" + r"|token" + r"|consumer[-_]?(?:id|key|secret)" + r"|sign(?:ed|ature)?" + r"|auth(?:entication|orization)?" + r")" + r"(?:" + # '=' query string separator, plus value til next '&' separator + r"(?:\s|%20)*(?:=|%3D)[^&]+" + # JSON-ish '": "somevalue"', key being handled with case above, without the opening '"' + r'|(?:"|%22)' # closing '"' at end of key + r"(?:\s|%20)*(?::|%3A)(?:\s|%20)*" # ':' key-value separator, with surrounding spaces + r'(?:"|%22)' # opening '"' at start of value + r'(?:%2[^2]|%[^2]|[^"%])+' # value + r'(?:"|%22)' # closing '"' at end of value + r")" + r"|(?:" # other common secret values + r" bearer(?:\s|%20)+[a-z0-9._\-]+" + r"|token(?::|%3A)[a-z0-9]{13}" + r"|gh[opsu]_[0-9a-zA-Z]{36}" + r"|ey[I-L](?:[\w=-]|%3D)+\.ey[I-L](?:[\w=-]|%3D)+(?:\.(?:[\w.+/=-]|%3D|%2F|%2B)+)?" + r"|-{5}BEGIN(?:[a-z\s]|%20)+PRIVATE(?:\s|%20)KEY-{5}[^\-]+-{5}END" + r"(?:[a-z\s]|%20)+PRIVATE(?:\s|%20)KEY(?:-{5})?(?:\n|%0A)?" + r"|(?:ssh-(?:rsa|dss)|ecdsa-[a-z0-9]+-[a-z0-9]+)(?:\s|%20|%09)+(?:[a-z0-9/.+]" + r"|%2F|%5C|%2B){100,}(?:=|%3D)*(?:(?:\s|%20|%09)+[a-z0-9._-]+)?" + r")" +) + + +def _parse_propagation_styles(styles_str): + # type: (str) -> Optional[List[str]] + """Helper to parse http propagation extract/inject styles via env variables. + + The expected format is:: + +