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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 5 additions & 10 deletions ddtrace/internal/opentelemetry/logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
from ddtrace.internal.logger import get_logger
from ddtrace.internal.telemetry import telemetry_writer
from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE
from ddtrace.settings._agent import get_agent_hostname
from ddtrace.settings._opentelemetry import exporter_config
from ddtrace.settings._opentelemetry import otel_config


log = get_logger(__name__)
Expand All @@ -36,7 +35,7 @@ def set_otel_logs_provider() -> None:
if resource is None:
return

protocol = (exporter_config.OTLP_PROTOCOL or exporter_config.OTLP_LOGS_PROTOCOL or DEFAULT_PROTOCOL).lower()
protocol = otel_config.exporter.LOGS_PROTOCOL
exporter_class = _import_exporter(protocol)
if exporter_class is None:
return
Expand Down Expand Up @@ -180,13 +179,9 @@ def _initialize_logging(exporter_class, protocol, resource):
try:
from opentelemetry.sdk._configuration import _init_logging

if not exporter_config.OTLP_ENDPOINT and not exporter_config.OTLP_LOGS_ENDPOINT:
if protocol in ("http/json", "http/protobuf"):
endpoint = f"http://{get_agent_hostname()}:{HTTP_PORT}{HTTP_LOGS_ENDPOINT}"
else:
endpoint = f"http://{get_agent_hostname()}:{GRPC_PORT}"
os.environ["OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"] = endpoint

# Ensure logging exporter is configured to send payloads to a Datadog Agent.
# The default endpoint is resolved using the hostname from DD_AGENT.. and DD_TRACE_AGENT_... configs
os.environ["OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"] = otel_config.exporter.LOGS_ENDPOINT
_init_logging({protocol: exporter_class}, resource=resource)
return True
except ImportError as e:
Expand Down
70 changes: 59 additions & 11 deletions ddtrace/settings/_opentelemetry.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,69 @@
import typing as t

from ddtrace.internal.telemetry import get_config
from ddtrace.internal.telemetry import report_configuration
from ddtrace.settings._agent import get_agent_hostname
from ddtrace.settings._core import DDConfig


class OpenTelemetryExporterConfig(DDConfig):
__prefix__ = "otel.exporter"
def _derive_endpoint(config: "ExporterConfig"):
if config.PROTOCOL.lower() in ("http/json", "http/protobuf"):
default_endpoint = (
f"http://{get_agent_hostname()}:{ExporterConfig.HTTP_PORT}{ExporterConfig.HTTP_LOGS_ENDPOINT}"
)
else:
default_endpoint = f"http://{get_agent_hostname()}:{ExporterConfig.GRPC_PORT}"
return get_config("OTEL_EXPORTER_OTLP_ENDPOINT", default_endpoint)

# OTLP exporter configuration
OTLP_PROTOCOL = DDConfig.v(t.Optional[str], "otlp.protocol", default=None)
OTLP_LOGS_PROTOCOL = DDConfig.v(t.Optional[str], "otlp.logs.protocol", default=None)
OTLP_ENDPOINT = DDConfig.v(t.Optional[str], "otlp.endpoint", default=None)
OTLP_LOGS_ENDPOINT = DDConfig.v(t.Optional[str], "otlp.logs.endpoint", default=None)
OTLP_HEADERS = DDConfig.v(t.Optional[str], "otlp.headers", default=None)
OTLP_LOGS_HEADERS = DDConfig.v(t.Optional[str], "otlp.logs.headers", default=None)

def _derive_logs_endpoint(config: "ExporterConfig"):
if config.LOGS_PROTOCOL.lower() in ("http/json", "http/protobuf"):
default_endpoint = (
f"http://{get_agent_hostname()}:{ExporterConfig.HTTP_PORT}{ExporterConfig.HTTP_LOGS_ENDPOINT}"
)
else:
default_endpoint = f"http://{get_agent_hostname()}:{ExporterConfig.GRPC_PORT}"
return get_config(["OTEL_EXPORTER_OTLP_LOGS_ENDPOINT", "OTEL_EXPORTER_OTLP_ENDPOINT"], default_endpoint)

exporter_config = OpenTelemetryExporterConfig()

report_configuration(exporter_config)
def _derive_logs_protocol(config: "ExporterConfig"):
return get_config("OTEL_EXPORTER_OTLP_LOGS_PROTOCOL", config.PROTOCOL)


def _derive_logs_headers(config: "ExporterConfig"):
return get_config("OTEL_EXPORTER_OTLP_LOGS_HEADERS", config.HEADERS)


