Skip to content

refactor: resolve config-telemetry circular reference #12820

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Mar 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ddtrace/_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
from os import path
from typing import Optional

from ddtrace.internal.telemetry import get_config
from ddtrace.internal.utils.formats import asbool
from ddtrace.settings._core import get_config


DD_LOG_FORMAT = "%(asctime)s %(levelname)s [%(name)s] [%(filename)s:%(lineno)d] {}- %(message)s".format(
Expand Down
2 changes: 1 addition & 1 deletion ddtrace/_trace/tracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
from ddtrace.internal.writer import AgentWriter
from ddtrace.internal.writer import LogWriter
from ddtrace.internal.writer import TraceWriter
from ddtrace.settings import Config
from ddtrace.settings._config import Config
from ddtrace.settings.asm import config as asm_config
from ddtrace.settings.peer_service import _ps_config

Expand Down
2 changes: 1 addition & 1 deletion ddtrace/contrib/internal/aiohttp/patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
from ddtrace.internal.logger import get_logger
from ddtrace.internal.schema import schematize_url_operation
from ddtrace.internal.schema.span_attribute_schema import SpanDirection
from ddtrace.internal.telemetry import get_config as _get_config
from ddtrace.internal.utils import get_argument_value
from ddtrace.internal.utils.formats import asbool
from ddtrace.propagation.http import HTTPPropagator
from ddtrace.settings._core import get_config as _get_config
from ddtrace.trace import Pin


Expand Down
2 changes: 1 addition & 1 deletion ddtrace/contrib/internal/wsgi/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from typing import Mapping # noqa:F401
from typing import Optional # noqa:F401

from ddtrace.settings import Config # noqa:F401
from ddtrace.settings._config import Config # noqa:F401
from ddtrace.trace import Pin # noqa:F401
from ddtrace.trace import Span # noqa:F401
from ddtrace.trace import Tracer # noqa:F401
Expand Down
6 changes: 3 additions & 3 deletions ddtrace/debugging/_products/exception_replay.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from ddtrace.debugging._config import er_config
from ddtrace.debugging._config import er_config as config


# TODO[gab]: Uncomment this when the feature is ready
Expand All @@ -10,7 +10,7 @@ def post_preload():


def start():
if er_config.enabled:
if config.enabled:
from ddtrace.debugging._exception.replay import SpanExceptionHandler

SpanExceptionHandler.enable()
Expand All @@ -21,7 +21,7 @@ def restart(join=False):


def stop(join=False):
if er_config.enabled:
if config.enabled:
from ddtrace.debugging._exception.replay import SpanExceptionHandler

SpanExceptionHandler.disable()
Expand Down
1 change: 1 addition & 0 deletions ddtrace/internal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ gets extended to add support for additional features.
| Attribute | Description |
|-----------|-------------|
| `requires: list[str]` | A list of other product names that the product depends on |
| `config: DDConfig` | A configuration object; when an instance of `DDConfig`, configuration telemetry is automatically reported |
148 changes: 1 addition & 147 deletions ddtrace/internal/agent.py
Original file line number Diff line number Diff line change
@@ -1,167 +1,21 @@
import json
import os
import socket
from typing import Optional
from typing import TypeVar
from typing import Union

from ddtrace.internal.constants import DEFAULT_TIMEOUT
from ddtrace.internal.logger import get_logger
from ddtrace.settings._core import DDConfig
from ddtrace.settings._agent import config

from .http import HTTPConnection
from .http import HTTPSConnection
from .uds import UDSHTTPConnection
from .utils.http import get_connection


DEFAULT_HOSTNAME = "localhost"
DEFAULT_TRACE_PORT = 8126
DEFAULT_UNIX_TRACE_PATH = "/var/run/datadog/apm.socket"
DEFAULT_UNIX_DSD_PATH = "/var/run/datadog/dsd.socket"
DEFAULT_STATS_PORT = 8125

ConnectionType = Union[HTTPSConnection, HTTPConnection, UDSHTTPConnection]

T = TypeVar("T")

log = get_logger(__name__)


def _derive_trace_url(config: "AgentConfig") -> str:
user_supplied_host = config._trace_agent_hostname or config._agent_host
user_supplied_port = config._trace_agent_port or config._agent_port

url = config._trace_agent_url
if not url:
if user_supplied_host is not None or user_supplied_port is not None:
host = user_supplied_host or DEFAULT_HOSTNAME
port = user_supplied_port or DEFAULT_TRACE_PORT
if is_ipv6_hostname(host):
host = "[{}]".format(host)
url = "http://%s:%s" % (host, port)
elif os.path.exists("/var/run/datadog/apm.socket"):
url = "unix://%s" % (DEFAULT_UNIX_TRACE_PATH)
else:
url = "http://{}:{}".format(DEFAULT_HOSTNAME, DEFAULT_TRACE_PORT)

return url


def _derive_stats_url(config: "AgentConfig") -> str:
user_supplied_host = config._dogstatsd_host or config._agent_host
user_supplied_port = config._dogstatsd_port or config._agent_port
url = config._dogstatsd_url

if not url:
if user_supplied_host is not None or user_supplied_port is not None:
port = user_supplied_port or DEFAULT_STATS_PORT
host = user_supplied_host or DEFAULT_HOSTNAME
if is_ipv6_hostname(host):
host = "[{}]".format(host)
url = "udp://{}:{}".format(host, port)
elif os.path.exists("/var/run/datadog/dsd.socket"):
url = "unix://%s" % (DEFAULT_UNIX_DSD_PATH)
else:
url = "udp://{}:{}".format(DEFAULT_HOSTNAME, DEFAULT_STATS_PORT)
return url


# This method returns if a hostname is an IPv6 address
def is_ipv6_hostname(hostname):
# type: (Union[T, str]) -> bool
if not isinstance(hostname, str):
return False
try:
socket.inet_pton(socket.AF_INET6, hostname)
return True
except socket.error: # not a valid address
return False


class AgentConfig(DDConfig):
__prefix__ = "dd"

_trace_agent_hostname = DDConfig.v(
Optional[str],
"trace_agent_hostname",
default=None,
help_type="String",
help="Legacy configuration, stores the hostname of the trace agent",
)

_trace_agent_port = DDConfig.v(
Optional[int],
"trace_agent_port",
default=None,
help_type="Int",
help="Legacy configuration, stores the port of the trace agent",
)

_trace_agent_url = DDConfig.v(
Optional[str],
"trace_agent_url",
default=None,
help_type="String",
help="Stores the URL of the trace agent",
)

trace_agent_timeout_seconds = DDConfig.v(
float,
"trace_agent_timeout_seconds",
default=DEFAULT_TIMEOUT,
help_type="Float",
help="Stores the timeout in seconds for the trace agent",
)

_dogstatsd_host = DDConfig.v(
Optional[str],
"dogstatsd_host",
default=None,
help_type="String",
help="Stores the hostname of the agent receiving DogStatsD metrics",
)

_dogstatsd_port = DDConfig.v(
Optional[int],
"dogstatsd_port",
default=None,
help_type="Int",
help="Stores the port of the agent receiving DogStatsD metrics",
)

_dogstatsd_url = DDConfig.v(
Optional[str],
"dogstatsd_url",
default=None,
help_type="String",
help="Stores the URL of the DogStatsD agent",
)

_agent_host = DDConfig.v(
Optional[str],
"agent_host",
default=None,
help_type="String",
help="Stores the hostname of the agent",
)

_agent_port = DDConfig.v(
Optional[int],
"agent_port",
default=None,
help_type="Int",
help="Stores the port of the agent",
)
# Effective trace agent URL (this is the one that will be used)
trace_agent_url = DDConfig.d(str, _derive_trace_url)
# Effective DogStatsD URL (this is the one that will be used)
dogstatsd_url = DDConfig.d(str, _derive_stats_url)


config = AgentConfig()


def get_trace_url() -> str:
"""Return the Agent URL computed from the environment."""
return config.trace_agent_url
Expand Down
6 changes: 3 additions & 3 deletions ddtrace/internal/appsec/product.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from ddtrace.settings.asm import config as asm_config
from ddtrace.settings.asm import config


requires = ["remote-configuration"]
Expand All @@ -9,14 +9,14 @@ def post_preload():


def start():
if asm_config._asm_rc_enabled:
if config._asm_rc_enabled:
from ddtrace.appsec._remoteconfiguration import enable_appsec_rc

enable_appsec_rc()


def restart(join=False):
if asm_config._asm_rc_enabled:
if config._asm_rc_enabled:
from ddtrace.appsec._remoteconfiguration import _forksafe_appsec_rc

_forksafe_appsec_rc()
Expand Down
6 changes: 6 additions & 0 deletions ddtrace/internal/products.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@

from ddtrace.internal import forksafe
from ddtrace.internal.logger import get_logger
from ddtrace.internal.telemetry import report_configuration
from ddtrace.internal.telemetry import telemetry_writer
from ddtrace.internal.uwsgi import check_uwsgi
from ddtrace.internal.uwsgi import uWSGIConfigError
from ddtrace.internal.uwsgi import uWSGIMasterProcess
from ddtrace.settings._core import DDConfig


log = get_logger(__name__)
Expand Down Expand Up @@ -61,6 +63,10 @@ def __init__(self) -> None:
log.exception("Failed to load product plugin '%s'", name)
continue

# Report configuration via telemetry
if isinstance(config := getattr(product, "config", None), DDConfig):
report_configuration(config)

log.debug("Product plugin '%s' loaded successfully", name)

self.__products__[name] = product
Expand Down
Loading
Loading