def _derive_logs_timeout(config: "ExporterConfig"):
return get_config("OTEL_EXPORTER_OTLP_LOGS_TIMEOUT", config.DEFAULT_TIMEOUT, int)


class OpenTelemetryConfig(DDConfig):
__prefix__ = "otel"


class ExporterConfig(DDConfig):
__prefix__ = "exporter"

GRPC_PORT: int = 4317
HTTP_PORT: int = 4318
HTTP_LOGS_ENDPOINT: str = "/v1/logs"
DEFAULT_HEADERS: str = ""
DEFAULT_TIMEOUT: int = 10000

PROTOCOL = DDConfig.v(t.Optional[str], "otlp.protocol", default="grpc")
ENDPOINT = DDConfig.d(str, _derive_endpoint)
HEADERS = DDConfig.v(str, "otlp.headers", default=DEFAULT_HEADERS)
TIMEOUT = DDConfig.v(int, "otlp.timeout", default=DEFAULT_TIMEOUT)

LOGS_PROTOCOL = DDConfig.d(str, _derive_logs_protocol)
LOGS_ENDPOINT = DDConfig.d(str, _derive_logs_endpoint)
LOGS_HEADERS = DDConfig.d(str, _derive_logs_headers)
LOGS_TIMEOUT = DDConfig.d(int, _derive_logs_timeout)


OpenTelemetryConfig.include(ExporterConfig, namespace="exporter")

otel_config = OpenTelemetryConfig()

report_configuration(otel_config)
44 changes: 43 additions & 1 deletion tests/telemetry/test_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ def test_app_started_event_configuration_override(test_agent_session, run_python
env["DD_TRACE_WRITER_REUSE_CONNECTIONS"] = "True"
env["DD_TAGS"] = "team:apm,component:web"
env["DD_INSTRUMENTATION_CONFIG_ID"] = "abcedf123"
env["DD_LOGS_OTEL_ENABLED"] = "True"
env["OTEL_EXPORTER_OTLP_ENDPOINT"] = "http://localhost:4317"

file = tmpdir.join("moon_ears.json")
file.write('[{"service":"xy?","name":"a*c"}]')
Expand Down Expand Up @@ -424,7 +426,7 @@ def test_app_started_event_configuration_override(test_agent_session, run_python
{"name": "DD_LLMOBS_ML_APP", "origin": "default", "value": None},
{"name": "DD_LLMOBS_SAMPLE_RATE", "origin": "default", "value": 1.0},
{"name": "DD_LOGS_INJECTION", "origin": "env_var", "value": True},
{"name": "DD_LOGS_OTEL_ENABLED", "origin": "default", "value": False},
{"name": "DD_LOGS_OTEL_ENABLED", "origin": "env_var", "value": True},
{"name": "DD_METRICS_OTEL_ENABLED", "origin": "default", "value": False},
{"name": "DD_PROFILING_AGENTLESS", "origin": "default", "value": False},
{"name": "DD_PROFILING_API_TIMEOUT", "origin": "default", "value": 10.0},
Expand Down Expand Up @@ -532,6 +534,46 @@ def test_app_started_event_configuration_override(test_agent_session, run_python
{"name": "DD_USER_MODEL_LOGIN_FIELD", "origin": "default", "value": ""},
{"name": "DD_USER_MODEL_NAME_FIELD", "origin": "default", "value": ""},
{"name": "DD_VERSION", "origin": "default", "value": None},
{
"name": "OTEL_EXPORTER_OTLP_ENDPOINT",
"origin": "env_var",
"value": "http://localhost:4317",
},
{
"name": "OTEL_EXPORTER_OTLP_HEADERS",
"origin": "default",
"value": "",
},
{
"name": "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT",
"origin": "env_var",
"value": "http://localhost:4317",
},
{
"name": "OTEL_EXPORTER_OTLP_LOGS_HEADERS",
"origin": "default",
"value": "",
},
{
"name": "OTEL_EXPORTER_OTLP_LOGS_PROTOCOL",
"origin": "default",
"value": "grpc",
},
{
"name": "OTEL_EXPORTER_OTLP_LOGS_TIMEOUT",
"origin": "default",
"value": 10000,
},
{
"name": "OTEL_EXPORTER_OTLP_PROTOCOL",
"origin": "default",
"value": "grpc",
},
{
"name": "OTEL_EXPORTER_OTLP_TIMEOUT",
"origin": "default",
"value": 10000,
},
{"name": "_DD_APPSEC_DEDUPLICATION_ENABLED", "origin": "default", "value": True},
{"name": "_DD_IAST_LAZY_TAINT", "origin": "default", "value": False},
{"name": "_DD_IAST_USE_ROOT_SPAN", "origin": "default", "value": False},
Expand Down
Loading