From 283e118790e1367fb1ae3ee89ac25b03230b81e8 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Thu, 15 May 2025 12:20:02 -0700 Subject: [PATCH 01/52] Consolidate env reading to single config object. --- datadog_lambda/api.py | 15 ++++--- datadog_lambda/cold_start.py | 12 ++---- datadog_lambda/config.py | 73 +++++++++++++++++++++++++++++++++ datadog_lambda/fips.py | 19 --------- datadog_lambda/metric.py | 17 +++----- datadog_lambda/patch.py | 10 ++--- datadog_lambda/span_pointers.py | 9 +--- datadog_lambda/tracing.py | 32 +++++---------- datadog_lambda/wrapper.py | 6 +-- 9 files changed, 107 insertions(+), 86 deletions(-) create mode 100644 datadog_lambda/config.py delete mode 100644 datadog_lambda/fips.py diff --git a/datadog_lambda/api.py b/datadog_lambda/api.py index d1cee4e4..4921dae9 100644 --- a/datadog_lambda/api.py +++ b/datadog_lambda/api.py @@ -1,7 +1,7 @@ import logging import os -from datadog_lambda.fips import fips_mode_enabled +from datadog_lambda.config import config logger = logging.getLogger(__name__) KMS_ENCRYPTION_CONTEXT_KEY = "LambdaFunctionName" @@ -29,7 +29,6 @@ def decrypt_kms_api_key(kms_client, ciphertext): is added. We need to try decrypting the API key both with and without the encryption context. """ # Try without encryption context, in case API key was encrypted using the AWS CLI - function_name = os.environ.get("AWS_LAMBDA_FUNCTION_NAME") try: plaintext = kms_client.decrypt(CiphertextBlob=decoded_bytes)[ "Plaintext" @@ -43,7 +42,7 @@ def decrypt_kms_api_key(kms_client, ciphertext): plaintext = kms_client.decrypt( CiphertextBlob=decoded_bytes, EncryptionContext={ - KMS_ENCRYPTION_CONTEXT_KEY: function_name, + KMS_ENCRYPTION_CONTEXT_KEY: config.function_name, }, )["Plaintext"].decode("utf-8") @@ -66,7 +65,7 @@ def get_api_key() -> str: DD_API_KEY = os.environ.get("DD_API_KEY", os.environ.get("DATADOG_API_KEY", "")) LAMBDA_REGION = os.environ.get("AWS_REGION", "") - if fips_mode_enabled: + if config.fips_mode_enabled: logger.debug( "FIPS mode is enabled, using FIPS endpoints for secrets management." ) @@ -82,7 +81,7 @@ def get_api_key() -> str: return "" endpoint_url = ( f"https://secretsmanager-fips.{secrets_region}.amazonaws.com" - if fips_mode_enabled + if config.fips_mode_enabled else None ) secrets_manager_client = _boto3_client( @@ -95,7 +94,7 @@ def get_api_key() -> str: # SSM endpoints: https://docs.aws.amazon.com/general/latest/gr/ssm.html fips_endpoint = ( f"https://ssm-fips.{LAMBDA_REGION}.amazonaws.com" - if fips_mode_enabled + if config.fips_mode_enabled else None ) ssm_client = _boto3_client("ssm", endpoint_url=fips_endpoint) @@ -106,7 +105,7 @@ def get_api_key() -> str: # KMS endpoints: https://docs.aws.amazon.com/general/latest/gr/kms.html fips_endpoint = ( f"https://kms-fips.{LAMBDA_REGION}.amazonaws.com" - if fips_mode_enabled + if config.fips_mode_enabled else None ) kms_client = _boto3_client("kms", endpoint_url=fips_endpoint) @@ -118,7 +117,7 @@ def get_api_key() -> str: def init_api(): - if not os.environ.get("DD_FLUSH_TO_LOG", "").lower() == "true": + if not config.flush_to_log: # Make sure that this package would always be lazy-loaded/outside from the critical path # since underlying packages are quite heavy to load # and useless with the extension unless sending metrics with timestamps diff --git a/datadog_lambda/cold_start.py b/datadog_lambda/cold_start.py index ea10ea20..96cb2074 100644 --- a/datadog_lambda/cold_start.py +++ b/datadog_lambda/cold_start.py @@ -1,8 +1,9 @@ import time -import os from typing import List, Hashable import logging +from datadog_lambda.config import config + logger = logging.getLogger(__name__) _cold_start = True @@ -86,14 +87,12 @@ def reset_node_stacks(): def push_node(module_name, file_path): node = ImportNode(module_name, file_path, time.time_ns()) - global import_stack if import_stack: import_stack[-1].children.append(node) import_stack.append(node) def pop_node(module_name): - global import_stack if not import_stack: return node = import_stack.pop() @@ -102,7 +101,6 @@ def pop_node(module_name): end_time_ns = time.time_ns() node.end_time_ns = end_time_ns if not import_stack: # import_stack empty, a root node has been found - global root_nodes root_nodes.append(node) @@ -147,11 +145,7 @@ def wrapped_find_spec(*args, **kwargs): def initialize_cold_start_tracing(): - if ( - is_new_sandbox() - and os.environ.get("DD_TRACE_ENABLED", "true").lower() == "true" - and os.environ.get("DD_COLD_START_TRACING", "true").lower() == "true" - ): + if is_new_sandbox() and config.trace_enabled and config.cold_start_tracing: from sys import meta_path for importer in meta_path: diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py new file mode 100644 index 00000000..1048aa0b --- /dev/null +++ b/datadog_lambda/config.py @@ -0,0 +1,73 @@ +# Unless explicitly stated otherwise all files in this repository are licensed +# under the Apache License Version 2.0. +# This product includes software developed at Datadog (https://www.datadoghq.com/). +# Copyright 2019 Datadog, Inc. + +import logging +import os + + +def _get_env(key, default=None, cast=None): + """Get an environment variable with a default value.""" + val = os.environ.get(key, default) + if cast is not None: + try: + val = cast(val) + except ValueError: + raise ValueError(f"Invalid value for {key}: {val}") + return cast(default) + return val + + +def as_bool(val): + """Convert a string to a boolean.""" + if isinstance(val, bool): + return val + if isinstance(val, str): + val = val.lower() + if val in ("true", "1", "yes"): + return True + elif val in ("false", "0", "no"): + return False + raise ValueError(f"Invalid boolean value: {val}") + + +class config: + + function_name = os.environ.get("AWS_LAMBDA_FUNCTION_NAME") + flush_to_log = os.environ.get("DD_FLUSH_TO_LOG", "").lower() == "true" + trace_enabled = os.environ.get("DD_TRACE_ENABLED", "true").lower() == "true" + cold_start_tracing = ( + os.environ.get("DD_COLD_START_TRACING", "true").lower() == "true" + ) + is_gov_region = os.environ.get("AWS_REGION", "").startswith("us-gov-") + fips_mode_enabled = ( + os.environ.get( + "DD_LAMBDA_FIPS_MODE", + "true" if is_gov_region else "false", + ).lower() + == "true" + ) + log_level = (os.environ.get("DD_LOG_LEVEL") or "INFO").upper() + flush_in_thread = os.environ.get("DD_FLUSH_IN_THREAD", "").lower() == "true" + enhanced_metrics_enabled = ( + os.environ.get("DD_ENHANCED_METRICS", "true").lower() == "true" + ) + is_in_tests = os.environ.get("DD_INTEGRATION_TEST", "false").lower() == "true" + add_span_pointers = os.environ.get( + "DD_BOTOCORE_ADD_SPAN_POINTERS", "true" + ).lower() in ("true", "1") + otel_enabled = os.environ.get("DD_TRACE_OTEL_ENABLED", "false").lower() == "true" + is_lambda_context = bool(function_name) + telemetry_enabled = ( + os.environ.get("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "false").lower() + == "true" + ) + + +if config.is_gov_region or config.fips_mode_enabled: + logger = logging.getLogger(__name__) + logger.debug( + "Python Lambda Layer FIPS mode is %s.", + "enabled" if config.fips_mode_enabled else "not enabled", + ) diff --git a/datadog_lambda/fips.py b/datadog_lambda/fips.py deleted file mode 100644 index 8442ddd9..00000000 --- a/datadog_lambda/fips.py +++ /dev/null @@ -1,19 +0,0 @@ -import logging -import os - -is_gov_region = os.environ.get("AWS_REGION", "").startswith("us-gov-") - -fips_mode_enabled = ( - os.environ.get( - "DD_LAMBDA_FIPS_MODE", - "true" if is_gov_region else "false", - ).lower() - == "true" -) - -if is_gov_region or fips_mode_enabled: - logger = logging.getLogger(__name__) - logger.debug( - "Python Lambda Layer FIPS mode is %s.", - "enabled" if fips_mode_enabled else "not enabled", - ) diff --git a/datadog_lambda/metric.py b/datadog_lambda/metric.py index c9b978d6..73bbeca3 100644 --- a/datadog_lambda/metric.py +++ b/datadog_lambda/metric.py @@ -5,14 +5,13 @@ import enum import logging -import os import time from datetime import datetime, timedelta import ujson as json +from datadog_lambda.config import config from datadog_lambda.extension import should_use_extension -from datadog_lambda.fips import fips_mode_enabled from datadog_lambda.tags import dd_lambda_layer_tag, get_enhanced_metrics_tags logger = logging.getLogger(__name__) @@ -28,10 +27,10 @@ class MetricsHandler(enum.Enum): def _select_metrics_handler(): if should_use_extension: return MetricsHandler.EXTENSION - if os.environ.get("DD_FLUSH_TO_LOG", "").lower() == "true": + if config.flush_to_log: return MetricsHandler.FORWARDER - if fips_mode_enabled: + if config.fips_mode_enabled: logger.debug( "With FIPS mode enabled, the Datadog API metrics handler is unavailable." ) @@ -58,14 +57,8 @@ def _select_metrics_handler(): from datadog_lambda.api import init_api from datadog_lambda.thread_stats_writer import ThreadStatsWriter - flush_in_thread = os.environ.get("DD_FLUSH_IN_THREAD", "").lower() == "true" init_api() - lambda_stats = ThreadStatsWriter(flush_in_thread) - - -enhanced_metrics_enabled = ( - os.environ.get("DD_ENHANCED_METRICS", "true").lower() == "true" -) + lambda_stats = ThreadStatsWriter(config.flush_in_thread) def lambda_metric(metric_name, value, timestamp=None, tags=None, force_async=False): @@ -191,7 +184,7 @@ def submit_enhanced_metric(metric_name, lambda_context): metric_name (str): metric name w/o enhanced prefix i.e. "invocations" or "errors" lambda_context (object): Lambda context dict passed to the function by AWS """ - if not enhanced_metrics_enabled: + if not config.enhanced_metrics_enabled: logger.debug( "Not submitting enhanced metric %s because enhanced metrics are disabled", metric_name, diff --git a/datadog_lambda/patch.py b/datadog_lambda/patch.py index 5b8a92c5..da07c6b0 100644 --- a/datadog_lambda/patch.py +++ b/datadog_lambda/patch.py @@ -3,7 +3,6 @@ # This product includes software developed at Datadog (https://www.datadoghq.com/). # Copyright 2019 Datadog, Inc. -import os import sys import logging import zlib @@ -13,10 +12,8 @@ from wrapt.importer import when_imported from ddtrace import patch_all as patch_all_dd -from datadog_lambda.tracing import ( - get_dd_trace_context, - dd_tracing_enabled, -) +from datadog_lambda import config +from datadog_lambda.tracing import get_dd_trace_context from collections.abc import MutableMapping logger = logging.getLogger(__name__) @@ -44,8 +41,7 @@ def _patch_for_integration_tests(): Patch `requests` to log the outgoing requests for integration tests. """ global _integration_tests_patched - is_in_tests = os.environ.get("DD_INTEGRATION_TEST", "false").lower() == "true" - if not _integration_tests_patched and is_in_tests: + if not _integration_tests_patched and config.is_in_tests: wrap("requests", "Session.send", _log_request) _integration_tests_patched = True diff --git a/datadog_lambda/span_pointers.py b/datadog_lambda/span_pointers.py index 40d959e6..45925d92 100644 --- a/datadog_lambda/span_pointers.py +++ b/datadog_lambda/span_pointers.py @@ -1,12 +1,12 @@ from itertools import chain import logging -import os from typing import List from typing import Optional from ddtrace._trace._span_pointer import _SpanPointerDirection from ddtrace._trace._span_pointer import _SpanPointerDescription +from datadog_lambda.config import config from datadog_lambda.metric import submit_dynamodb_stream_type_metric from datadog_lambda.trigger import EventTypes @@ -14,15 +14,10 @@ logger = logging.getLogger(__name__) -dd_botocore_add_span_pointers = os.environ.get( - "DD_BOTOCORE_ADD_SPAN_POINTERS", "true" -).lower() in ("true", "1") - - def calculate_span_pointers( event_source, event, - botocore_add_span_pointers=dd_botocore_add_span_pointers, + botocore_add_span_pointers=config.add_span_pointers, ) -> List[_SpanPointerDescription]: try: if botocore_add_span_pointers: diff --git a/datadog_lambda/tracing.py b/datadog_lambda/tracing.py index 4b6f300a..c449bf54 100644 --- a/datadog_lambda/tracing.py +++ b/datadog_lambda/tracing.py @@ -32,6 +32,8 @@ from ddtrace import __version__ as ddtrace_version from ddtrace.propagation.http import HTTPPropagator from ddtrace.trace import Context, Span, tracer + +from datadog_lambda.config import config from datadog_lambda import __version__ as datadog_lambda_version from datadog_lambda.trigger import ( _EventSource, @@ -42,10 +44,7 @@ EventSubtypes, ) -dd_trace_otel_enabled = ( - os.environ.get("DD_TRACE_OTEL_ENABLED", "false").lower() == "true" -) -if dd_trace_otel_enabled: +if config.otel_enabled: from opentelemetry.trace import set_tracer_provider from ddtrace.opentelemetry import TracerProvider @@ -55,18 +54,11 @@ logger = logging.getLogger(__name__) dd_trace_context = None -dd_tracing_enabled = os.environ.get("DD_TRACE_ENABLED", "false").lower() == "true" -if dd_tracing_enabled: +if config.trace_enabled and config.telemetry_enabled: # Enable the telemetry client if the user has opted in - if ( - os.environ.get("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "false").lower() - == "true" - ): - from ddtrace.internal.telemetry import telemetry_writer + from ddtrace.internal.telemetry import telemetry_writer - telemetry_writer.enable() - -is_lambda_context = os.environ.get(XrayDaemon.FUNCTION_NAME_HEADER_NAME) != "" + telemetry_writer.enable() propagator = HTTPPropagator() @@ -97,7 +89,7 @@ def _convert_xray_sampling(xray_sampled): def _get_xray_trace_context(): - if not is_lambda_context: + if not config.is_lambda_context: return None xray_trace_entity = parse_xray_header( @@ -639,13 +631,11 @@ def get_dd_trace_context_obj(): automatically, but this function can be used to manually inject the trace context to an outgoing request. """ - if dd_tracing_enabled: + if config.trace_enabled: dd_trace_py_context = _get_dd_trace_py_context() if _is_context_complete(dd_trace_py_context): return dd_trace_py_context - global dd_trace_context - try: xray_context = _get_xray_trace_context() # xray (sub)segment except Exception as e: @@ -690,10 +680,10 @@ def set_correlation_ids(): TODO: Remove me when Datadog tracer is natively supported in Lambda. """ - if not is_lambda_context: + if not config.is_lambda_context: logger.debug("set_correlation_ids is only supported in LambdaContext") return - if dd_tracing_enabled: + if config.trace_enabled: logger.debug("using ddtrace implementation for spans") return @@ -1480,7 +1470,7 @@ def emit_telemetry_on_exception_outside_of_handler( Emit an enhanced error metric and create a span for exceptions occurring outside the handler """ submit_errors_metric(None) - if dd_tracing_enabled: + if config.trace_enabled: span = tracer.trace( "aws.lambda", service="aws.lambda", diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index 0e23b721..ec6c5d86 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -18,6 +18,7 @@ is_new_sandbox, ColdStartTracer, ) +from datadog_lambda.config import config from datadog_lambda.constants import ( TraceContextSource, XraySubsegment, @@ -30,7 +31,6 @@ extract_dd_trace_context, create_dd_dummy_metadata_subsegment, inject_correlation_ids, - dd_tracing_enabled, mark_trace_as_error_for_5xx_responses, set_correlation_ids, set_dd_trace_py_root, @@ -175,7 +175,7 @@ def __init__(self, func): self.span = None self.inferred_span = None depends_on_dd_tracing_enabled = ( - lambda original_boolean: dd_tracing_enabled and original_boolean + lambda original_boolean: config.trace_enabled and original_boolean ) self.make_inferred_span = depends_on_dd_tracing_enabled( os.environ.get(DD_TRACE_MANAGED_SERVICES, "true").lower() == "true" @@ -321,7 +321,7 @@ def _before(self, event, context): XraySubsegment.TRACE_KEY, ) - if dd_tracing_enabled: + if config.trace_enabled: set_dd_trace_py_root(trace_context_source, self.merge_xray_traces) if self.make_inferred_span: self.inferred_span = create_inferred_span( From 05398fcb0847ebe795187c3d0d946f3a8c890ab5 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Thu, 15 May 2025 14:00:14 -0700 Subject: [PATCH 02/52] Tests for reading config values. --- datadog_lambda/config.py | 84 +++++++++++--------- tests/test_config.py | 168 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 214 insertions(+), 38 deletions(-) create mode 100644 tests/test_config.py diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 1048aa0b..790309ee 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -9,15 +9,21 @@ def _get_env(key, default=None, cast=None): """Get an environment variable with a default value.""" - val = os.environ.get(key, default) - if cast is not None: - try: - val = cast(val) - except ValueError: - raise ValueError(f"Invalid value for {key}: {val}") - return cast(default) - return val + prop_key = f"_{key}" + @property + def _getter(self): + if not hasattr(self, prop_key): + val = os.environ.get(key, default) + if cast is not None: + try: + val = cast(val) + except ValueError: + raise ValueError(f"Invalid value for {key}: {val}") + return cast(default) + return getattr(self, prop_key) + + return _getter def as_bool(val): """Convert a string to a boolean.""" @@ -32,38 +38,40 @@ def as_bool(val): raise ValueError(f"Invalid boolean value: {val}") -class config: +class Config: + + def __init__(self): + self.function_name = os.environ.get("AWS_LAMBDA_FUNCTION_NAME") + self.flush_to_log = os.environ.get("DD_FLUSH_TO_LOG", "").lower() == "true" + self.trace_enabled = os.environ.get("DD_TRACE_ENABLED", "true").lower() == "true" + self.cold_start_tracing = ( + os.environ.get("DD_COLD_START_TRACING", "true").lower() == "true" + ) + self.is_gov_region = os.environ.get("AWS_REGION", "").startswith("us-gov-") + self.fips_mode_enabled = ( + os.environ.get( + "DD_LAMBDA_FIPS_MODE", + "true" if self.is_gov_region else "false", + ).lower() + == "true" + ) + self.flush_in_thread = os.environ.get("DD_FLUSH_IN_THREAD", "").lower() == "true" + self.enhanced_metrics_enabled = ( + os.environ.get("DD_ENHANCED_METRICS", "true").lower() == "true" + ) + self.is_in_tests = os.environ.get("DD_INTEGRATION_TEST", "false").lower() == "true" + self.add_span_pointers = os.environ.get( + "DD_BOTOCORE_ADD_SPAN_POINTERS", "true" + ).lower() in ("true", "1") + self.otel_enabled = os.environ.get("DD_TRACE_OTEL_ENABLED", "false").lower() == "true" + self.is_lambda_context = bool(self.function_name) + self.telemetry_enabled = ( + os.environ.get("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "false").lower() + == "true" + ) - function_name = os.environ.get("AWS_LAMBDA_FUNCTION_NAME") - flush_to_log = os.environ.get("DD_FLUSH_TO_LOG", "").lower() == "true" - trace_enabled = os.environ.get("DD_TRACE_ENABLED", "true").lower() == "true" - cold_start_tracing = ( - os.environ.get("DD_COLD_START_TRACING", "true").lower() == "true" - ) - is_gov_region = os.environ.get("AWS_REGION", "").startswith("us-gov-") - fips_mode_enabled = ( - os.environ.get( - "DD_LAMBDA_FIPS_MODE", - "true" if is_gov_region else "false", - ).lower() - == "true" - ) - log_level = (os.environ.get("DD_LOG_LEVEL") or "INFO").upper() - flush_in_thread = os.environ.get("DD_FLUSH_IN_THREAD", "").lower() == "true" - enhanced_metrics_enabled = ( - os.environ.get("DD_ENHANCED_METRICS", "true").lower() == "true" - ) - is_in_tests = os.environ.get("DD_INTEGRATION_TEST", "false").lower() == "true" - add_span_pointers = os.environ.get( - "DD_BOTOCORE_ADD_SPAN_POINTERS", "true" - ).lower() in ("true", "1") - otel_enabled = os.environ.get("DD_TRACE_OTEL_ENABLED", "false").lower() == "true" - is_lambda_context = bool(function_name) - telemetry_enabled = ( - os.environ.get("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "false").lower() - == "true" - ) +config = Config() if config.is_gov_region or config.fips_mode_enabled: logger = logging.getLogger(__name__) diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 00000000..fe0f8ac3 --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,168 @@ +import pytest + +from datadog_lambda.config import Config + + +_test_config_from_environ = ( + ("AWS_LAMBDA_FUNCTION_NAME", "function_name", None, None), + ("AWS_LAMBDA_FUNCTION_NAME", "function_name", "", ""), + ("AWS_LAMBDA_FUNCTION_NAME", "function_name", "my_function", "my_function"), + + ("DD_FLUSH_TO_LOG", "flush_to_log", None, False), + ("DD_FLUSH_TO_LOG", "flush_to_log", "", False), + ("DD_FLUSH_TO_LOG", "flush_to_log", "true", True), + ("DD_FLUSH_TO_LOG", "flush_to_log", "TRUE", True), + ("DD_FLUSH_TO_LOG", "flush_to_log", "false", False), + ("DD_FLUSH_TO_LOG", "flush_to_log", "FALSE", False), + ("DD_FLUSH_TO_LOG", "flush_to_log", "1", False), + ("DD_FLUSH_TO_LOG", "flush_to_log", "0", False), + ("DD_FLUSH_TO_LOG", "flush_to_log", "purple", False), + + ("DD_TRACE_ENABLED", "trace_enabled", None, True), + ("DD_TRACE_ENABLED", "trace_enabled", "", False), + ("DD_TRACE_ENABLED", "trace_enabled", "true", True), + ("DD_TRACE_ENABLED", "trace_enabled", "TRUE", True), + ("DD_TRACE_ENABLED", "trace_enabled", "false", False), + ("DD_TRACE_ENABLED", "trace_enabled", "FALSE", False), + ("DD_TRACE_ENABLED", "trace_enabled", "1", False), + ("DD_TRACE_ENABLED", "trace_enabled", "0", False), + ("DD_TRACE_ENABLED", "trace_enabled", "purple", False), + + ("DD_COLD_START_TRACING", "cold_start_tracing", None, True), + ("DD_COLD_START_TRACING", "cold_start_tracing", "", False), + ("DD_COLD_START_TRACING", "cold_start_tracing", "true", True), + ("DD_COLD_START_TRACING", "cold_start_tracing", "TRUE", True), + ("DD_COLD_START_TRACING", "cold_start_tracing", "false", False), + ("DD_COLD_START_TRACING", "cold_start_tracing", "FALSE", False), + ("DD_COLD_START_TRACING", "cold_start_tracing", "1", False), + ("DD_COLD_START_TRACING", "cold_start_tracing", "0", False), + ("DD_COLD_START_TRACING", "cold_start_tracing", "purple", False), + + ("AWS_REGION", "is_gov_region", None, False), + ("AWS_REGION", "is_gov_region", "", False), + ("AWS_REGION", "is_gov_region", "us-gov-1", True), + ("AWS_REGION", "is_gov_region", "us-est-1", False), + + ("DD_FLUSH_IN_THREAD", "flush_in_thread", None, False), + ("DD_FLUSH_IN_THREAD", "flush_in_thread", "", False), + ("DD_FLUSH_IN_THREAD", "flush_in_thread", "true", True), + ("DD_FLUSH_IN_THREAD", "flush_in_thread", "TRUE", True), + ("DD_FLUSH_IN_THREAD", "flush_in_thread", "false", False), + ("DD_FLUSH_IN_THREAD", "flush_in_thread", "FALSE", False), + ("DD_FLUSH_IN_THREAD", "flush_in_thread", "1", False), + ("DD_FLUSH_IN_THREAD", "flush_in_thread", "0", False), + ("DD_FLUSH_IN_THREAD", "flush_in_thread", "purple", False), + + ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", None, True), + ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "", False), + ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "true", True), + ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "TRUE", True), + ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "false", False), + ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "FALSE", False), + ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "1", False), + ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "0", False), + ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "purple", False), + + ("DD_INTEGRATION_TEST", "is_in_tests", None, False), + ("DD_INTEGRATION_TEST", "is_in_tests", "", False), + ("DD_INTEGRATION_TEST", "is_in_tests", "true", True), + ("DD_INTEGRATION_TEST", "is_in_tests", "TRUE", True), + ("DD_INTEGRATION_TEST", "is_in_tests", "false", False), + ("DD_INTEGRATION_TEST", "is_in_tests", "FALSE", False), + ("DD_INTEGRATION_TEST", "is_in_tests", "1", False), + ("DD_INTEGRATION_TEST", "is_in_tests", "0", False), + ("DD_INTEGRATION_TEST", "is_in_tests", "purple", False), + + ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", None, True), + ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "", False), + ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "true", True), + ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "TRUE", True), + ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "false", False), + ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "FALSE", False), + ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "1", True), + ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "0", False), + ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "purple", False), + + ("DD_TRACE_OTEL_ENABLED", "otel_enabled", None, False), + ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "", False), + ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "true", True), + ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "TRUE", True), + ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "false", False), + ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "FALSE", False), + ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "1", False), + ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "0", False), + ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "purple", False), + + ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", None, False), + ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", "", False), + ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", "my_function", True), + + ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", None, False), + ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "", False), + ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "true", True), + ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "TRUE", True), + ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "false", False), + ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "FALSE", False), + ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "1", False), + ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "0", False), + ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "purple", False), +) + +@pytest.mark.parametrize('env_key,conf_key,env_val,conf_val', _test_config_from_environ) +def test_config_from_environ(env_key, conf_key, env_val, conf_val, monkeypatch): + if env_val is not None: + monkeypatch.setenv(env_key, env_val) + config = Config() + assert getattr(config, conf_key) == conf_val + + +_test_fips_mode_from_environ = ( + (None, None, False), + (None, "", False), + (None, "us-gov-1", True), + (None, "us-east-1", False), + + ("", None, False), + ("", "", False), + ("", "us-gov-1", False), + ("", "us-east-1", False), + + ("true", None, True), + ("true", "", True), + ("true", "us-gov-1", True), + ("true", "us-east-1", True), + + ("TRUE", None, True), + ("TRUE", "", True), + ("TRUE", "us-gov-1", True), + ("TRUE", "us-east-1", True), + + ("false", None, False), + ("false", "", False), + ("false", "us-gov-1", False), + ("false", "us-east-1", False), + + ("FALSE", None, False), + ("FALSE", "", False), + ("FALSE", "us-gov-1", False), + ("FALSE", "us-east-1", False), + + + ("1", None, False), + ("1", "", False), + ("1", "us-gov-1", False), + ("1", "us-east-1", False), + + ("0", None, False), + ("0", "", False), + ("0", "us-gov-1", False), + ("0", "us-east-1", False), +) + +@pytest.mark.parametrize('fips_mode,region,conf_val', _test_fips_mode_from_environ) +def test_fips_mode_from_environ(fips_mode, region, conf_val, monkeypatch): + if fips_mode is not None: + monkeypatch.setenv("DD_LAMBDA_FIPS_MODE", fips_mode) + if region is not None: + monkeypatch.setenv("AWS_REGION", region) + assert Config().fips_mode_enabled == conf_val From dfc8581a272a6da5db17035e9651daaad0cc2d45 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Thu, 15 May 2025 14:30:31 -0700 Subject: [PATCH 03/52] Lazy read env values on config. --- datadog_lambda/config.py | 69 ++++++++++++++-------------------------- tests/test_config.py | 16 +++++----- 2 files changed, 32 insertions(+), 53 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 790309ee..601cfbba 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -8,67 +8,46 @@ def _get_env(key, default=None, cast=None): - """Get an environment variable with a default value.""" - prop_key = f"_{key}" - @property def _getter(self): if not hasattr(self, prop_key): val = os.environ.get(key, default) if cast is not None: - try: - val = cast(val) - except ValueError: - raise ValueError(f"Invalid value for {key}: {val}") - return cast(default) + val = cast(val) + setattr(self, prop_key, val) return getattr(self, prop_key) + prop_key = f"_{key}" return _getter + def as_bool(val): - """Convert a string to a boolean.""" - if isinstance(val, bool): - return val - if isinstance(val, str): - val = val.lower() - if val in ("true", "1", "yes"): - return True - elif val in ("false", "0", "no"): - return False - raise ValueError(f"Invalid boolean value: {val}") + return val.lower() == "true" or val == "1" class Config: - def __init__(self): - self.function_name = os.environ.get("AWS_LAMBDA_FUNCTION_NAME") - self.flush_to_log = os.environ.get("DD_FLUSH_TO_LOG", "").lower() == "true" - self.trace_enabled = os.environ.get("DD_TRACE_ENABLED", "true").lower() == "true" - self.cold_start_tracing = ( - os.environ.get("DD_COLD_START_TRACING", "true").lower() == "true" - ) - self.is_gov_region = os.environ.get("AWS_REGION", "").startswith("us-gov-") - self.fips_mode_enabled = ( - os.environ.get( + add_span_pointers = _get_env("DD_BOTOCORE_ADD_SPAN_POINTERS", "true", as_bool) + cold_start_tracing = _get_env("DD_COLD_START_TRACING", "true", as_bool) + enhanced_metrics_enabled = _get_env("DD_ENHANCED_METRICS", "true", as_bool) + flush_in_thread = _get_env("DD_FLUSH_IN_THREAD", "false", as_bool) + flush_to_log = _get_env("DD_FLUSH_TO_LOG", "false", as_bool) + function_name = _get_env("AWS_LAMBDA_FUNCTION_NAME") + is_gov_region = _get_env("AWS_REGION", "", lambda x: x.startswith("us-gov-")) + is_in_tests = _get_env("DD_INTEGRATION_TEST", "false", as_bool) + is_lambda_context = _get_env("AWS_LAMBDA_FUNCTION_NAME", None, bool) + otel_enabled = _get_env("DD_TRACE_OTEL_ENABLED", "false", as_bool) + telemetry_enabled = _get_env("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "false", as_bool) + trace_enabled = _get_env("DD_TRACE_ENABLED", "true", as_bool) + + @property + def fips_mode_enabled(self): + if not hasattr(self, "_fips_mode_enabled"): + self._fips_mode_enabled = os.environ.get( "DD_LAMBDA_FIPS_MODE", "true" if self.is_gov_region else "false", - ).lower() - == "true" - ) - self.flush_in_thread = os.environ.get("DD_FLUSH_IN_THREAD", "").lower() == "true" - self.enhanced_metrics_enabled = ( - os.environ.get("DD_ENHANCED_METRICS", "true").lower() == "true" - ) - self.is_in_tests = os.environ.get("DD_INTEGRATION_TEST", "false").lower() == "true" - self.add_span_pointers = os.environ.get( - "DD_BOTOCORE_ADD_SPAN_POINTERS", "true" - ).lower() in ("true", "1") - self.otel_enabled = os.environ.get("DD_TRACE_OTEL_ENABLED", "false").lower() == "true" - self.is_lambda_context = bool(self.function_name) - self.telemetry_enabled = ( - os.environ.get("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "false").lower() - == "true" - ) + ).lower() == "true" + return self._fips_mode_enabled config = Config() diff --git a/tests/test_config.py b/tests/test_config.py index fe0f8ac3..656ba6c7 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -14,7 +14,7 @@ ("DD_FLUSH_TO_LOG", "flush_to_log", "TRUE", True), ("DD_FLUSH_TO_LOG", "flush_to_log", "false", False), ("DD_FLUSH_TO_LOG", "flush_to_log", "FALSE", False), - ("DD_FLUSH_TO_LOG", "flush_to_log", "1", False), + ("DD_FLUSH_TO_LOG", "flush_to_log", "1", True), # CHANGED ("DD_FLUSH_TO_LOG", "flush_to_log", "0", False), ("DD_FLUSH_TO_LOG", "flush_to_log", "purple", False), @@ -24,7 +24,7 @@ ("DD_TRACE_ENABLED", "trace_enabled", "TRUE", True), ("DD_TRACE_ENABLED", "trace_enabled", "false", False), ("DD_TRACE_ENABLED", "trace_enabled", "FALSE", False), - ("DD_TRACE_ENABLED", "trace_enabled", "1", False), + ("DD_TRACE_ENABLED", "trace_enabled", "1", True), # CHANGED ("DD_TRACE_ENABLED", "trace_enabled", "0", False), ("DD_TRACE_ENABLED", "trace_enabled", "purple", False), @@ -34,7 +34,7 @@ ("DD_COLD_START_TRACING", "cold_start_tracing", "TRUE", True), ("DD_COLD_START_TRACING", "cold_start_tracing", "false", False), ("DD_COLD_START_TRACING", "cold_start_tracing", "FALSE", False), - ("DD_COLD_START_TRACING", "cold_start_tracing", "1", False), + ("DD_COLD_START_TRACING", "cold_start_tracing", "1", True), # CHANGED ("DD_COLD_START_TRACING", "cold_start_tracing", "0", False), ("DD_COLD_START_TRACING", "cold_start_tracing", "purple", False), @@ -49,7 +49,7 @@ ("DD_FLUSH_IN_THREAD", "flush_in_thread", "TRUE", True), ("DD_FLUSH_IN_THREAD", "flush_in_thread", "false", False), ("DD_FLUSH_IN_THREAD", "flush_in_thread", "FALSE", False), - ("DD_FLUSH_IN_THREAD", "flush_in_thread", "1", False), + ("DD_FLUSH_IN_THREAD", "flush_in_thread", "1", True), # CHANGED ("DD_FLUSH_IN_THREAD", "flush_in_thread", "0", False), ("DD_FLUSH_IN_THREAD", "flush_in_thread", "purple", False), @@ -59,7 +59,7 @@ ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "TRUE", True), ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "false", False), ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "FALSE", False), - ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "1", False), + ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "1", True), # CHANGED ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "0", False), ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "purple", False), @@ -69,7 +69,7 @@ ("DD_INTEGRATION_TEST", "is_in_tests", "TRUE", True), ("DD_INTEGRATION_TEST", "is_in_tests", "false", False), ("DD_INTEGRATION_TEST", "is_in_tests", "FALSE", False), - ("DD_INTEGRATION_TEST", "is_in_tests", "1", False), + ("DD_INTEGRATION_TEST", "is_in_tests", "1", True), # CHANGED ("DD_INTEGRATION_TEST", "is_in_tests", "0", False), ("DD_INTEGRATION_TEST", "is_in_tests", "purple", False), @@ -89,7 +89,7 @@ ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "TRUE", True), ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "false", False), ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "FALSE", False), - ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "1", False), + ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "1", True), # CHANGED ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "0", False), ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "purple", False), @@ -103,7 +103,7 @@ ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "TRUE", True), ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "false", False), ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "FALSE", False), - ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "1", False), + ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "1", True), # CHANGED ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "0", False), ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "purple", False), ) From 40b666e6837e018c6576e851ca252c6510a8586a Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Thu, 15 May 2025 15:35:26 -0700 Subject: [PATCH 04/52] Tests correctly reset config object. --- datadog_lambda/config.py | 13 +++++++++---- tests/conftest.py | 7 +++++++ tests/test_api.py | 11 +++++++---- tests/test_cold_start.py | 2 +- tests/test_config.py | 28 +++++++++++++++++----------- tests/test_metric.py | 4 +++- tests/test_tracing.py | 24 +++++++++++------------- tests/test_wrapper.py | 18 +++++++----------- 8 files changed, 62 insertions(+), 45 deletions(-) create mode 100644 tests/conftest.py diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 601cfbba..32302650 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -17,7 +17,7 @@ def _getter(self): setattr(self, prop_key, val) return getattr(self, prop_key) - prop_key = f"_{key}" + prop_key = f"_config_{key}" return _getter @@ -42,12 +42,17 @@ class Config: @property def fips_mode_enabled(self): - if not hasattr(self, "_fips_mode_enabled"): - self._fips_mode_enabled = os.environ.get( + if not hasattr(self, "_config_fips_mode_enabled"): + self._config_fips_mode_enabled = os.environ.get( "DD_LAMBDA_FIPS_MODE", "true" if self.is_gov_region else "false", ).lower() == "true" - return self._fips_mode_enabled + return self._config_fips_mode_enabled + + def reset(self): + for attr in dir(self): + if attr.startswith("_config_"): + delattr(self, attr) config = Config() diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..6159593f --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,7 @@ +import pytest + +from datadog_lambda.config import config + +@pytest.fixture(autouse=True) +def reset_config(): + config.reset() diff --git a/tests/test_api.py b/tests/test_api.py index 59ee4ee8..7fcc3c22 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -22,7 +22,10 @@ def setUp(self): ) self.env_patcher.start() - @patch("datadog_lambda.api.fips_mode_enabled", True) + def tearDown(self): + del os.environ["AWS_REGION"] + + @patch("datadog_lambda.config.Config.fips_mode_enabled", True) @patch("botocore.session.Session.create_client") def test_secrets_manager_fips_endpoint(self, mock_boto3_client): mock_client = MagicMock() @@ -63,7 +66,7 @@ def test_secrets_manager_different_region(self, mock_boto3_client): ) self.assertEqual(api_key, "test-api-key") - @patch("datadog_lambda.api.fips_mode_enabled", True) + @patch("datadog_lambda.config.Config.fips_mode_enabled", True) @patch("botocore.session.Session.create_client") def test_secrets_manager_different_region_but_still_fips(self, mock_boto3_client): mock_client = MagicMock() @@ -84,7 +87,7 @@ def test_secrets_manager_different_region_but_still_fips(self, mock_boto3_client ) self.assertEqual(api_key, "test-api-key") - @patch("datadog_lambda.api.fips_mode_enabled", True) + @patch("datadog_lambda.config.Config.fips_mode_enabled", True) @patch("botocore.session.Session.create_client") def test_ssm_fips_endpoint(self, mock_boto3_client): mock_client = MagicMock() @@ -103,7 +106,7 @@ def test_ssm_fips_endpoint(self, mock_boto3_client): ) self.assertEqual(api_key, "test-api-key") - @patch("datadog_lambda.api.fips_mode_enabled", True) + @patch("datadog_lambda.config.Config.fips_mode_enabled", True) @patch("botocore.session.Session.create_client") @patch("datadog_lambda.api.decrypt_kms_api_key") def test_kms_fips_endpoint(self, mock_decrypt_kms, mock_boto3_client): diff --git a/tests/test_cold_start.py b/tests/test_cold_start.py index c7444c49..f90df270 100644 --- a/tests/test_cold_start.py +++ b/tests/test_cold_start.py @@ -247,7 +247,7 @@ def finish(span): monkeypatch.setattr(wrapper.tracer, "_on_span_finish", finish) monkeypatch.setattr(wrapper, "is_new_sandbox", lambda: True) - monkeypatch.setattr("datadog_lambda.wrapper.dd_tracing_enabled", True) + monkeypatch.setattr("datadog_lambda.config.Config.trace_enabled", True) monkeypatch.setenv( "DD_COLD_START_TRACE_SKIP_LIB", "ddtrace.contrib.logging,datadog_lambda.wrapper" ) diff --git a/tests/test_config.py b/tests/test_config.py index 656ba6c7..68e6150f 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,6 +1,16 @@ import pytest -from datadog_lambda.config import Config +from datadog_lambda.config import config + + +@pytest.fixture +def setenv(monkeypatch): + def set_env(key, value): + if value is None: + monkeypatch.delenv(key, raising=False) + else: + monkeypatch.setenv(key, value) + return set_env _test_config_from_environ = ( @@ -109,10 +119,8 @@ ) @pytest.mark.parametrize('env_key,conf_key,env_val,conf_val', _test_config_from_environ) -def test_config_from_environ(env_key, conf_key, env_val, conf_val, monkeypatch): - if env_val is not None: - monkeypatch.setenv(env_key, env_val) - config = Config() +def test_config_from_environ(env_key, conf_key, env_val, conf_val, setenv): + setenv(env_key, env_val) assert getattr(config, conf_key) == conf_val @@ -160,9 +168,7 @@ def test_config_from_environ(env_key, conf_key, env_val, conf_val, monkeypatch): ) @pytest.mark.parametrize('fips_mode,region,conf_val', _test_fips_mode_from_environ) -def test_fips_mode_from_environ(fips_mode, region, conf_val, monkeypatch): - if fips_mode is not None: - monkeypatch.setenv("DD_LAMBDA_FIPS_MODE", fips_mode) - if region is not None: - monkeypatch.setenv("AWS_REGION", region) - assert Config().fips_mode_enabled == conf_val +def test_fips_mode_from_environ(fips_mode, region, conf_val, setenv): + setenv("DD_LAMBDA_FIPS_MODE", fips_mode) + setenv("AWS_REGION", region) + assert config.fips_mode_enabled == conf_val diff --git a/tests/test_metric.py b/tests/test_metric.py index e7dab2c3..3de43334 100644 --- a/tests/test_metric.py +++ b/tests/test_metric.py @@ -7,6 +7,7 @@ from datadog.api.exceptions import ClientError from datadog_lambda.api import KMS_ENCRYPTION_CONTEXT_KEY, decrypt_kms_api_key +from datadog_lambda.config import config from datadog_lambda.metric import ( MetricsHandler, _select_metrics_handler, @@ -19,6 +20,7 @@ class TestLambdaMetric(unittest.TestCase): def setUp(self): + config.reset() lambda_stats_patcher = patch("datadog_lambda.metric.lambda_stats") self.mock_metric_lambda_stats = lambda_stats_patcher.start() self.addCleanup(lambda_stats_patcher.stop) @@ -62,7 +64,7 @@ def test_select_metrics_handler_dd_api_fallback(self): self.assertEqual(MetricsHandler.DATADOG_API, _select_metrics_handler()) del os.environ["DD_FLUSH_TO_LOG"] - @patch("datadog_lambda.metric.fips_mode_enabled", True) + @patch("datadog_lambda.config.Config.fips_mode_enabled", True) @patch("datadog_lambda.metric.should_use_extension", False) def test_select_metrics_handler_has_no_fallback_in_fips_mode(self): os.environ["DD_FLUSH_TO_LOG"] = "False" diff --git a/tests/test_tracing.py b/tests/test_tracing.py index e38e4ecd..27513e64 100644 --- a/tests/test_tracing.py +++ b/tests/test_tracing.py @@ -15,6 +15,7 @@ from ddtrace._trace._span_pointer import _SpanPointerDirection from ddtrace._trace._span_pointer import _SpanPointerDescription +from datadog_lambda.config import config from datadog_lambda.constants import ( SamplingPriority, TraceHeader, @@ -251,20 +252,16 @@ def test_extract_dd_trace_context(event, expect): class TestExtractAndGetDDTraceContext(unittest.TestCase): def setUp(self): - global dd_tracing_enabled - dd_tracing_enabled = False os.environ["_X_AMZN_TRACE_ID"] = fake_xray_header_value patcher = patch("datadog_lambda.tracing.send_segment") self.mock_send_segment = patcher.start() self.addCleanup(patcher.stop) - patcher = patch("datadog_lambda.tracing.is_lambda_context") + patcher = patch("datadog_lambda.config.Config.is_lambda_context") self.mock_is_lambda_context = patcher.start() self.mock_is_lambda_context.return_value = True self.addCleanup(patcher.stop) def tearDown(self): - global dd_tracing_enabled - dd_tracing_enabled = False del os.environ["_X_AMZN_TRACE_ID"] @with_trace_propagation_style("datadog") @@ -975,6 +972,7 @@ def test_convert_xray_sampling(self): class TestLogsInjection(unittest.TestCase): def setUp(self): + config.reset() patcher = patch("datadog_lambda.tracing.get_dd_trace_context_obj") self.mock_get_dd_trace_context = patcher.start() self.mock_get_dd_trace_context.return_value = Context( @@ -984,11 +982,12 @@ def setUp(self): ) self.addCleanup(patcher.stop) - patcher = patch("datadog_lambda.tracing.is_lambda_context") + patcher = patch("datadog_lambda.config.Config.is_lambda_context") self.mock_is_lambda_context = patcher.start() self.mock_is_lambda_context.return_value = True self.addCleanup(patcher.stop) + @patch("datadog_lambda.config.Config.trace_enabled", False) def test_set_correlation_ids(self): set_correlation_ids() span = tracer.current_span() @@ -1124,13 +1123,11 @@ def test_function_with_span_pointers(self): class TestSetTraceRootSpan(unittest.TestCase): def setUp(self): - global dd_tracing_enabled - dd_tracing_enabled = False os.environ["_X_AMZN_TRACE_ID"] = fake_xray_header_value patcher = patch("datadog_lambda.tracing.send_segment") self.mock_send_segment = patcher.start() self.addCleanup(patcher.stop) - patcher = patch("datadog_lambda.tracing.is_lambda_context") + patcher = patch("datadog_lambda.config.Config.is_lambda_context") self.mock_is_lambda_context = patcher.start() self.mock_is_lambda_context.return_value = True self.addCleanup(patcher.stop) @@ -1143,8 +1140,6 @@ def setUp(self): self.addCleanup(patcher.stop) def tearDown(self): - global dd_tracing_enabled - dd_tracing_enabled = False del os.environ["_X_AMZN_TRACE_ID"] def test_mixed_parent_context_when_merging(self): @@ -1245,6 +1240,7 @@ def test_get_service_mapping(self): create_service_mapping(os.environ["DD_SERVICE_MAPPING"]) ) self.assertEqual(self.get_service_mapping(), expected_output) + del os.environ["DD_SERVICE_MAPPING"] def test_set_service_mapping(self): new_service_mapping = {"api3": "service3", "api4": "service4"} @@ -1285,6 +1281,8 @@ def test_determine_service_name(self): "default", ) + del os.environ["DD_SERVICE_MAPPING"] + def test_remaps_all_inferred_span_service_names_from_api_gateway_event(self): new_service_mapping = {"lambda_api_gateway": "new-name"} self.set_service_mapping(new_service_mapping) @@ -2386,7 +2384,7 @@ def test_deterministic_m5_hash__always_leading_with_zero(self): class TestExceptionOutsideHandler(unittest.TestCase): - @patch("datadog_lambda.tracing.dd_tracing_enabled", True) + @patch("datadog_lambda.config.Config.trace_enabled", True) @patch("datadog_lambda.tracing.submit_errors_metric") @patch("time.time_ns", return_value=42) def test_exception_outside_handler_tracing_enabled( @@ -2427,7 +2425,7 @@ def test_exception_outside_handler_tracing_enabled( assert mock_span.error == 1 assert mock_span.start_ns == 42 - @patch("datadog_lambda.tracing.dd_tracing_enabled", False) + @patch("datadog_lambda.config.Config.trace_enabled", False) @patch("datadog_lambda.tracing.submit_errors_metric") @patch("time.time_ns", return_value=42) def test_exception_outside_handler_tracing_disabled( diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index f482fa3d..97569095 100644 --- a/tests/test_wrapper.py +++ b/tests/test_wrapper.py @@ -8,6 +8,8 @@ import datadog_lambda.wrapper as wrapper import datadog_lambda.xray as xray + +from datadog_lambda.config import config from datadog_lambda.metric import lambda_metric from datadog_lambda.thread_stats_writer import ThreadStatsWriter from ddtrace.trace import Span, tracer @@ -24,7 +26,6 @@ def setUp(self): patch("ddtrace.internal.writer.AgentWriter.flush_queue").start() wrapper.datadog_lambda_wrapper._force_wrap = True - wrapper.dd_tracing_enabled = True patcher = patch( "datadog.threadstats.reporters.HttpReporter.flush_distributions" ) @@ -80,9 +81,8 @@ def setUp(self): self.mock_set_dsm_context = patcher.start() self.addCleanup(patcher.stop) + @patch("datadog_lambda.config.Config.trace_enabled", False) def test_datadog_lambda_wrapper(self): - wrapper.dd_tracing_enabled = False - @wrapper.datadog_lambda_wrapper def lambda_handler(event, context): lambda_metric("test.metric", 100) @@ -92,7 +92,6 @@ def lambda_handler(event, context): lambda_context = get_mock_context() lambda_handler(lambda_event, lambda_context) - wrapper.dd_tracing_enabled = True self.mock_threadstats_flush_distributions.assert_has_calls( [ call( @@ -189,9 +188,9 @@ def lambda_handler(event, context): metric_module.lambda_stats.stop() metric_module.lambda_stats = ThreadStatsWriter(False) + @patch("datadog_lambda.config.Config.trace_enabled", False) def test_datadog_lambda_wrapper_inject_correlation_ids(self): os.environ["DD_LOGS_INJECTION"] = "True" - wrapper.dd_tracing_enabled = False @wrapper.datadog_lambda_wrapper def lambda_handler(event, context): @@ -199,7 +198,6 @@ def lambda_handler(event, context): lambda_event = {} lambda_handler(lambda_event, get_mock_context()) - wrapper.dd_tracing_enabled = True self.mock_set_correlation_ids.assert_called() self.mock_inject_correlation_ids.assert_called() @@ -457,11 +455,8 @@ def lambda_handler(event, context): ] ) + @patch("datadog_lambda.config.Config.enhanced_metrics_enabled", False) def test_no_enhanced_metrics_without_env_var(self): - patcher = patch("datadog_lambda.metric.enhanced_metrics_enabled", False) - patcher.start() - self.addCleanup(patcher.stop) - @wrapper.datadog_lambda_wrapper def lambda_handler(event, context): raise RuntimeError() @@ -625,8 +620,9 @@ def lambda_handler(event, context): class TestLambdaDecoratorSettings(unittest.TestCase): + + @patch("datadog_lambda.config.Config.trace_enabled", False) def test_some_envs_should_depend_on_dd_tracing_enabled(self): - wrapper.dd_tracing_enabled = False os.environ[wrapper.DD_TRACE_MANAGED_SERVICES] = "true" os.environ[wrapper.DD_ENCODE_AUTHORIZER_CONTEXT] = "true" os.environ[wrapper.DD_DECODE_AUTHORIZER_CONTEXT] = "true" From fb20e39d18766809a1043ede080137330c598d66 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Thu, 15 May 2025 15:36:34 -0700 Subject: [PATCH 05/52] Black. --- datadog_lambda/config.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 32302650..881993a4 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -37,16 +37,21 @@ class Config: is_in_tests = _get_env("DD_INTEGRATION_TEST", "false", as_bool) is_lambda_context = _get_env("AWS_LAMBDA_FUNCTION_NAME", None, bool) otel_enabled = _get_env("DD_TRACE_OTEL_ENABLED", "false", as_bool) - telemetry_enabled = _get_env("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "false", as_bool) + telemetry_enabled = _get_env( + "DD_INSTRUMENTATION_TELEMETRY_ENABLED", "false", as_bool + ) trace_enabled = _get_env("DD_TRACE_ENABLED", "true", as_bool) @property def fips_mode_enabled(self): if not hasattr(self, "_config_fips_mode_enabled"): - self._config_fips_mode_enabled = os.environ.get( - "DD_LAMBDA_FIPS_MODE", - "true" if self.is_gov_region else "false", - ).lower() == "true" + self._config_fips_mode_enabled = ( + os.environ.get( + "DD_LAMBDA_FIPS_MODE", + "true" if self.is_gov_region else "false", + ).lower() + == "true" + ) return self._config_fips_mode_enabled def reset(self): From 55a47f5a82986d666f561ced1e4c4479ee1a7474 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Thu, 15 May 2025 15:38:54 -0700 Subject: [PATCH 06/52] More black. --- tests/conftest.py | 1 + tests/test_config.py | 272 ++++++++++++++++++++---------------------- tests/test_wrapper.py | 1 - 3 files changed, 129 insertions(+), 145 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 6159593f..091265e9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,6 +2,7 @@ from datadog_lambda.config import config + @pytest.fixture(autouse=True) def reset_config(): config.reset() diff --git a/tests/test_config.py b/tests/test_config.py index 68e6150f..868f5a33 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -10,164 +10,148 @@ def set_env(key, value): monkeypatch.delenv(key, raising=False) else: monkeypatch.setenv(key, value) + return set_env _test_config_from_environ = ( - ("AWS_LAMBDA_FUNCTION_NAME", "function_name", None, None), - ("AWS_LAMBDA_FUNCTION_NAME", "function_name", "", ""), - ("AWS_LAMBDA_FUNCTION_NAME", "function_name", "my_function", "my_function"), - - ("DD_FLUSH_TO_LOG", "flush_to_log", None, False), - ("DD_FLUSH_TO_LOG", "flush_to_log", "", False), - ("DD_FLUSH_TO_LOG", "flush_to_log", "true", True), - ("DD_FLUSH_TO_LOG", "flush_to_log", "TRUE", True), - ("DD_FLUSH_TO_LOG", "flush_to_log", "false", False), - ("DD_FLUSH_TO_LOG", "flush_to_log", "FALSE", False), - ("DD_FLUSH_TO_LOG", "flush_to_log", "1", True), # CHANGED - ("DD_FLUSH_TO_LOG", "flush_to_log", "0", False), - ("DD_FLUSH_TO_LOG", "flush_to_log", "purple", False), - - ("DD_TRACE_ENABLED", "trace_enabled", None, True), - ("DD_TRACE_ENABLED", "trace_enabled", "", False), - ("DD_TRACE_ENABLED", "trace_enabled", "true", True), - ("DD_TRACE_ENABLED", "trace_enabled", "TRUE", True), - ("DD_TRACE_ENABLED", "trace_enabled", "false", False), - ("DD_TRACE_ENABLED", "trace_enabled", "FALSE", False), - ("DD_TRACE_ENABLED", "trace_enabled", "1", True), # CHANGED - ("DD_TRACE_ENABLED", "trace_enabled", "0", False), - ("DD_TRACE_ENABLED", "trace_enabled", "purple", False), - - ("DD_COLD_START_TRACING", "cold_start_tracing", None, True), - ("DD_COLD_START_TRACING", "cold_start_tracing", "", False), - ("DD_COLD_START_TRACING", "cold_start_tracing", "true", True), - ("DD_COLD_START_TRACING", "cold_start_tracing", "TRUE", True), - ("DD_COLD_START_TRACING", "cold_start_tracing", "false", False), - ("DD_COLD_START_TRACING", "cold_start_tracing", "FALSE", False), - ("DD_COLD_START_TRACING", "cold_start_tracing", "1", True), # CHANGED - ("DD_COLD_START_TRACING", "cold_start_tracing", "0", False), - ("DD_COLD_START_TRACING", "cold_start_tracing", "purple", False), - - ("AWS_REGION", "is_gov_region", None, False), - ("AWS_REGION", "is_gov_region", "", False), - ("AWS_REGION", "is_gov_region", "us-gov-1", True), - ("AWS_REGION", "is_gov_region", "us-est-1", False), - - ("DD_FLUSH_IN_THREAD", "flush_in_thread", None, False), - ("DD_FLUSH_IN_THREAD", "flush_in_thread", "", False), - ("DD_FLUSH_IN_THREAD", "flush_in_thread", "true", True), - ("DD_FLUSH_IN_THREAD", "flush_in_thread", "TRUE", True), - ("DD_FLUSH_IN_THREAD", "flush_in_thread", "false", False), - ("DD_FLUSH_IN_THREAD", "flush_in_thread", "FALSE", False), - ("DD_FLUSH_IN_THREAD", "flush_in_thread", "1", True), # CHANGED - ("DD_FLUSH_IN_THREAD", "flush_in_thread", "0", False), - ("DD_FLUSH_IN_THREAD", "flush_in_thread", "purple", False), - - ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", None, True), - ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "", False), - ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "true", True), - ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "TRUE", True), - ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "false", False), - ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "FALSE", False), - ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "1", True), # CHANGED - ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "0", False), - ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "purple", False), - - ("DD_INTEGRATION_TEST", "is_in_tests", None, False), - ("DD_INTEGRATION_TEST", "is_in_tests", "", False), - ("DD_INTEGRATION_TEST", "is_in_tests", "true", True), - ("DD_INTEGRATION_TEST", "is_in_tests", "TRUE", True), - ("DD_INTEGRATION_TEST", "is_in_tests", "false", False), - ("DD_INTEGRATION_TEST", "is_in_tests", "FALSE", False), - ("DD_INTEGRATION_TEST", "is_in_tests", "1", True), # CHANGED - ("DD_INTEGRATION_TEST", "is_in_tests", "0", False), - ("DD_INTEGRATION_TEST", "is_in_tests", "purple", False), - - ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", None, True), - ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "", False), - ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "true", True), - ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "TRUE", True), - ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "false", False), - ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "FALSE", False), - ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "1", True), - ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "0", False), - ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "purple", False), - - ("DD_TRACE_OTEL_ENABLED", "otel_enabled", None, False), - ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "", False), - ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "true", True), - ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "TRUE", True), - ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "false", False), - ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "FALSE", False), - ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "1", True), # CHANGED - ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "0", False), - ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "purple", False), - - ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", None, False), - ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", "", False), - ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", "my_function", True), - - ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", None, False), - ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "", False), - ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "true", True), - ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "TRUE", True), - ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "false", False), - ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "FALSE", False), - ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "1", True), # CHANGED - ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "0", False), - ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "purple", False), + ("AWS_LAMBDA_FUNCTION_NAME", "function_name", None, None), + ("AWS_LAMBDA_FUNCTION_NAME", "function_name", "", ""), + ("AWS_LAMBDA_FUNCTION_NAME", "function_name", "my_function", "my_function"), + ("DD_FLUSH_TO_LOG", "flush_to_log", None, False), + ("DD_FLUSH_TO_LOG", "flush_to_log", "", False), + ("DD_FLUSH_TO_LOG", "flush_to_log", "true", True), + ("DD_FLUSH_TO_LOG", "flush_to_log", "TRUE", True), + ("DD_FLUSH_TO_LOG", "flush_to_log", "false", False), + ("DD_FLUSH_TO_LOG", "flush_to_log", "FALSE", False), + ("DD_FLUSH_TO_LOG", "flush_to_log", "1", True), # CHANGED + ("DD_FLUSH_TO_LOG", "flush_to_log", "0", False), + ("DD_FLUSH_TO_LOG", "flush_to_log", "purple", False), + ("DD_TRACE_ENABLED", "trace_enabled", None, True), + ("DD_TRACE_ENABLED", "trace_enabled", "", False), + ("DD_TRACE_ENABLED", "trace_enabled", "true", True), + ("DD_TRACE_ENABLED", "trace_enabled", "TRUE", True), + ("DD_TRACE_ENABLED", "trace_enabled", "false", False), + ("DD_TRACE_ENABLED", "trace_enabled", "FALSE", False), + ("DD_TRACE_ENABLED", "trace_enabled", "1", True), # CHANGED + ("DD_TRACE_ENABLED", "trace_enabled", "0", False), + ("DD_TRACE_ENABLED", "trace_enabled", "purple", False), + ("DD_COLD_START_TRACING", "cold_start_tracing", None, True), + ("DD_COLD_START_TRACING", "cold_start_tracing", "", False), + ("DD_COLD_START_TRACING", "cold_start_tracing", "true", True), + ("DD_COLD_START_TRACING", "cold_start_tracing", "TRUE", True), + ("DD_COLD_START_TRACING", "cold_start_tracing", "false", False), + ("DD_COLD_START_TRACING", "cold_start_tracing", "FALSE", False), + ("DD_COLD_START_TRACING", "cold_start_tracing", "1", True), # CHANGED + ("DD_COLD_START_TRACING", "cold_start_tracing", "0", False), + ("DD_COLD_START_TRACING", "cold_start_tracing", "purple", False), + ("AWS_REGION", "is_gov_region", None, False), + ("AWS_REGION", "is_gov_region", "", False), + ("AWS_REGION", "is_gov_region", "us-gov-1", True), + ("AWS_REGION", "is_gov_region", "us-est-1", False), + ("DD_FLUSH_IN_THREAD", "flush_in_thread", None, False), + ("DD_FLUSH_IN_THREAD", "flush_in_thread", "", False), + ("DD_FLUSH_IN_THREAD", "flush_in_thread", "true", True), + ("DD_FLUSH_IN_THREAD", "flush_in_thread", "TRUE", True), + ("DD_FLUSH_IN_THREAD", "flush_in_thread", "false", False), + ("DD_FLUSH_IN_THREAD", "flush_in_thread", "FALSE", False), + ("DD_FLUSH_IN_THREAD", "flush_in_thread", "1", True), # CHANGED + ("DD_FLUSH_IN_THREAD", "flush_in_thread", "0", False), + ("DD_FLUSH_IN_THREAD", "flush_in_thread", "purple", False), + ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", None, True), + ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "", False), + ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "true", True), + ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "TRUE", True), + ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "false", False), + ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "FALSE", False), + ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "1", True), # CHANGED + ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "0", False), + ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "purple", False), + ("DD_INTEGRATION_TEST", "is_in_tests", None, False), + ("DD_INTEGRATION_TEST", "is_in_tests", "", False), + ("DD_INTEGRATION_TEST", "is_in_tests", "true", True), + ("DD_INTEGRATION_TEST", "is_in_tests", "TRUE", True), + ("DD_INTEGRATION_TEST", "is_in_tests", "false", False), + ("DD_INTEGRATION_TEST", "is_in_tests", "FALSE", False), + ("DD_INTEGRATION_TEST", "is_in_tests", "1", True), # CHANGED + ("DD_INTEGRATION_TEST", "is_in_tests", "0", False), + ("DD_INTEGRATION_TEST", "is_in_tests", "purple", False), + ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", None, True), + ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "", False), + ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "true", True), + ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "TRUE", True), + ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "false", False), + ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "FALSE", False), + ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "1", True), + ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "0", False), + ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "purple", False), + ("DD_TRACE_OTEL_ENABLED", "otel_enabled", None, False), + ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "", False), + ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "true", True), + ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "TRUE", True), + ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "false", False), + ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "FALSE", False), + ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "1", True), # CHANGED + ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "0", False), + ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "purple", False), + ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", None, False), + ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", "", False), + ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", "my_function", True), + ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", None, False), + ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "", False), + ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "true", True), + ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "TRUE", True), + ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "false", False), + ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "FALSE", False), + ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "1", True), # CHANGED + ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "0", False), + ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "purple", False), ) -@pytest.mark.parametrize('env_key,conf_key,env_val,conf_val', _test_config_from_environ) + +@pytest.mark.parametrize("env_key,conf_key,env_val,conf_val", _test_config_from_environ) def test_config_from_environ(env_key, conf_key, env_val, conf_val, setenv): setenv(env_key, env_val) assert getattr(config, conf_key) == conf_val _test_fips_mode_from_environ = ( - (None, None, False), - (None, "", False), - (None, "us-gov-1", True), - (None, "us-east-1", False), - - ("", None, False), - ("", "", False), - ("", "us-gov-1", False), - ("", "us-east-1", False), - - ("true", None, True), - ("true", "", True), - ("true", "us-gov-1", True), - ("true", "us-east-1", True), - - ("TRUE", None, True), - ("TRUE", "", True), - ("TRUE", "us-gov-1", True), - ("TRUE", "us-east-1", True), - - ("false", None, False), - ("false", "", False), - ("false", "us-gov-1", False), - ("false", "us-east-1", False), - - ("FALSE", None, False), - ("FALSE", "", False), - ("FALSE", "us-gov-1", False), - ("FALSE", "us-east-1", False), - - - ("1", None, False), - ("1", "", False), - ("1", "us-gov-1", False), - ("1", "us-east-1", False), - - ("0", None, False), - ("0", "", False), - ("0", "us-gov-1", False), - ("0", "us-east-1", False), + (None, None, False), + (None, "", False), + (None, "us-gov-1", True), + (None, "us-east-1", False), + ("", None, False), + ("", "", False), + ("", "us-gov-1", False), + ("", "us-east-1", False), + ("true", None, True), + ("true", "", True), + ("true", "us-gov-1", True), + ("true", "us-east-1", True), + ("TRUE", None, True), + ("TRUE", "", True), + ("TRUE", "us-gov-1", True), + ("TRUE", "us-east-1", True), + ("false", None, False), + ("false", "", False), + ("false", "us-gov-1", False), + ("false", "us-east-1", False), + ("FALSE", None, False), + ("FALSE", "", False), + ("FALSE", "us-gov-1", False), + ("FALSE", "us-east-1", False), + ("1", None, False), + ("1", "", False), + ("1", "us-gov-1", False), + ("1", "us-east-1", False), + ("0", None, False), + ("0", "", False), + ("0", "us-gov-1", False), + ("0", "us-east-1", False), ) -@pytest.mark.parametrize('fips_mode,region,conf_val', _test_fips_mode_from_environ) + +@pytest.mark.parametrize("fips_mode,region,conf_val", _test_fips_mode_from_environ) def test_fips_mode_from_environ(fips_mode, region, conf_val, setenv): setenv("DD_LAMBDA_FIPS_MODE", fips_mode) setenv("AWS_REGION", region) diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index 97569095..76ef5fc3 100644 --- a/tests/test_wrapper.py +++ b/tests/test_wrapper.py @@ -620,7 +620,6 @@ def lambda_handler(event, context): class TestLambdaDecoratorSettings(unittest.TestCase): - @patch("datadog_lambda.config.Config.trace_enabled", False) def test_some_envs_should_depend_on_dd_tracing_enabled(self): os.environ[wrapper.DD_TRACE_MANAGED_SERVICES] = "true" From 5da02672ea6c984035a01eba3e7702c8df7d2441 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Thu, 15 May 2025 15:40:08 -0700 Subject: [PATCH 07/52] Use config. --- datadog_lambda/patch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datadog_lambda/patch.py b/datadog_lambda/patch.py index da07c6b0..e5aa5540 100644 --- a/datadog_lambda/patch.py +++ b/datadog_lambda/patch.py @@ -29,7 +29,7 @@ def patch_all(): """ _patch_for_integration_tests() - if dd_tracing_enabled: + if config.trace_enabled: patch_all_dd() else: _patch_http() From 9dd96f1cfc82917168ac16aa83d554f3a39786b8 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Tue, 3 Jun 2025 14:09:53 -0700 Subject: [PATCH 08/52] Correct config import. --- datadog_lambda/patch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datadog_lambda/patch.py b/datadog_lambda/patch.py index e5aa5540..11b25c65 100644 --- a/datadog_lambda/patch.py +++ b/datadog_lambda/patch.py @@ -12,7 +12,7 @@ from wrapt.importer import when_imported from ddtrace import patch_all as patch_all_dd -from datadog_lambda import config +from datadog_lambda.config import config from datadog_lambda.tracing import get_dd_trace_context from collections.abc import MutableMapping From d48a5466d75418fb5f6df6044cd240accf28ac90 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 09:20:27 -0700 Subject: [PATCH 09/52] ??????????? --- datadog_lambda/patch.py | 1 + tests/test_cold_start.py | 1 + 2 files changed, 2 insertions(+) diff --git a/datadog_lambda/patch.py b/datadog_lambda/patch.py index 11b25c65..3c309fa7 100644 --- a/datadog_lambda/patch.py +++ b/datadog_lambda/patch.py @@ -30,6 +30,7 @@ def patch_all(): _patch_for_integration_tests() if config.trace_enabled: + # XXX this call is making the tests fail patch_all_dd() else: _patch_http() diff --git a/tests/test_cold_start.py b/tests/test_cold_start.py index f90df270..d437de3d 100644 --- a/tests/test_cold_start.py +++ b/tests/test_cold_start.py @@ -261,6 +261,7 @@ def handler(event, context): lambda_context.invoked_function_arn = ( "arn:aws:lambda:us-west-1:123457598159:function:python-layer-test:1" ) + lambda_context.get_remaining_time_in_millis = lambda: 100 handler.cold_start_tracing = True handler({}, lambda_context) From f0ad15dae25f2007302ea925e93c3e7cef220bf1 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 09:49:12 -0700 Subject: [PATCH 10/52] Fix unittests. --- datadog_lambda/patch.py | 1 - tests/test_cold_start.py | 8 +++----- tests/test_patch.py | 8 ++++++++ tests/utils.py | 1 + 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/datadog_lambda/patch.py b/datadog_lambda/patch.py index 3c309fa7..11b25c65 100644 --- a/datadog_lambda/patch.py +++ b/datadog_lambda/patch.py @@ -30,7 +30,6 @@ def patch_all(): _patch_for_integration_tests() if config.trace_enabled: - # XXX this call is making the tests fail patch_all_dd() else: _patch_http() diff --git a/tests/test_cold_start.py b/tests/test_cold_start.py index d437de3d..d75b5f43 100644 --- a/tests/test_cold_start.py +++ b/tests/test_cold_start.py @@ -8,6 +8,8 @@ import datadog_lambda.cold_start as cold_start import datadog_lambda.wrapper as wrapper +from tests.utils import get_mock_context + class TestColdStartTracingSetup(unittest.TestCase): def test_proactive_init(self): @@ -257,11 +259,7 @@ def finish(span): def handler(event, context): import tabnanny - lambda_context = MagicMock() - lambda_context.invoked_function_arn = ( - "arn:aws:lambda:us-west-1:123457598159:function:python-layer-test:1" - ) - lambda_context.get_remaining_time_in_millis = lambda: 100 + lambda_context = get_mock_context() handler.cold_start_tracing = True handler({}, lambda_context) diff --git a/tests/test_patch.py b/tests/test_patch.py index bf924875..b03d2e23 100644 --- a/tests/test_patch.py +++ b/tests/test_patch.py @@ -1,3 +1,4 @@ +import pytest import unittest from unittest.mock import patch, MagicMock @@ -5,6 +6,13 @@ from datadog_lambda.patch import _patch_http, _ensure_patch_requests from datadog_lambda.constants import TraceHeader +from ddtrace.contrib.internal.requests.patch import unpatch as unpatch_requests + + +@pytest.fixture(scope="module", autouse=True) +def reset_patches(): + unpatch_requests() + class TestPatchHTTPClients(unittest.TestCase): def setUp(self): diff --git a/tests/utils.py b/tests/utils.py index 0f246e68..2d56ca0c 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -22,6 +22,7 @@ def get_mock_context( lambda_context.invoked_function_arn = invoked_function_arn lambda_context.function_version = function_version lambda_context.function_name = function_name + lambda_context.get_remaining_time_in_millis = lambda: 100 lambda_context.client_context = ClientContext(custom) return lambda_context From b89acabd0ad3f1b512b3ef9f29683867c1c8745b Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 09:52:22 -0700 Subject: [PATCH 11/52] Consolidate reset. --- datadog_lambda/config.py | 2 +- tests/conftest.py | 2 +- tests/test_metric.py | 2 -- tests/test_tracing.py | 2 -- 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 881993a4..e03c6215 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -54,7 +54,7 @@ def fips_mode_enabled(self): ) return self._config_fips_mode_enabled - def reset(self): + def _reset(self): for attr in dir(self): if attr.startswith("_config_"): delattr(self, attr) diff --git a/tests/conftest.py b/tests/conftest.py index 091265e9..33869802 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,4 +5,4 @@ @pytest.fixture(autouse=True) def reset_config(): - config.reset() + config._reset() diff --git a/tests/test_metric.py b/tests/test_metric.py index 3de43334..aa537d34 100644 --- a/tests/test_metric.py +++ b/tests/test_metric.py @@ -7,7 +7,6 @@ from datadog.api.exceptions import ClientError from datadog_lambda.api import KMS_ENCRYPTION_CONTEXT_KEY, decrypt_kms_api_key -from datadog_lambda.config import config from datadog_lambda.metric import ( MetricsHandler, _select_metrics_handler, @@ -20,7 +19,6 @@ class TestLambdaMetric(unittest.TestCase): def setUp(self): - config.reset() lambda_stats_patcher = patch("datadog_lambda.metric.lambda_stats") self.mock_metric_lambda_stats = lambda_stats_patcher.start() self.addCleanup(lambda_stats_patcher.stop) diff --git a/tests/test_tracing.py b/tests/test_tracing.py index 27513e64..a629343e 100644 --- a/tests/test_tracing.py +++ b/tests/test_tracing.py @@ -15,7 +15,6 @@ from ddtrace._trace._span_pointer import _SpanPointerDirection from ddtrace._trace._span_pointer import _SpanPointerDescription -from datadog_lambda.config import config from datadog_lambda.constants import ( SamplingPriority, TraceHeader, @@ -972,7 +971,6 @@ def test_convert_xray_sampling(self): class TestLogsInjection(unittest.TestCase): def setUp(self): - config.reset() patcher = patch("datadog_lambda.tracing.get_dd_trace_context_obj") self.mock_get_dd_trace_context = patcher.start() self.mock_get_dd_trace_context.return_value = Context( From ab67bd4bf3227ae3769e8cd94577a93f9f1ca630 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 09:54:54 -0700 Subject: [PATCH 12/52] Flush to logs from config. --- datadog_lambda/wrapper.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index ec6c5d86..3bb15559 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -63,7 +63,6 @@ logger = logging.getLogger(__name__) -DD_FLUSH_TO_LOG = "DD_FLUSH_TO_LOG" DD_LOGS_INJECTION = "DD_LOGS_INJECTION" DD_MERGE_XRAY_TRACES = "DD_MERGE_XRAY_TRACES" AWS_LAMBDA_FUNCTION_NAME = "AWS_LAMBDA_FUNCTION_NAME" @@ -161,7 +160,6 @@ def __init__(self, func): """Executes when the wrapped function gets wrapped""" try: self.func = func - self.flush_to_log = os.environ.get(DD_FLUSH_TO_LOG, "").lower() == "true" self.logs_injection = ( os.environ.get(DD_LOGS_INJECTION, "true").lower() == "true" ) @@ -400,7 +398,7 @@ def _after(self, event, context): except Exception as e: logger.debug("Failed to create cold start spans. %s", e) - if not self.flush_to_log or should_use_extension: + if not config.flush_to_log or should_use_extension: from datadog_lambda.metric import flush_stats flush_stats(context) From 91887a96e3dc9bafbf2ebe7f1d01c0a8af646e3c Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 09:59:02 -0700 Subject: [PATCH 13/52] Add logs_injection. --- datadog_lambda/config.py | 1 + datadog_lambda/wrapper.py | 5 +---- tests/test_config.py | 9 +++++++++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index e03c6215..8bf7753c 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -32,6 +32,7 @@ class Config: enhanced_metrics_enabled = _get_env("DD_ENHANCED_METRICS", "true", as_bool) flush_in_thread = _get_env("DD_FLUSH_IN_THREAD", "false", as_bool) flush_to_log = _get_env("DD_FLUSH_TO_LOG", "false", as_bool) + logs_injection = _get_env("DD_LOGS_INJECTION", "true", as_bool) function_name = _get_env("AWS_LAMBDA_FUNCTION_NAME") is_gov_region = _get_env("AWS_REGION", "", lambda x: x.startswith("us-gov-")) is_in_tests = _get_env("DD_INTEGRATION_TEST", "false", as_bool) diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index 3bb15559..b170e96b 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -160,9 +160,6 @@ def __init__(self, func): """Executes when the wrapped function gets wrapped""" try: self.func = func - self.logs_injection = ( - os.environ.get(DD_LOGS_INJECTION, "true").lower() == "true" - ) self.merge_xray_traces = ( os.environ.get(DD_MERGE_XRAY_TRACES, "false").lower() == "true" ) @@ -219,7 +216,7 @@ def __init__(self, func): self.trace_extractor = getattr(extractor_module, extractor_name) # Inject trace correlation ids to logs - if self.logs_injection: + if config.logs_injection: inject_correlation_ids() # This prevents a breaking change in ddtrace v0.49 regarding the service name diff --git a/tests/test_config.py b/tests/test_config.py index 868f5a33..5a63295d 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -27,6 +27,15 @@ def set_env(key, value): ("DD_FLUSH_TO_LOG", "flush_to_log", "1", True), # CHANGED ("DD_FLUSH_TO_LOG", "flush_to_log", "0", False), ("DD_FLUSH_TO_LOG", "flush_to_log", "purple", False), + ("DD_LOGS_INJECTION", "logs_injection", None, True), + ("DD_LOGS_INJECTION", "logs_injection", "", False), + ("DD_LOGS_INJECTION", "logs_injection", "true", True), + ("DD_LOGS_INJECTION", "logs_injection", "TRUE", True), + ("DD_LOGS_INJECTION", "logs_injection", "false", False), + ("DD_LOGS_INJECTION", "logs_injection", "FALSE", False), + ("DD_LOGS_INJECTION", "logs_injection", "1", True), # CHANGED + ("DD_LOGS_INJECTION", "logs_injection", "0", False), + ("DD_LOGS_INJECTION", "logs_injection", "purple", False), ("DD_TRACE_ENABLED", "trace_enabled", None, True), ("DD_TRACE_ENABLED", "trace_enabled", "", False), ("DD_TRACE_ENABLED", "trace_enabled", "true", True), From c4c3d8ddd4280ad492d27627114f05fb4567766a Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 11:10:31 -0700 Subject: [PATCH 14/52] Add merge_xray_traces. --- datadog_lambda/config.py | 1 + datadog_lambda/wrapper.py | 8 ++------ tests/test_config.py | 9 +++++++++ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 8bf7753c..9f386cea 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -42,6 +42,7 @@ class Config: "DD_INSTRUMENTATION_TELEMETRY_ENABLED", "false", as_bool ) trace_enabled = _get_env("DD_TRACE_ENABLED", "true", as_bool) + merge_xray_traces = _get_env("DD_MERGE_XRAY_TRACES", "false", as_bool) @property def fips_mode_enabled(self): diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index b170e96b..c5d135c0 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -64,7 +64,6 @@ logger = logging.getLogger(__name__) DD_LOGS_INJECTION = "DD_LOGS_INJECTION" -DD_MERGE_XRAY_TRACES = "DD_MERGE_XRAY_TRACES" AWS_LAMBDA_FUNCTION_NAME = "AWS_LAMBDA_FUNCTION_NAME" DD_LOCAL_TEST = "DD_LOCAL_TEST" DD_TRACE_EXTRACTOR = "DD_TRACE_EXTRACTOR" @@ -160,9 +159,6 @@ def __init__(self, func): """Executes when the wrapped function gets wrapped""" try: self.func = func - self.merge_xray_traces = ( - os.environ.get(DD_MERGE_XRAY_TRACES, "false").lower() == "true" - ) self.function_name = os.environ.get(AWS_LAMBDA_FUNCTION_NAME, "function") self.service = os.environ.get(DD_SERVICE, None) self.extractor_env = os.environ.get(DD_TRACE_EXTRACTOR, None) @@ -317,7 +313,7 @@ def _before(self, event, context): ) if config.trace_enabled: - set_dd_trace_py_root(trace_context_source, self.merge_xray_traces) + set_dd_trace_py_root(trace_context_source, config.merge_xray_traces) if self.make_inferred_span: self.inferred_span = create_inferred_span( event, context, event_source, self.decode_authorizer_context @@ -330,7 +326,7 @@ def _before(self, event, context): is_cold_start=is_cold_start(), is_proactive_init=is_proactive_init(), trace_context_source=trace_context_source, - merge_xray_traces=self.merge_xray_traces, + merge_xray_traces=config.merge_xray_traces, trigger_tags=self.trigger_tags, parent_span=self.inferred_span, span_pointers=calculate_span_pointers(event_source, event), diff --git a/tests/test_config.py b/tests/test_config.py index 5a63295d..26dce9dc 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -115,6 +115,15 @@ def set_env(key, value): ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "1", True), # CHANGED ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "0", False), ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "purple", False), + ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", None, False), + ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", "", False), + ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", "true", True), + ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", "TRUE", True), + ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", "false", False), + ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", "FALSE", False), + ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", "1", True), # CHANGED + ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", "0", False), + ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", "purple", False), ) From e2426f61ad12e9c44a74b2f327598648f7655dc7 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 11:12:09 -0700 Subject: [PATCH 15/52] Use functionname. --- datadog_lambda/config.py | 2 +- datadog_lambda/wrapper.py | 6 ++---- tests/test_config.py | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 9f386cea..4fd1620a 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -33,7 +33,7 @@ class Config: flush_in_thread = _get_env("DD_FLUSH_IN_THREAD", "false", as_bool) flush_to_log = _get_env("DD_FLUSH_TO_LOG", "false", as_bool) logs_injection = _get_env("DD_LOGS_INJECTION", "true", as_bool) - function_name = _get_env("AWS_LAMBDA_FUNCTION_NAME") + function_name = _get_env("AWS_LAMBDA_FUNCTION_NAME", "function") is_gov_region = _get_env("AWS_REGION", "", lambda x: x.startswith("us-gov-")) is_in_tests = _get_env("DD_INTEGRATION_TEST", "false", as_bool) is_lambda_context = _get_env("AWS_LAMBDA_FUNCTION_NAME", None, bool) diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index c5d135c0..caae1ea5 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -64,7 +64,6 @@ logger = logging.getLogger(__name__) DD_LOGS_INJECTION = "DD_LOGS_INJECTION" -AWS_LAMBDA_FUNCTION_NAME = "AWS_LAMBDA_FUNCTION_NAME" DD_LOCAL_TEST = "DD_LOCAL_TEST" DD_TRACE_EXTRACTOR = "DD_TRACE_EXTRACTOR" DD_TRACE_MANAGED_SERVICES = "DD_TRACE_MANAGED_SERVICES" @@ -159,7 +158,6 @@ def __init__(self, func): """Executes when the wrapped function gets wrapped""" try: self.func = func - self.function_name = os.environ.get(AWS_LAMBDA_FUNCTION_NAME, "function") self.service = os.environ.get(DD_SERVICE, None) self.extractor_env = os.environ.get(DD_TRACE_EXTRACTOR, None) self.trace_extractor = None @@ -322,7 +320,7 @@ def _before(self, event, context): set_dsm_context(event, event_source) self.span = create_function_execution_span( context=context, - function_name=self.function_name, + function_name=config.function_name, is_cold_start=is_cold_start(), is_proactive_init=is_proactive_init(), trace_context_source=trace_context_source, @@ -382,7 +380,7 @@ def _after(self, event, context): following_span = self.span or self.inferred_span ColdStartTracer( tracer, - self.function_name, + config.function_name, following_span.start_ns, trace_ctx, self.min_cold_start_trace_duration, diff --git a/tests/test_config.py b/tests/test_config.py index 26dce9dc..39f5fd9a 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -15,7 +15,7 @@ def set_env(key, value): _test_config_from_environ = ( - ("AWS_LAMBDA_FUNCTION_NAME", "function_name", None, None), + ("AWS_LAMBDA_FUNCTION_NAME", "function_name", None, "function"), ("AWS_LAMBDA_FUNCTION_NAME", "function_name", "", ""), ("AWS_LAMBDA_FUNCTION_NAME", "function_name", "my_function", "my_function"), ("DD_FLUSH_TO_LOG", "flush_to_log", None, False), From 2f640efb9f88c09846381c9df6f4436e22015d77 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 11:16:15 -0700 Subject: [PATCH 16/52] Add service. --- datadog_lambda/config.py | 1 + datadog_lambda/wrapper.py | 7 +++---- tests/test_config.py | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 4fd1620a..4acd90f7 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -27,6 +27,7 @@ def as_bool(val): class Config: + service = _get_env("DD_SERVICE") add_span_pointers = _get_env("DD_BOTOCORE_ADD_SPAN_POINTERS", "true", as_bool) cold_start_tracing = _get_env("DD_COLD_START_TRACING", "true", as_bool) enhanced_metrics_enabled = _get_env("DD_ENHANCED_METRICS", "true", as_bool) diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index caae1ea5..d2ec163a 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -158,7 +158,6 @@ def __init__(self, func): """Executes when the wrapped function gets wrapped""" try: self.func = func - self.service = os.environ.get(DD_SERVICE, None) self.extractor_env = os.environ.get(DD_TRACE_EXTRACTOR, None) self.trace_extractor = None self.span = None @@ -200,7 +199,7 @@ def __init__(self, func): logger.debug(f"Malformatted for env {DD_COLD_START_TRACE_SKIP_LIB}") self.response = None if profiling_env_var: - self.prof = profiler.Profiler(env=env_env_var, service=self.service) + self.prof = profiler.Profiler(env=env_env_var, service=config.service) if self.extractor_env: extractor_parts = self.extractor_env.rsplit(".", 1) if len(extractor_parts) == 2: @@ -367,8 +366,8 @@ def _after(self, event, context): if status_code: self.inferred_span.set_tag("http.status_code", status_code) - if self.service: - self.inferred_span.set_tag("peer.service", self.service) + if config.service: + self.inferred_span.set_tag("peer.service", config.service) if InferredSpanInfo.is_async(self.inferred_span) and self.span: self.inferred_span.finish(finish_time=self.span.start) diff --git a/tests/test_config.py b/tests/test_config.py index 39f5fd9a..53262469 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -15,6 +15,9 @@ def set_env(key, value): _test_config_from_environ = ( + ("DD_SERVICE", "service", None, None), + ("DD_SERVICE", "service", "", ""), + ("DD_SERVICE", "service", "my_service", "my_service"), ("AWS_LAMBDA_FUNCTION_NAME", "function_name", None, "function"), ("AWS_LAMBDA_FUNCTION_NAME", "function_name", "", ""), ("AWS_LAMBDA_FUNCTION_NAME", "function_name", "my_function", "my_function"), From 192b9c1ca861d0658e0746189620e00b1a37d5e5 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 11:20:21 -0700 Subject: [PATCH 17/52] Add trace_extractor. --- datadog_lambda/config.py | 1 + datadog_lambda/wrapper.py | 7 ++----- tests/test_config.py | 3 +++ 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 4acd90f7..84651105 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -44,6 +44,7 @@ class Config: ) trace_enabled = _get_env("DD_TRACE_ENABLED", "true", as_bool) merge_xray_traces = _get_env("DD_MERGE_XRAY_TRACES", "false", as_bool) + trace_extractor = _get_env("DD_TRACE_EXTRACTOR") @property def fips_mode_enabled(self): diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index d2ec163a..9241f2be 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -63,9 +63,7 @@ logger = logging.getLogger(__name__) -DD_LOGS_INJECTION = "DD_LOGS_INJECTION" DD_LOCAL_TEST = "DD_LOCAL_TEST" -DD_TRACE_EXTRACTOR = "DD_TRACE_EXTRACTOR" DD_TRACE_MANAGED_SERVICES = "DD_TRACE_MANAGED_SERVICES" DD_ENCODE_AUTHORIZER_CONTEXT = "DD_ENCODE_AUTHORIZER_CONTEXT" DD_DECODE_AUTHORIZER_CONTEXT = "DD_DECODE_AUTHORIZER_CONTEXT" @@ -158,7 +156,6 @@ def __init__(self, func): """Executes when the wrapped function gets wrapped""" try: self.func = func - self.extractor_env = os.environ.get(DD_TRACE_EXTRACTOR, None) self.trace_extractor = None self.span = None self.inferred_span = None @@ -200,8 +197,8 @@ def __init__(self, func): self.response = None if profiling_env_var: self.prof = profiler.Profiler(env=env_env_var, service=config.service) - if self.extractor_env: - extractor_parts = self.extractor_env.rsplit(".", 1) + if config.trace_extractor: + extractor_parts = config.trace_extractor.rsplit(".", 1) if len(extractor_parts) == 2: (mod_name, extractor_name) = extractor_parts modified_extractor_name = modify_module_name(mod_name) diff --git a/tests/test_config.py b/tests/test_config.py index 53262469..c216add1 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -127,6 +127,9 @@ def set_env(key, value): ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", "1", True), # CHANGED ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", "0", False), ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", "purple", False), + ("DD_TRACE_EXTRACTOR", "trace_extractor", None, None), + ("DD_TRACE_EXTRACTOR", "trace_extractor", "", ""), + ("DD_TRACE_EXTRACTOR", "trace_extractor", "my_extractor", "my_extractor"), ) From ddd25c61add64d0238eecd5c939b6ca24667a37e Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 12:04:54 -0700 Subject: [PATCH 18/52] Add capture_payload_max_depth. --- datadog_lambda/config.py | 18 ++++++++++++++++-- datadog_lambda/tag_object.py | 7 +++---- datadog_lambda/wrapper.py | 8 -------- tests/test_config.py | 7 +++++++ tests/test_tag_object.py | 8 ++------ 5 files changed, 28 insertions(+), 20 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 84651105..b7c1ca6c 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -6,6 +6,8 @@ import logging import os +logger = logging.getLogger(__name__) + def _get_env(key, default=None, cast=None): @property @@ -13,7 +15,17 @@ def _getter(self): if not hasattr(self, prop_key): val = os.environ.get(key, default) if cast is not None: - val = cast(val) + try: + val = cast(val) + except (ValueError, TypeError): + logger.warning( + "Failed to cast environment variable '%s' with value '%s' to type %s. Using default value '%s'.", + key, + val, + cast.__name__, + default, + ) + val = default setattr(self, prop_key, val) return getattr(self, prop_key) @@ -45,6 +57,9 @@ class Config: trace_enabled = _get_env("DD_TRACE_ENABLED", "true", as_bool) merge_xray_traces = _get_env("DD_MERGE_XRAY_TRACES", "false", as_bool) trace_extractor = _get_env("DD_TRACE_EXTRACTOR") + capture_payload_max_depth = _get_env( + "DD_CAPTURE_PAYLOAD_MAX_DEPTH", 10, int + ) @property def fips_mode_enabled(self): @@ -67,7 +82,6 @@ def _reset(self): config = Config() if config.is_gov_region or config.fips_mode_enabled: - logger = logging.getLogger(__name__) logger.debug( "Python Lambda Layer FIPS mode is %s.", "enabled" if config.fips_mode_enabled else "not enabled", diff --git a/datadog_lambda/tag_object.py b/datadog_lambda/tag_object.py index 6d82f83b..744e4893 100644 --- a/datadog_lambda/tag_object.py +++ b/datadog_lambda/tag_object.py @@ -4,18 +4,17 @@ # Copyright 2021 Datadog, Inc. from decimal import Decimal -import logging import ujson as json +from datadog_lambda.config import config + redactable_keys = ["authorization", "x-authorization", "password", "token"] -max_depth = 10 -logger = logging.getLogger(__name__) def tag_object(span, key, obj, depth=0): if obj is None: return span.set_tag(key, obj) - if depth >= max_depth: + if depth >= config.capture_payload_max_depth: return span.set_tag(key, _redact_val(key, str(obj)[0:5000])) depth += 1 if _should_try_string(obj): diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index 9241f2be..b1410427 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -71,7 +71,6 @@ DD_MIN_COLD_START_DURATION = "DD_MIN_COLD_START_DURATION" DD_COLD_START_TRACE_SKIP_LIB = "DD_COLD_START_TRACE_SKIP_LIB" DD_CAPTURE_LAMBDA_PAYLOAD = "DD_CAPTURE_LAMBDA_PAYLOAD" -DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH = "DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH" DD_REQUESTS_SERVICE_NAME = "DD_REQUESTS_SERVICE_NAME" DD_SERVICE = "DD_SERVICE" DD_ENV = "DD_ENV" @@ -92,13 +91,6 @@ def get_env_as_int(env_key, default_value: int) -> int: os.environ.get(DD_CAPTURE_LAMBDA_PAYLOAD, "false").lower() == "true" ) -if dd_capture_lambda_payload_enabled: - import datadog_lambda.tag_object as tag_object - - tag_object.max_depth = get_env_as_int( - DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH, tag_object.max_depth - ) - env_env_var = os.environ.get(DD_ENV, None) init_timestamp_ns = time_ns() diff --git a/tests/test_config.py b/tests/test_config.py index c216add1..10ee4b98 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -130,6 +130,13 @@ def set_env(key, value): ("DD_TRACE_EXTRACTOR", "trace_extractor", None, None), ("DD_TRACE_EXTRACTOR", "trace_extractor", "", ""), ("DD_TRACE_EXTRACTOR", "trace_extractor", "my_extractor", "my_extractor"), + ("DD_CAPTURE_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", None, 10), + ("DD_CAPTURE_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "", 10), + ("DD_CAPTURE_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "5", 5), + ("DD_CAPTURE_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "0", 0), + ("DD_CAPTURE_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "2.5", 10), + ("DD_CAPTURE_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "-1", -1), + ("DD_CAPTURE_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "purple", 10), ) diff --git a/tests/test_tag_object.py b/tests/test_tag_object.py index 77512164..574bb331 100644 --- a/tests/test_tag_object.py +++ b/tests/test_tag_object.py @@ -29,6 +29,7 @@ def test_tag_object(self): True, ) + @patch("datadog_lambda.config.Config.capture_payload_max_depth", 2) def test_tag_object_max_depth(self): payload = { "hello": "world", @@ -41,11 +42,8 @@ def test_tag_object_max_depth(self): "vals": [{"thingOne": 1}, {"thingTwo": 2}], } spanMock = MagicMock() - import datadog_lambda.tag_object as lib_ref - lib_ref.max_depth = 2 # setting up the test tag_object(spanMock, "function.request", payload) - lib_ref.max_depth = 10 # revert the setup spanMock.set_tag.assert_has_calls( [ call("function.request.vals.0", "{'thingOne': 1}"), @@ -62,6 +60,7 @@ def test_tag_object_max_depth(self): True, ) + @patch("datadog_lambda.config.Config.capture_payload_max_depth", 0) def test_tag_object_max_depth_0(self): payload = { "hello": "world", @@ -74,11 +73,8 @@ def test_tag_object_max_depth_0(self): "vals": [{"thingOne": 1}, {"thingTwo": 2}], } spanMock = MagicMock() - import datadog_lambda.tag_object as lib_ref - lib_ref.max_depth = 0 # setting up the test tag_object(spanMock, "function.request", payload) - lib_ref.max_depth = 10 # revert the setup spanMock.set_tag.assert_has_calls( [ call( From 09adc0faf76faef6dbe34dba56b3a41536c80c6e Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 12:05:50 -0700 Subject: [PATCH 19/52] UNblack. --- datadog_lambda/config.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index b7c1ca6c..5f0b8641 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -57,9 +57,7 @@ class Config: trace_enabled = _get_env("DD_TRACE_ENABLED", "true", as_bool) merge_xray_traces = _get_env("DD_MERGE_XRAY_TRACES", "false", as_bool) trace_extractor = _get_env("DD_TRACE_EXTRACTOR") - capture_payload_max_depth = _get_env( - "DD_CAPTURE_PAYLOAD_MAX_DEPTH", 10, int - ) + capture_payload_max_depth = _get_env("DD_CAPTURE_PAYLOAD_MAX_DEPTH", 10, int) @property def fips_mode_enabled(self): From 5c812894fac84b740447f187bf241b0d0fa8072d Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 12:08:25 -0700 Subject: [PATCH 20/52] Add profiling_enabled. --- datadog_lambda/config.py | 1 + datadog_lambda/wrapper.py | 7 +++---- tests/test_config.py | 9 +++++++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 5f0b8641..1489a8a1 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -58,6 +58,7 @@ class Config: merge_xray_traces = _get_env("DD_MERGE_XRAY_TRACES", "false", as_bool) trace_extractor = _get_env("DD_TRACE_EXTRACTOR") capture_payload_max_depth = _get_env("DD_CAPTURE_PAYLOAD_MAX_DEPTH", 10, int) + profiling_enabled = _get_env("DD_PROFILING_ENABLED", "false", as_bool) @property def fips_mode_enabled(self): diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index b1410427..5f665a9b 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -46,8 +46,7 @@ extract_http_status_code_tag, ) -profiling_env_var = os.environ.get("DD_PROFILING_ENABLED", "false").lower() == "true" -if profiling_env_var: +if config.profiling_enabled: from ddtrace.profiling import profiler llmobs_env_var = os.environ.get("DD_LLMOBS_ENABLED", "false").lower() in ("true", "1") @@ -187,7 +186,7 @@ def __init__(self, func): except Exception: logger.debug(f"Malformatted for env {DD_COLD_START_TRACE_SKIP_LIB}") self.response = None - if profiling_env_var: + if config.profiling_enabled: self.prof = profiler.Profiler(env=env_env_var, service=config.service) if config.trace_extractor: extractor_parts = config.trace_extractor.rsplit(".", 1) @@ -319,7 +318,7 @@ def _before(self, event, context): ) else: set_correlation_ids() - if profiling_env_var and is_new_sandbox(): + if config.profiling_enabled and is_new_sandbox(): self.prof.start(stop_on_exit=False, profile_children=True) logger.debug("datadog_lambda_wrapper _before() done") except Exception as e: diff --git a/tests/test_config.py b/tests/test_config.py index 10ee4b98..a1355fe1 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -137,6 +137,15 @@ def set_env(key, value): ("DD_CAPTURE_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "2.5", 10), ("DD_CAPTURE_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "-1", -1), ("DD_CAPTURE_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "purple", 10), + ("DD_PROFILING_ENABLED", "profiling_enabled", None, False), + ("DD_PROFILING_ENABLED", "profiling_enabled", "", False), + ("DD_PROFILING_ENABLED", "profiling_enabled", "true", True), + ("DD_PROFILING_ENABLED", "profiling_enabled", "TRUE", True), + ("DD_PROFILING_ENABLED", "profiling_enabled", "false", False), + ("DD_PROFILING_ENABLED", "profiling_enabled", "FALSE", False), + ("DD_PROFILING_ENABLED", "profiling_enabled", "1", True), # CHANGED + ("DD_PROFILING_ENABLED", "profiling_enabled", "0", False), + ("DD_PROFILING_ENABLED", "profiling_enabled", "purple", False), ) From 63b3d4fe44a9e3fc56a18a6488b97d2d6bc61782 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 12:10:38 -0700 Subject: [PATCH 21/52] Add llmobs_enabled. --- datadog_lambda/config.py | 1 + datadog_lambda/wrapper.py | 7 +++---- tests/test_config.py | 9 +++++++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 1489a8a1..5f7edba5 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -59,6 +59,7 @@ class Config: trace_extractor = _get_env("DD_TRACE_EXTRACTOR") capture_payload_max_depth = _get_env("DD_CAPTURE_PAYLOAD_MAX_DEPTH", 10, int) profiling_enabled = _get_env("DD_PROFILING_ENABLED", "false", as_bool) + llmobs_enabled = _get_env("DD_LLMOBS_ENABLED", "false", as_bool) @property def fips_mode_enabled(self): diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index 5f665a9b..d06a2005 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -49,8 +49,7 @@ if config.profiling_enabled: from ddtrace.profiling import profiler -llmobs_env_var = os.environ.get("DD_LLMOBS_ENABLED", "false").lower() in ("true", "1") -if llmobs_env_var: +if config.llmobs_enabled: from ddtrace.llmobs import LLMObs exception_replay_env_var = os.environ.get( @@ -209,7 +208,7 @@ def __init__(self, func): patch_all() # Enable LLM Observability - if llmobs_env_var: + if config.llmobs_enabled: LLMObs.enable() # Enable Exception Replay @@ -386,7 +385,7 @@ def _after(self, event, context): # logs api flush_extension() - if llmobs_env_var: + if config.llmobs_enabled: LLMObs.flush() # Flush exception replay diff --git a/tests/test_config.py b/tests/test_config.py index a1355fe1..b314af69 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -146,6 +146,15 @@ def set_env(key, value): ("DD_PROFILING_ENABLED", "profiling_enabled", "1", True), # CHANGED ("DD_PROFILING_ENABLED", "profiling_enabled", "0", False), ("DD_PROFILING_ENABLED", "profiling_enabled", "purple", False), + ("DD_LLMOBS_ENABLED", "llmobs_enabled", None, False), + ("DD_LLMOBS_ENABLED", "llmobs_enabled", "", False), + ("DD_LLMOBS_ENABLED", "llmobs_enabled", "true", True), + ("DD_LLMOBS_ENABLED", "llmobs_enabled", "TRUE", True), + ("DD_LLMOBS_ENABLED", "llmobs_enabled", "false", False), + ("DD_LLMOBS_ENABLED", "llmobs_enabled", "FALSE", False), + ("DD_LLMOBS_ENABLED", "llmobs_enabled", "1", True), # CHANGED + ("DD_LLMOBS_ENABLED", "llmobs_enabled", "0", False), + ("DD_LLMOBS_ENABLED", "llmobs_enabled", "purple", False), ) From 62378bcb9e6c6187f846a802a381eeb110d7e6ef Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 12:12:18 -0700 Subject: [PATCH 22/52] Add exception_replay_enabled. --- datadog_lambda/config.py | 1 + datadog_lambda/wrapper.py | 7 ++----- tests/test_config.py | 9 +++++++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 5f7edba5..f4691b66 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -60,6 +60,7 @@ class Config: capture_payload_max_depth = _get_env("DD_CAPTURE_PAYLOAD_MAX_DEPTH", 10, int) profiling_enabled = _get_env("DD_PROFILING_ENABLED", "false", as_bool) llmobs_enabled = _get_env("DD_LLMOBS_ENABLED", "false", as_bool) + exception_replay_enabled = _get_env("DD_EXCEPTION_REPLAY_ENABLED", "false", as_bool) @property def fips_mode_enabled(self): diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index d06a2005..31ec5f8a 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -52,10 +52,7 @@ if config.llmobs_enabled: from ddtrace.llmobs import LLMObs -exception_replay_env_var = os.environ.get( - "DD_EXCEPTION_REPLAY_ENABLED", "false" -).lower() in ("true", "1") -if exception_replay_env_var: +if config.exception_replay_enabled: from ddtrace.debugging._exception.replay import SpanExceptionHandler from ddtrace.debugging._uploader import LogsIntakeUploaderV1 @@ -212,7 +209,7 @@ def __init__(self, func): LLMObs.enable() # Enable Exception Replay - if exception_replay_env_var: + if config.exception_replay_enabled: logger.debug("Enabling exception replay") SpanExceptionHandler.enable() diff --git a/tests/test_config.py b/tests/test_config.py index b314af69..626b8e88 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -155,6 +155,15 @@ def set_env(key, value): ("DD_LLMOBS_ENABLED", "llmobs_enabled", "1", True), # CHANGED ("DD_LLMOBS_ENABLED", "llmobs_enabled", "0", False), ("DD_LLMOBS_ENABLED", "llmobs_enabled", "purple", False), + ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", None, False), + ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", "", False), + ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", "true", True), + ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", "TRUE", True), + ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", "false", False), + ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", "FALSE", False), + ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", "1", True), # CHANGED + ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", "0", False), + ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", "purple", False), ) From 76f5d2dbd2aed37b99350a40f4e810cdbfeed191 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 12:13:57 -0700 Subject: [PATCH 23/52] Add env. --- datadog_lambda/config.py | 2 ++ datadog_lambda/wrapper.py | 4 +--- tests/test_config.py | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index f4691b66..ffa24741 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -40,6 +40,8 @@ def as_bool(val): class Config: service = _get_env("DD_SERVICE") + env = _get_env("DD_ENV") + add_span_pointers = _get_env("DD_BOTOCORE_ADD_SPAN_POINTERS", "true", as_bool) cold_start_tracing = _get_env("DD_COLD_START_TRACING", "true", as_bool) enhanced_metrics_enabled = _get_env("DD_ENHANCED_METRICS", "true", as_bool) diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index 31ec5f8a..28b95690 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -86,8 +86,6 @@ def get_env_as_int(env_key, default_value: int) -> int: os.environ.get(DD_CAPTURE_LAMBDA_PAYLOAD, "false").lower() == "true" ) -env_env_var = os.environ.get(DD_ENV, None) - init_timestamp_ns = time_ns() """ @@ -183,7 +181,7 @@ def __init__(self, func): logger.debug(f"Malformatted for env {DD_COLD_START_TRACE_SKIP_LIB}") self.response = None if config.profiling_enabled: - self.prof = profiler.Profiler(env=env_env_var, service=config.service) + self.prof = profiler.Profiler(env=config.env, service=config.service) if config.trace_extractor: extractor_parts = config.trace_extractor.rsplit(".", 1) if len(extractor_parts) == 2: diff --git a/tests/test_config.py b/tests/test_config.py index 626b8e88..e24ab8b2 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -164,6 +164,9 @@ def set_env(key, value): ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", "1", True), # CHANGED ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", "0", False), ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", "purple", False), + ("DD_ENV", "env", None, None), + ("DD_ENV", "env", "", ""), + ("DD_ENV", "env", "my_env", "my_env"), ) From 47c808afb6333e6d5b6b5893bc28087cd4be5b3e Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 12:19:48 -0700 Subject: [PATCH 24/52] Add capture_payload_enabled. --- datadog_lambda/config.py | 3 ++- datadog_lambda/wrapper.py | 7 +------ tests/test_config.py | 23 ++++++++++++++++------- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index ffa24741..c64f276e 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -59,7 +59,8 @@ class Config: trace_enabled = _get_env("DD_TRACE_ENABLED", "true", as_bool) merge_xray_traces = _get_env("DD_MERGE_XRAY_TRACES", "false", as_bool) trace_extractor = _get_env("DD_TRACE_EXTRACTOR") - capture_payload_max_depth = _get_env("DD_CAPTURE_PAYLOAD_MAX_DEPTH", 10, int) + capture_payload_max_depth = _get_env("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", 10, int) + capture_payload_enabled = _get_env("DD_CAPTURE_LAMBDA_PAYLOAD", "false", as_bool) profiling_enabled = _get_env("DD_PROFILING_ENABLED", "false", as_bool) llmobs_enabled = _get_env("DD_LLMOBS_ENABLED", "false", as_bool) exception_replay_enabled = _get_env("DD_EXCEPTION_REPLAY_ENABLED", "false", as_bool) diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index 28b95690..f1dd2783 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -65,7 +65,6 @@ DD_COLD_START_TRACING = "DD_COLD_START_TRACING" DD_MIN_COLD_START_DURATION = "DD_MIN_COLD_START_DURATION" DD_COLD_START_TRACE_SKIP_LIB = "DD_COLD_START_TRACE_SKIP_LIB" -DD_CAPTURE_LAMBDA_PAYLOAD = "DD_CAPTURE_LAMBDA_PAYLOAD" DD_REQUESTS_SERVICE_NAME = "DD_REQUESTS_SERVICE_NAME" DD_SERVICE = "DD_SERVICE" DD_ENV = "DD_ENV" @@ -82,10 +81,6 @@ def get_env_as_int(env_key, default_value: int) -> int: return default_value -dd_capture_lambda_payload_enabled = ( - os.environ.get(DD_CAPTURE_LAMBDA_PAYLOAD, "false").lower() == "true" -) - init_timestamp_ns = time_ns() """ @@ -336,7 +331,7 @@ def _after(self, event, context): trace_ctx = tracer.current_trace_context() if self.span: - if dd_capture_lambda_payload_enabled: + if config.capture_payload_enabled: tag_object.tag_object(self.span, "function.request", event) tag_object.tag_object(self.span, "function.response", self.response) diff --git a/tests/test_config.py b/tests/test_config.py index e24ab8b2..38fc8392 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -130,13 +130,13 @@ def set_env(key, value): ("DD_TRACE_EXTRACTOR", "trace_extractor", None, None), ("DD_TRACE_EXTRACTOR", "trace_extractor", "", ""), ("DD_TRACE_EXTRACTOR", "trace_extractor", "my_extractor", "my_extractor"), - ("DD_CAPTURE_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", None, 10), - ("DD_CAPTURE_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "", 10), - ("DD_CAPTURE_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "5", 5), - ("DD_CAPTURE_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "0", 0), - ("DD_CAPTURE_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "2.5", 10), - ("DD_CAPTURE_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "-1", -1), - ("DD_CAPTURE_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "purple", 10), + ("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", None, 10), + ("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "", 10), + ("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "5", 5), + ("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "0", 0), + ("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "2.5", 10), + ("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "-1", -1), + ("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "purple", 10), ("DD_PROFILING_ENABLED", "profiling_enabled", None, False), ("DD_PROFILING_ENABLED", "profiling_enabled", "", False), ("DD_PROFILING_ENABLED", "profiling_enabled", "true", True), @@ -167,6 +167,15 @@ def set_env(key, value): ("DD_ENV", "env", None, None), ("DD_ENV", "env", "", ""), ("DD_ENV", "env", "my_env", "my_env"), + ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", None, False), + ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", "", False), + ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", "true", True), + ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", "TRUE", True), + ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", "false", False), + ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", "FALSE", False), + ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", "1", True), # CHANGED + ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", "0", False), + ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", "purple", False), ) From 0e35ee1baa9d62015c45d52285aeff6a10409a68 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 12:22:27 -0700 Subject: [PATCH 25/52] Add min_cold_start_trace_duration. --- datadog_lambda/config.py | 9 ++++++--- datadog_lambda/wrapper.py | 3 +-- tests/test_config.py | 7 +++++++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index c64f276e..718235a2 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -42,8 +42,13 @@ class Config: service = _get_env("DD_SERVICE") env = _get_env("DD_ENV") - add_span_pointers = _get_env("DD_BOTOCORE_ADD_SPAN_POINTERS", "true", as_bool) cold_start_tracing = _get_env("DD_COLD_START_TRACING", "true", as_bool) + min_cold_start_trace_duration = _get_env("DD_MIN_COLD_START_DURATION", 3, int) + + capture_payload_max_depth = _get_env("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", 10, int) + capture_payload_enabled = _get_env("DD_CAPTURE_LAMBDA_PAYLOAD", "false", as_bool) + + add_span_pointers = _get_env("DD_BOTOCORE_ADD_SPAN_POINTERS", "true", as_bool) enhanced_metrics_enabled = _get_env("DD_ENHANCED_METRICS", "true", as_bool) flush_in_thread = _get_env("DD_FLUSH_IN_THREAD", "false", as_bool) flush_to_log = _get_env("DD_FLUSH_TO_LOG", "false", as_bool) @@ -59,8 +64,6 @@ class Config: trace_enabled = _get_env("DD_TRACE_ENABLED", "true", as_bool) merge_xray_traces = _get_env("DD_MERGE_XRAY_TRACES", "false", as_bool) trace_extractor = _get_env("DD_TRACE_EXTRACTOR") - capture_payload_max_depth = _get_env("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", 10, int) - capture_payload_enabled = _get_env("DD_CAPTURE_LAMBDA_PAYLOAD", "false", as_bool) profiling_enabled = _get_env("DD_PROFILING_ENABLED", "false", as_bool) llmobs_enabled = _get_env("DD_LLMOBS_ENABLED", "false", as_bool) exception_replay_enabled = _get_env("DD_EXCEPTION_REPLAY_ENABLED", "false", as_bool) diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index f1dd2783..ddbe5f86 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -63,7 +63,6 @@ DD_ENCODE_AUTHORIZER_CONTEXT = "DD_ENCODE_AUTHORIZER_CONTEXT" DD_DECODE_AUTHORIZER_CONTEXT = "DD_DECODE_AUTHORIZER_CONTEXT" DD_COLD_START_TRACING = "DD_COLD_START_TRACING" -DD_MIN_COLD_START_DURATION = "DD_MIN_COLD_START_DURATION" DD_COLD_START_TRACE_SKIP_LIB = "DD_COLD_START_TRACE_SKIP_LIB" DD_REQUESTS_SERVICE_NAME = "DD_REQUESTS_SERVICE_NAME" DD_SERVICE = "DD_SERVICE" @@ -359,7 +358,7 @@ def _after(self, event, context): config.function_name, following_span.start_ns, trace_ctx, - self.min_cold_start_trace_duration, + config.min_cold_start_trace_duration, self.cold_start_trace_skip_lib, ).trace() except Exception as e: diff --git a/tests/test_config.py b/tests/test_config.py index 38fc8392..ed6ff056 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -176,6 +176,13 @@ def set_env(key, value): ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", "1", True), # CHANGED ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", "0", False), ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", "purple", False), + ("DD_MIN_COLD_START_DURATION", "min_cold_start_trace_duration", None, 3), + ("DD_MIN_COLD_START_DURATION", "min_cold_start_trace_duration", "", 3), + ("DD_MIN_COLD_START_DURATION", "min_cold_start_trace_duration", "5", 5), + ("DD_MIN_COLD_START_DURATION", "min_cold_start_trace_duration", "0", 0), + ("DD_MIN_COLD_START_DURATION", "min_cold_start_trace_duration", "2.5", 3), + ("DD_MIN_COLD_START_DURATION", "min_cold_start_trace_duration", "-1", -1), + ("DD_MIN_COLD_START_DURATION", "min_cold_start_trace_duration", "purple", 3), ) From 863fa9e1a79e9ece060e1a911283cb4a1de0c33a Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 12:24:19 -0700 Subject: [PATCH 26/52] Add local_test. --- datadog_lambda/config.py | 2 ++ datadog_lambda/wrapper.py | 16 +--------------- tests/test_config.py | 9 +++++++++ 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 718235a2..4cc3215c 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -68,6 +68,8 @@ class Config: llmobs_enabled = _get_env("DD_LLMOBS_ENABLED", "false", as_bool) exception_replay_enabled = _get_env("DD_EXCEPTION_REPLAY_ENABLED", "false", as_bool) + local_test = _get_env("DD_LOCAL_TEST", "false", as_bool) + @property def fips_mode_enabled(self): if not hasattr(self, "_config_fips_mode_enabled"): diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index ddbe5f86..476d5bab 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -58,7 +58,6 @@ logger = logging.getLogger(__name__) -DD_LOCAL_TEST = "DD_LOCAL_TEST" DD_TRACE_MANAGED_SERVICES = "DD_TRACE_MANAGED_SERVICES" DD_ENCODE_AUTHORIZER_CONTEXT = "DD_ENCODE_AUTHORIZER_CONTEXT" DD_DECODE_AUTHORIZER_CONTEXT = "DD_DECODE_AUTHORIZER_CONTEXT" @@ -70,16 +69,6 @@ DD_DATA_STREAMS_ENABLED = "DD_DATA_STREAMS_ENABLED" -def get_env_as_int(env_key, default_value: int) -> int: - try: - return int(os.environ.get(env_key, default_value)) - except Exception as e: - logger.warn( - f"Failed to parse {env_key} as int. Using default value: {default_value}. Error: {e}" - ) - return default_value - - init_timestamp_ns = time_ns() """ @@ -153,9 +142,6 @@ def __init__(self, func): self.cold_start_tracing = depends_on_dd_tracing_enabled( os.environ.get(DD_COLD_START_TRACING, "true").lower() == "true" ) - self.min_cold_start_trace_duration = get_env_as_int( - DD_MIN_COLD_START_DURATION, 3 - ) self.data_streams_enabled = ( os.environ.get(DD_DATA_STREAMS_ENABLED, "false").lower() == "true" ) @@ -368,7 +354,7 @@ def _after(self, event, context): from datadog_lambda.metric import flush_stats flush_stats(context) - if should_use_extension and self.local_testing_mode: + if should_use_extension and config.local_test: # when testing locally, the extension does not know when an # invocation completes because it does not have access to the # logs api diff --git a/tests/test_config.py b/tests/test_config.py index ed6ff056..ecf4dee1 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -183,6 +183,15 @@ def set_env(key, value): ("DD_MIN_COLD_START_DURATION", "min_cold_start_trace_duration", "2.5", 3), ("DD_MIN_COLD_START_DURATION", "min_cold_start_trace_duration", "-1", -1), ("DD_MIN_COLD_START_DURATION", "min_cold_start_trace_duration", "purple", 3), + ("DD_LOCAL_TEST", "local_test", None, False), + ("DD_LOCAL_TEST", "local_test", "", False), + ("DD_LOCAL_TEST", "local_test", "true", True), + ("DD_LOCAL_TEST", "local_test", "TRUE", True), + ("DD_LOCAL_TEST", "local_test", "false", False), + ("DD_LOCAL_TEST", "local_test", "FALSE", False), + ("DD_LOCAL_TEST", "local_test", "1", True), # CHANGED + ("DD_LOCAL_TEST", "local_test", "0", False), + ("DD_LOCAL_TEST", "local_test", "purple", False), ) From 321024039a82dd25c01458a37cd31af44cca9581 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 12:36:51 -0700 Subject: [PATCH 27/52] Add cold_start_trace_skip_lib. --- datadog_lambda/config.py | 13 +++++++++++ datadog_lambda/wrapper.py | 6 +---- tests/test_config.py | 10 ++++++++ tests/test_wrapper.py | 49 ++++++++++++++++++++------------------- 4 files changed, 49 insertions(+), 29 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 4cc3215c..6611be92 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -37,6 +37,14 @@ def as_bool(val): return val.lower() == "true" or val == "1" +def as_list(val): + if isinstance(val, str): + if not val: + return [] + return [val.strip() for val in val.split(",") if val.strip()] + return val + + class Config: service = _get_env("DD_SERVICE") @@ -44,6 +52,11 @@ class Config: cold_start_tracing = _get_env("DD_COLD_START_TRACING", "true", as_bool) min_cold_start_trace_duration = _get_env("DD_MIN_COLD_START_DURATION", 3, int) + cold_start_trace_skip_lib = _get_env( + "DD_COLD_START_TRACE_SKIP_LIB", [ + "ddtrace.internal.compat", + "ddtrace.filters", + ], as_list) capture_payload_max_depth = _get_env("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", 10, int) capture_payload_enabled = _get_env("DD_CAPTURE_LAMBDA_PAYLOAD", "false", as_bool) diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index 476d5bab..9beeb606 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -62,7 +62,6 @@ DD_ENCODE_AUTHORIZER_CONTEXT = "DD_ENCODE_AUTHORIZER_CONTEXT" DD_DECODE_AUTHORIZER_CONTEXT = "DD_DECODE_AUTHORIZER_CONTEXT" DD_COLD_START_TRACING = "DD_COLD_START_TRACING" -DD_COLD_START_TRACE_SKIP_LIB = "DD_COLD_START_TRACE_SKIP_LIB" DD_REQUESTS_SERVICE_NAME = "DD_REQUESTS_SERVICE_NAME" DD_SERVICE = "DD_SERVICE" DD_ENV = "DD_ENV" @@ -145,9 +144,6 @@ def __init__(self, func): self.data_streams_enabled = ( os.environ.get(DD_DATA_STREAMS_ENABLED, "false").lower() == "true" ) - self.local_testing_mode = os.environ.get( - DD_LOCAL_TEST, "false" - ).lower() in ("true", "1") self.cold_start_trace_skip_lib = [ "ddtrace.internal.compat", "ddtrace.filters", @@ -345,7 +341,7 @@ def _after(self, event, context): following_span.start_ns, trace_ctx, config.min_cold_start_trace_duration, - self.cold_start_trace_skip_lib, + config.cold_start_trace_skip_lib, ).trace() except Exception as e: logger.debug("Failed to create cold start spans. %s", e) diff --git a/tests/test_config.py b/tests/test_config.py index ecf4dee1..8adcc297 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -192,6 +192,16 @@ def set_env(key, value): ("DD_LOCAL_TEST", "local_test", "1", True), # CHANGED ("DD_LOCAL_TEST", "local_test", "0", False), ("DD_LOCAL_TEST", "local_test", "purple", False), + ("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", None, ["ddtrace.internal.compat", "ddtrace.filters"]), + ("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", "", []), + ("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", " ", []), + ("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", ",", []), + ("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", " , ", []), + ("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", "a", ["a"]), + ("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", "a,", ["a"]), + ("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", "a, ", ["a"]), + ("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", "a,b", ["a", "b"]), + ("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", "a, b", ["a", "b"]), ) diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index 76ef5fc3..310b75db 100644 --- a/tests/test_wrapper.py +++ b/tests/test_wrapper.py @@ -701,14 +701,28 @@ def handler(event, context): class TestLambdaWrapperFlushExtension(unittest.TestCase): - def setUp(self): - self.orig_environ = os.environ + @patch("datadog_lambda.config.Config.local_test", True) + @patch("datadog_lambda.wrapper.should_use_extension", True) + def test_local_test_true_flushing(self): + flushes = [] + lambda_event = {} + lambda_context = get_mock_context() - def tearDown(self): - os.environ = self.orig_environ + def flush(): + flushes.append(1) + @patch("datadog_lambda.wrapper.flush_extension", flush) + @wrapper.datadog_lambda_wrapper + def lambda_handler(event, context): + pass + + lambda_handler(lambda_event, lambda_context) + + self.assertEqual(len(flushes), 1) + + @patch("datadog_lambda.config.Config.local_test", False) @patch("datadog_lambda.wrapper.should_use_extension", True) - def test_local_test_envvar_flushing(self): + def test_local_test_false_flushing(self): flushes = [] lambda_event = {} lambda_context = get_mock_context() @@ -716,24 +730,11 @@ def test_local_test_envvar_flushing(self): def flush(): flushes.append(1) - for environ, flush_called in ( - ({"DD_LOCAL_TEST": "True"}, True), - ({"DD_LOCAL_TEST": "true"}, True), - ({"DD_LOCAL_TEST": "1"}, True), - ({"DD_LOCAL_TEST": "False"}, False), - ({"DD_LOCAL_TEST": "false"}, False), - ({"DD_LOCAL_TEST": "0"}, False), - ({"DD_LOCAL_TEST": ""}, False), - ({}, False), - ): - os.environ = environ - flushes.clear() - - @patch("datadog_lambda.wrapper.flush_extension", flush) - @wrapper.datadog_lambda_wrapper - def lambda_handler(event, context): - pass + @patch("datadog_lambda.wrapper.flush_extension", flush) + @wrapper.datadog_lambda_wrapper + def lambda_handler(event, context): + pass - lambda_handler(lambda_event, lambda_context) + lambda_handler(lambda_event, lambda_context) - self.assertEqual(flush_called, len(flushes) == 1) + self.assertEqual(len(flushes), 0) From a1e499308266043d1078ce292b9d9afaa5d7d058 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 12:59:08 -0700 Subject: [PATCH 28/52] Default should always be string. --- datadog_lambda/config.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 6611be92..f10e92c1 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -38,11 +38,7 @@ def as_bool(val): def as_list(val): - if isinstance(val, str): - if not val: - return [] - return [val.strip() for val in val.split(",") if val.strip()] - return val + return [val.strip() for val in val.split(",") if val.strip()] class Config: @@ -53,10 +49,8 @@ class Config: cold_start_tracing = _get_env("DD_COLD_START_TRACING", "true", as_bool) min_cold_start_trace_duration = _get_env("DD_MIN_COLD_START_DURATION", 3, int) cold_start_trace_skip_lib = _get_env( - "DD_COLD_START_TRACE_SKIP_LIB", [ - "ddtrace.internal.compat", - "ddtrace.filters", - ], as_list) + "DD_COLD_START_TRACE_SKIP_LIB", "ddtrace.internal.compat,ddtrace.filters", + as_list) capture_payload_max_depth = _get_env("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", 10, int) capture_payload_enabled = _get_env("DD_CAPTURE_LAMBDA_PAYLOAD", "false", as_bool) From 2dfd8ad26a5dbc0df0ac409d63ef952558bc7599 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 13:24:33 -0700 Subject: [PATCH 29/52] Parametrize the parametrize. --- tests/test_config.py | 234 ++++++++++++------------------------------- 1 file changed, 66 insertions(+), 168 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index 8adcc297..c8a9a51d 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -14,194 +14,92 @@ def set_env(key, value): return set_env +def _test_as_bool(env_key, conf_key, default): + return ( + (env_key, conf_key, None, default), + (env_key, conf_key, "", False), + (env_key, conf_key, "true", True), + (env_key, conf_key, "TRUE", True), + (env_key, conf_key, "false", False), + (env_key, conf_key, "FALSE", False), + (env_key, conf_key, "1", True), # CHANGED + (env_key, conf_key, "0", False), + (env_key, conf_key, "purple", False), + ) + +def _test_int(env_key, conf_key, default): + return ( + (env_key, conf_key, None, default), + (env_key, conf_key, "", default), + (env_key, conf_key, "5", 5), + (env_key, conf_key, "0", 0), + (env_key, conf_key, "2.5", default), + (env_key, conf_key, "-1", -1), + (env_key, conf_key, "purple", default), + ) + +def _test_as_list(env_key, conf_key, default): + return ( + (env_key, conf_key, None, default.split(",")), + (env_key, conf_key, "", []), + (env_key, conf_key, " ", []), + (env_key, conf_key, ",", []), + (env_key, conf_key, " , ", []), + (env_key, conf_key, "a", ["a"]), + (env_key, conf_key, "a,", ["a"]), + (env_key, conf_key, "a, ", ["a"]), + (env_key, conf_key, "a,b", ["a", "b"]), + (env_key, conf_key, "a, b", ["a", "b"]), + ) + + _test_config_from_environ = ( + *_test_as_bool("DD_FLUSH_TO_LOG", "flush_to_log", default=False), + *_test_as_bool("DD_LOGS_INJECTION", "logs_injection", default=True), + *_test_as_bool("DD_TRACE_ENABLED", "trace_enabled", default=True), + *_test_as_bool("DD_COLD_START_TRACING", "cold_start_tracing", default=True), + *_test_as_bool("DD_FLUSH_IN_THREAD", "flush_in_thread", default=False), + *_test_as_bool("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", default=True), + *_test_as_bool("DD_INTEGRATION_TEST", "is_in_tests", default=False), + *_test_as_bool("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", default=True), + *_test_as_bool("DD_TRACE_OTEL_ENABLED", "otel_enabled", default=False), + *_test_as_bool("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", default=False), + *_test_as_bool("DD_MERGE_XRAY_TRACES", "merge_xray_traces", default=False), + *_test_as_bool("DD_PROFILING_ENABLED", "profiling_enabled", default=False), + *_test_as_bool("DD_LLMOBS_ENABLED", "llmobs_enabled", default=False), + *_test_as_bool("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", default=False), + *_test_as_bool("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", default=False), + *_test_as_bool("DD_LOCAL_TEST", "local_test", default=False), + + *_test_int("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", default=10), + *_test_int("DD_MIN_COLD_START_DURATION", "min_cold_start_trace_duration", default=3), + + *_test_as_list("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", default="ddtrace.internal.compat,ddtrace.filters"), + ("DD_SERVICE", "service", None, None), ("DD_SERVICE", "service", "", ""), ("DD_SERVICE", "service", "my_service", "my_service"), + ("AWS_LAMBDA_FUNCTION_NAME", "function_name", None, "function"), ("AWS_LAMBDA_FUNCTION_NAME", "function_name", "", ""), ("AWS_LAMBDA_FUNCTION_NAME", "function_name", "my_function", "my_function"), - ("DD_FLUSH_TO_LOG", "flush_to_log", None, False), - ("DD_FLUSH_TO_LOG", "flush_to_log", "", False), - ("DD_FLUSH_TO_LOG", "flush_to_log", "true", True), - ("DD_FLUSH_TO_LOG", "flush_to_log", "TRUE", True), - ("DD_FLUSH_TO_LOG", "flush_to_log", "false", False), - ("DD_FLUSH_TO_LOG", "flush_to_log", "FALSE", False), - ("DD_FLUSH_TO_LOG", "flush_to_log", "1", True), # CHANGED - ("DD_FLUSH_TO_LOG", "flush_to_log", "0", False), - ("DD_FLUSH_TO_LOG", "flush_to_log", "purple", False), - ("DD_LOGS_INJECTION", "logs_injection", None, True), - ("DD_LOGS_INJECTION", "logs_injection", "", False), - ("DD_LOGS_INJECTION", "logs_injection", "true", True), - ("DD_LOGS_INJECTION", "logs_injection", "TRUE", True), - ("DD_LOGS_INJECTION", "logs_injection", "false", False), - ("DD_LOGS_INJECTION", "logs_injection", "FALSE", False), - ("DD_LOGS_INJECTION", "logs_injection", "1", True), # CHANGED - ("DD_LOGS_INJECTION", "logs_injection", "0", False), - ("DD_LOGS_INJECTION", "logs_injection", "purple", False), - ("DD_TRACE_ENABLED", "trace_enabled", None, True), - ("DD_TRACE_ENABLED", "trace_enabled", "", False), - ("DD_TRACE_ENABLED", "trace_enabled", "true", True), - ("DD_TRACE_ENABLED", "trace_enabled", "TRUE", True), - ("DD_TRACE_ENABLED", "trace_enabled", "false", False), - ("DD_TRACE_ENABLED", "trace_enabled", "FALSE", False), - ("DD_TRACE_ENABLED", "trace_enabled", "1", True), # CHANGED - ("DD_TRACE_ENABLED", "trace_enabled", "0", False), - ("DD_TRACE_ENABLED", "trace_enabled", "purple", False), - ("DD_COLD_START_TRACING", "cold_start_tracing", None, True), - ("DD_COLD_START_TRACING", "cold_start_tracing", "", False), - ("DD_COLD_START_TRACING", "cold_start_tracing", "true", True), - ("DD_COLD_START_TRACING", "cold_start_tracing", "TRUE", True), - ("DD_COLD_START_TRACING", "cold_start_tracing", "false", False), - ("DD_COLD_START_TRACING", "cold_start_tracing", "FALSE", False), - ("DD_COLD_START_TRACING", "cold_start_tracing", "1", True), # CHANGED - ("DD_COLD_START_TRACING", "cold_start_tracing", "0", False), - ("DD_COLD_START_TRACING", "cold_start_tracing", "purple", False), + ("AWS_REGION", "is_gov_region", None, False), ("AWS_REGION", "is_gov_region", "", False), ("AWS_REGION", "is_gov_region", "us-gov-1", True), ("AWS_REGION", "is_gov_region", "us-est-1", False), - ("DD_FLUSH_IN_THREAD", "flush_in_thread", None, False), - ("DD_FLUSH_IN_THREAD", "flush_in_thread", "", False), - ("DD_FLUSH_IN_THREAD", "flush_in_thread", "true", True), - ("DD_FLUSH_IN_THREAD", "flush_in_thread", "TRUE", True), - ("DD_FLUSH_IN_THREAD", "flush_in_thread", "false", False), - ("DD_FLUSH_IN_THREAD", "flush_in_thread", "FALSE", False), - ("DD_FLUSH_IN_THREAD", "flush_in_thread", "1", True), # CHANGED - ("DD_FLUSH_IN_THREAD", "flush_in_thread", "0", False), - ("DD_FLUSH_IN_THREAD", "flush_in_thread", "purple", False), - ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", None, True), - ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "", False), - ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "true", True), - ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "TRUE", True), - ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "false", False), - ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "FALSE", False), - ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "1", True), # CHANGED - ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "0", False), - ("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", "purple", False), - ("DD_INTEGRATION_TEST", "is_in_tests", None, False), - ("DD_INTEGRATION_TEST", "is_in_tests", "", False), - ("DD_INTEGRATION_TEST", "is_in_tests", "true", True), - ("DD_INTEGRATION_TEST", "is_in_tests", "TRUE", True), - ("DD_INTEGRATION_TEST", "is_in_tests", "false", False), - ("DD_INTEGRATION_TEST", "is_in_tests", "FALSE", False), - ("DD_INTEGRATION_TEST", "is_in_tests", "1", True), # CHANGED - ("DD_INTEGRATION_TEST", "is_in_tests", "0", False), - ("DD_INTEGRATION_TEST", "is_in_tests", "purple", False), - ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", None, True), - ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "", False), - ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "true", True), - ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "TRUE", True), - ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "false", False), - ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "FALSE", False), - ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "1", True), - ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "0", False), - ("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", "purple", False), - ("DD_TRACE_OTEL_ENABLED", "otel_enabled", None, False), - ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "", False), - ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "true", True), - ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "TRUE", True), - ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "false", False), - ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "FALSE", False), - ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "1", True), # CHANGED - ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "0", False), - ("DD_TRACE_OTEL_ENABLED", "otel_enabled", "purple", False), + ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", None, False), ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", "", False), ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", "my_function", True), - ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", None, False), - ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "", False), - ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "true", True), - ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "TRUE", True), - ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "false", False), - ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "FALSE", False), - ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "1", True), # CHANGED - ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "0", False), - ("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", "purple", False), - ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", None, False), - ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", "", False), - ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", "true", True), - ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", "TRUE", True), - ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", "false", False), - ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", "FALSE", False), - ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", "1", True), # CHANGED - ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", "0", False), - ("DD_MERGE_XRAY_TRACES", "merge_xray_traces", "purple", False), + ("DD_TRACE_EXTRACTOR", "trace_extractor", None, None), ("DD_TRACE_EXTRACTOR", "trace_extractor", "", ""), ("DD_TRACE_EXTRACTOR", "trace_extractor", "my_extractor", "my_extractor"), - ("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", None, 10), - ("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "", 10), - ("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "5", 5), - ("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "0", 0), - ("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "2.5", 10), - ("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "-1", -1), - ("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", "purple", 10), - ("DD_PROFILING_ENABLED", "profiling_enabled", None, False), - ("DD_PROFILING_ENABLED", "profiling_enabled", "", False), - ("DD_PROFILING_ENABLED", "profiling_enabled", "true", True), - ("DD_PROFILING_ENABLED", "profiling_enabled", "TRUE", True), - ("DD_PROFILING_ENABLED", "profiling_enabled", "false", False), - ("DD_PROFILING_ENABLED", "profiling_enabled", "FALSE", False), - ("DD_PROFILING_ENABLED", "profiling_enabled", "1", True), # CHANGED - ("DD_PROFILING_ENABLED", "profiling_enabled", "0", False), - ("DD_PROFILING_ENABLED", "profiling_enabled", "purple", False), - ("DD_LLMOBS_ENABLED", "llmobs_enabled", None, False), - ("DD_LLMOBS_ENABLED", "llmobs_enabled", "", False), - ("DD_LLMOBS_ENABLED", "llmobs_enabled", "true", True), - ("DD_LLMOBS_ENABLED", "llmobs_enabled", "TRUE", True), - ("DD_LLMOBS_ENABLED", "llmobs_enabled", "false", False), - ("DD_LLMOBS_ENABLED", "llmobs_enabled", "FALSE", False), - ("DD_LLMOBS_ENABLED", "llmobs_enabled", "1", True), # CHANGED - ("DD_LLMOBS_ENABLED", "llmobs_enabled", "0", False), - ("DD_LLMOBS_ENABLED", "llmobs_enabled", "purple", False), - ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", None, False), - ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", "", False), - ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", "true", True), - ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", "TRUE", True), - ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", "false", False), - ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", "FALSE", False), - ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", "1", True), # CHANGED - ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", "0", False), - ("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", "purple", False), + ("DD_ENV", "env", None, None), ("DD_ENV", "env", "", ""), ("DD_ENV", "env", "my_env", "my_env"), - ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", None, False), - ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", "", False), - ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", "true", True), - ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", "TRUE", True), - ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", "false", False), - ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", "FALSE", False), - ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", "1", True), # CHANGED - ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", "0", False), - ("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", "purple", False), - ("DD_MIN_COLD_START_DURATION", "min_cold_start_trace_duration", None, 3), - ("DD_MIN_COLD_START_DURATION", "min_cold_start_trace_duration", "", 3), - ("DD_MIN_COLD_START_DURATION", "min_cold_start_trace_duration", "5", 5), - ("DD_MIN_COLD_START_DURATION", "min_cold_start_trace_duration", "0", 0), - ("DD_MIN_COLD_START_DURATION", "min_cold_start_trace_duration", "2.5", 3), - ("DD_MIN_COLD_START_DURATION", "min_cold_start_trace_duration", "-1", -1), - ("DD_MIN_COLD_START_DURATION", "min_cold_start_trace_duration", "purple", 3), - ("DD_LOCAL_TEST", "local_test", None, False), - ("DD_LOCAL_TEST", "local_test", "", False), - ("DD_LOCAL_TEST", "local_test", "true", True), - ("DD_LOCAL_TEST", "local_test", "TRUE", True), - ("DD_LOCAL_TEST", "local_test", "false", False), - ("DD_LOCAL_TEST", "local_test", "FALSE", False), - ("DD_LOCAL_TEST", "local_test", "1", True), # CHANGED - ("DD_LOCAL_TEST", "local_test", "0", False), - ("DD_LOCAL_TEST", "local_test", "purple", False), - ("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", None, ["ddtrace.internal.compat", "ddtrace.filters"]), - ("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", "", []), - ("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", " ", []), - ("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", ",", []), - ("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", " , ", []), - ("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", "a", ["a"]), - ("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", "a,", ["a"]), - ("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", "a, ", ["a"]), - ("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", "a,b", ["a", "b"]), - ("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", "a, b", ["a", "b"]), ) From adfe3ec0449f22e5120c8f6dac819b143dad7024 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 13:47:51 -0700 Subject: [PATCH 30/52] Add make_inferred_span. --- datadog_lambda/config.py | 34 ++++++++++++++++++++-------------- datadog_lambda/wrapper.py | 5 +---- tests/test_config.py | 15 +++++++++++++++ tests/test_wrapper.py | 4 +--- 4 files changed, 37 insertions(+), 21 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index f10e92c1..f09a94ba 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -9,23 +9,26 @@ logger = logging.getLogger(__name__) -def _get_env(key, default=None, cast=None): +def _get_env(key, default=None, cast=None, depends_on_tracing=False): @property def _getter(self): if not hasattr(self, prop_key): - val = os.environ.get(key, default) - if cast is not None: - try: - val = cast(val) - except (ValueError, TypeError): - logger.warning( - "Failed to cast environment variable '%s' with value '%s' to type %s. Using default value '%s'.", - key, - val, - cast.__name__, - default, - ) - val = default + if depends_on_tracing and not config.trace_enabled: + val = False + else: + val = os.environ.get(key, default) + if cast is not None: + try: + val = cast(val) + except (ValueError, TypeError): + logger.warning( + "Failed to cast environment variable '%s' with value '%s' to type %s. Using default value '%s'.", + key, + val, + cast.__name__, + default, + ) + val = default setattr(self, prop_key, val) return getattr(self, prop_key) @@ -75,6 +78,9 @@ class Config: llmobs_enabled = _get_env("DD_LLMOBS_ENABLED", "false", as_bool) exception_replay_enabled = _get_env("DD_EXCEPTION_REPLAY_ENABLED", "false", as_bool) + make_inferred_span = _get_env("DD_TRACE_MANAGED_SERVICES", "true", as_bool, + depends_on_tracing=True) + local_test = _get_env("DD_LOCAL_TEST", "false", as_bool) @property diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index 9beeb606..fff0668e 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -129,9 +129,6 @@ def __init__(self, func): depends_on_dd_tracing_enabled = ( lambda original_boolean: config.trace_enabled and original_boolean ) - self.make_inferred_span = depends_on_dd_tracing_enabled( - os.environ.get(DD_TRACE_MANAGED_SERVICES, "true").lower() == "true" - ) self.encode_authorizer_context = depends_on_dd_tracing_enabled( os.environ.get(DD_ENCODE_AUTHORIZER_CONTEXT, "true").lower() == "true" ) @@ -269,7 +266,7 @@ def _before(self, event, context): if config.trace_enabled: set_dd_trace_py_root(trace_context_source, config.merge_xray_traces) - if self.make_inferred_span: + if config.make_inferred_span: self.inferred_span = create_inferred_span( event, context, event_source, self.decode_authorizer_context ) diff --git a/tests/test_config.py b/tests/test_config.py index c8a9a51d..a7847bdf 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -109,6 +109,21 @@ def test_config_from_environ(env_key, conf_key, env_val, conf_val, setenv): assert getattr(config, conf_key) == conf_val +_test_config_from_environ_depends_on_tracing = ( + *_test_as_bool("DD_TRACE_MANAGED_SERVICES", "make_inferred_span", default=True), +) + +@pytest.mark.parametrize('env_key,conf_key,env_val,conf_val', _test_config_from_environ_depends_on_tracing) +@pytest.mark.parametrize('trace_enabled', [True, False]) +def test_config_from_environ_depends_on_tracing(env_key, conf_key, env_val, conf_val, setenv, trace_enabled): + setenv(env_key, env_val) + setenv("DD_TRACE_ENABLED", "true" if trace_enabled else "false") + if trace_enabled: + assert getattr(config, conf_key) is conf_val + else: + assert getattr(config, conf_key) is False + + _test_fips_mode_from_environ = ( (None, None, False), (None, "", False), diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index 310b75db..dde4bd5c 100644 --- a/tests/test_wrapper.py +++ b/tests/test_wrapper.py @@ -510,6 +510,7 @@ def lambda_handler(event, context): self.assertEqual(os.environ.get("DD_REQUESTS_SERVICE_NAME"), "myAwesomeService") del os.environ["DD_SERVICE"] + @patch("datadog_lambda.config.Config.make_inferred_span", False) def test_encode_authorizer_span(self): @wrapper.datadog_lambda_wrapper def lambda_handler(event, context): @@ -536,7 +537,6 @@ def lambda_handler(event, context): trace_ctx.sampling_priority = 1 test_span.finish() lambda_handler.inferred_span = test_span - lambda_handler.make_inferred_span = False result = lambda_handler(lambda_event, lambda_context) raw_inject_data = result["context"]["_datadog"] self.assertIsInstance(raw_inject_data, str) @@ -622,11 +622,9 @@ def lambda_handler(event, context): class TestLambdaDecoratorSettings(unittest.TestCase): @patch("datadog_lambda.config.Config.trace_enabled", False) def test_some_envs_should_depend_on_dd_tracing_enabled(self): - os.environ[wrapper.DD_TRACE_MANAGED_SERVICES] = "true" os.environ[wrapper.DD_ENCODE_AUTHORIZER_CONTEXT] = "true" os.environ[wrapper.DD_DECODE_AUTHORIZER_CONTEXT] = "true" decorator = wrapper._LambdaDecorator(func=None) - self.assertFalse(decorator.make_inferred_span) self.assertFalse(decorator.encode_authorizer_context) self.assertFalse(decorator.decode_authorizer_context) From 630826085e3f631bbf4905a71e13ae7a0147dda3 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 13:49:51 -0700 Subject: [PATCH 31/52] Add encode_authorizer_context. --- datadog_lambda/config.py | 2 ++ datadog_lambda/wrapper.py | 6 +----- tests/test_wrapper.py | 2 -- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index f09a94ba..3df70cd9 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -80,6 +80,8 @@ class Config: make_inferred_span = _get_env("DD_TRACE_MANAGED_SERVICES", "true", as_bool, depends_on_tracing=True) + encode_authorizer_context = _get_env("DD_ENCODE_AUTHORIZER_CONTEXT", "true", as_bool, + depends_on_tracing=True) local_test = _get_env("DD_LOCAL_TEST", "false", as_bool) diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index fff0668e..ab78634c 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -59,7 +59,6 @@ logger = logging.getLogger(__name__) DD_TRACE_MANAGED_SERVICES = "DD_TRACE_MANAGED_SERVICES" -DD_ENCODE_AUTHORIZER_CONTEXT = "DD_ENCODE_AUTHORIZER_CONTEXT" DD_DECODE_AUTHORIZER_CONTEXT = "DD_DECODE_AUTHORIZER_CONTEXT" DD_COLD_START_TRACING = "DD_COLD_START_TRACING" DD_REQUESTS_SERVICE_NAME = "DD_REQUESTS_SERVICE_NAME" @@ -129,9 +128,6 @@ def __init__(self, func): depends_on_dd_tracing_enabled = ( lambda original_boolean: config.trace_enabled and original_boolean ) - self.encode_authorizer_context = depends_on_dd_tracing_enabled( - os.environ.get(DD_ENCODE_AUTHORIZER_CONTEXT, "true").lower() == "true" - ) self.decode_authorizer_context = depends_on_dd_tracing_enabled( os.environ.get(DD_DECODE_AUTHORIZER_CONTEXT, "true").lower() == "true" ) @@ -360,7 +356,7 @@ def _after(self, event, context): if exception_replay_env_var: LogsIntakeUploaderV1._instance.periodic() - if self.encode_authorizer_context and is_authorizer_response(self.response): + if config.encode_authorizer_context and is_authorizer_response(self.response): self._inject_authorizer_span_headers( event.get("requestContext", {}).get("requestId") ) diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index dde4bd5c..d3daf26f 100644 --- a/tests/test_wrapper.py +++ b/tests/test_wrapper.py @@ -622,10 +622,8 @@ def lambda_handler(event, context): class TestLambdaDecoratorSettings(unittest.TestCase): @patch("datadog_lambda.config.Config.trace_enabled", False) def test_some_envs_should_depend_on_dd_tracing_enabled(self): - os.environ[wrapper.DD_ENCODE_AUTHORIZER_CONTEXT] = "true" os.environ[wrapper.DD_DECODE_AUTHORIZER_CONTEXT] = "true" decorator = wrapper._LambdaDecorator(func=None) - self.assertFalse(decorator.encode_authorizer_context) self.assertFalse(decorator.decode_authorizer_context) From 9a23c2ab2c511e69041f1b67d0cb2c4a1952a677 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 13:52:14 -0700 Subject: [PATCH 32/52] Add decode_authorizer_context. --- datadog_lambda/config.py | 2 ++ datadog_lambda/wrapper.py | 8 ++------ tests/test_wrapper.py | 8 -------- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 3df70cd9..bb5899d9 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -82,6 +82,8 @@ class Config: depends_on_tracing=True) encode_authorizer_context = _get_env("DD_ENCODE_AUTHORIZER_CONTEXT", "true", as_bool, depends_on_tracing=True) + decode_authorizer_context = _get_env("DD_DECODE_AUTHORIZER_CONTEXT", "true", as_bool, + depends_on_tracing=True) local_test = _get_env("DD_LOCAL_TEST", "false", as_bool) diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index ab78634c..e091b8dc 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -59,7 +59,6 @@ logger = logging.getLogger(__name__) DD_TRACE_MANAGED_SERVICES = "DD_TRACE_MANAGED_SERVICES" -DD_DECODE_AUTHORIZER_CONTEXT = "DD_DECODE_AUTHORIZER_CONTEXT" DD_COLD_START_TRACING = "DD_COLD_START_TRACING" DD_REQUESTS_SERVICE_NAME = "DD_REQUESTS_SERVICE_NAME" DD_SERVICE = "DD_SERVICE" @@ -128,9 +127,6 @@ def __init__(self, func): depends_on_dd_tracing_enabled = ( lambda original_boolean: config.trace_enabled and original_boolean ) - self.decode_authorizer_context = depends_on_dd_tracing_enabled( - os.environ.get(DD_DECODE_AUTHORIZER_CONTEXT, "true").lower() == "true" - ) self.cold_start_tracing = depends_on_dd_tracing_enabled( os.environ.get(DD_COLD_START_TRACING, "true").lower() == "true" ) @@ -246,7 +242,7 @@ def _before(self, event, context): event, context, extractor=self.trace_extractor, - decode_authorizer_context=self.decode_authorizer_context, + decode_authorizer_context=config.decode_authorizer_context, ) self.event_source = event_source # Create a Datadog X-Ray subsegment with the trace context @@ -264,7 +260,7 @@ def _before(self, event, context): set_dd_trace_py_root(trace_context_source, config.merge_xray_traces) if config.make_inferred_span: self.inferred_span = create_inferred_span( - event, context, event_source, self.decode_authorizer_context + event, context, event_source, config.decode_authorizer_context ) if self.data_streams_enabled: set_dsm_context(event, event_source) diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index d3daf26f..ad1fcb71 100644 --- a/tests/test_wrapper.py +++ b/tests/test_wrapper.py @@ -619,14 +619,6 @@ def lambda_handler(event, context): del os.environ["DD_DATA_STREAMS_ENABLED"] -class TestLambdaDecoratorSettings(unittest.TestCase): - @patch("datadog_lambda.config.Config.trace_enabled", False) - def test_some_envs_should_depend_on_dd_tracing_enabled(self): - os.environ[wrapper.DD_DECODE_AUTHORIZER_CONTEXT] = "true" - decorator = wrapper._LambdaDecorator(func=None) - self.assertFalse(decorator.decode_authorizer_context) - - class TestLambdaWrapperWithTraceContext(unittest.TestCase): xray_root = "1-5e272390-8c398be037738dc042009320" xray_parent = "94ae789b969f1cc5" From 831f368a26204eb0a6b0983200c511ef14ee2089 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 13:56:09 -0700 Subject: [PATCH 33/52] Use config.cold_start_tracing. --- datadog_lambda/config.py | 2 +- datadog_lambda/wrapper.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index bb5899d9..89293a5f 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -49,7 +49,7 @@ class Config: service = _get_env("DD_SERVICE") env = _get_env("DD_ENV") - cold_start_tracing = _get_env("DD_COLD_START_TRACING", "true", as_bool) + cold_start_tracing = _get_env("DD_COLD_START_TRACING", "true", as_bool, depends_on_tracing=True) min_cold_start_trace_duration = _get_env("DD_MIN_COLD_START_DURATION", 3, int) cold_start_trace_skip_lib = _get_env( "DD_COLD_START_TRACE_SKIP_LIB", "ddtrace.internal.compat,ddtrace.filters", diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index e091b8dc..e80c6c41 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -145,8 +145,10 @@ def __init__(self, func): except Exception: logger.debug(f"Malformatted for env {DD_COLD_START_TRACE_SKIP_LIB}") self.response = None + if config.profiling_enabled: self.prof = profiler.Profiler(env=config.env, service=config.service) + if config.trace_extractor: extractor_parts = config.trace_extractor.rsplit(".", 1) if len(extractor_parts) == 2: @@ -296,7 +298,7 @@ def _after(self, event, context): create_dd_dummy_metadata_subsegment( self.trigger_tags, XraySubsegment.LAMBDA_FUNCTION_TAGS_KEY ) - should_trace_cold_start = self.cold_start_tracing and is_new_sandbox() + should_trace_cold_start = config.cold_start_tracing and is_new_sandbox() if should_trace_cold_start: trace_ctx = tracer.current_trace_context() From 51eb5a7c050f230818118ee6474cdbf87f3b64f8 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 13:59:10 -0700 Subject: [PATCH 34/52] Rm unused. --- datadog_lambda/wrapper.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index e80c6c41..160371da 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -58,11 +58,8 @@ logger = logging.getLogger(__name__) -DD_TRACE_MANAGED_SERVICES = "DD_TRACE_MANAGED_SERVICES" -DD_COLD_START_TRACING = "DD_COLD_START_TRACING" DD_REQUESTS_SERVICE_NAME = "DD_REQUESTS_SERVICE_NAME" DD_SERVICE = "DD_SERVICE" -DD_ENV = "DD_ENV" DD_DATA_STREAMS_ENABLED = "DD_DATA_STREAMS_ENABLED" From c84b698a20c8c5d845791d2bf817dc837ed76959 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 13:59:53 -0700 Subject: [PATCH 35/52] Black. --- datadog_lambda/config.py | 25 +++++++++++++-------- datadog_lambda/wrapper.py | 4 +++- tests/test_config.py | 47 +++++++++++++++++++++++++-------------- 3 files changed, 49 insertions(+), 27 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 89293a5f..82595e8b 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -49,11 +49,15 @@ class Config: service = _get_env("DD_SERVICE") env = _get_env("DD_ENV") - cold_start_tracing = _get_env("DD_COLD_START_TRACING", "true", as_bool, depends_on_tracing=True) + cold_start_tracing = _get_env( + "DD_COLD_START_TRACING", "true", as_bool, depends_on_tracing=True + ) min_cold_start_trace_duration = _get_env("DD_MIN_COLD_START_DURATION", 3, int) cold_start_trace_skip_lib = _get_env( - "DD_COLD_START_TRACE_SKIP_LIB", "ddtrace.internal.compat,ddtrace.filters", - as_list) + "DD_COLD_START_TRACE_SKIP_LIB", + "ddtrace.internal.compat,ddtrace.filters", + as_list, + ) capture_payload_max_depth = _get_env("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", 10, int) capture_payload_enabled = _get_env("DD_CAPTURE_LAMBDA_PAYLOAD", "false", as_bool) @@ -78,12 +82,15 @@ class Config: llmobs_enabled = _get_env("DD_LLMOBS_ENABLED", "false", as_bool) exception_replay_enabled = _get_env("DD_EXCEPTION_REPLAY_ENABLED", "false", as_bool) - make_inferred_span = _get_env("DD_TRACE_MANAGED_SERVICES", "true", as_bool, - depends_on_tracing=True) - encode_authorizer_context = _get_env("DD_ENCODE_AUTHORIZER_CONTEXT", "true", as_bool, - depends_on_tracing=True) - decode_authorizer_context = _get_env("DD_DECODE_AUTHORIZER_CONTEXT", "true", as_bool, - depends_on_tracing=True) + make_inferred_span = _get_env( + "DD_TRACE_MANAGED_SERVICES", "true", as_bool, depends_on_tracing=True + ) + encode_authorizer_context = _get_env( + "DD_ENCODE_AUTHORIZER_CONTEXT", "true", as_bool, depends_on_tracing=True + ) + decode_authorizer_context = _get_env( + "DD_DECODE_AUTHORIZER_CONTEXT", "true", as_bool, depends_on_tracing=True + ) local_test = _get_env("DD_LOCAL_TEST", "false", as_bool) diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index 160371da..53491a8d 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -351,7 +351,9 @@ def _after(self, event, context): if exception_replay_env_var: LogsIntakeUploaderV1._instance.periodic() - if config.encode_authorizer_context and is_authorizer_response(self.response): + if config.encode_authorizer_context and is_authorizer_response( + self.response + ): self._inject_authorizer_span_headers( event.get("requestContext", {}).get("requestId") ) diff --git a/tests/test_config.py b/tests/test_config.py index a7847bdf..e13d5c97 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -27,6 +27,7 @@ def _test_as_bool(env_key, conf_key, default): (env_key, conf_key, "purple", False), ) + def _test_int(env_key, conf_key, default): return ( (env_key, conf_key, None, default), @@ -38,6 +39,7 @@ def _test_int(env_key, conf_key, default): (env_key, conf_key, "purple", default), ) + def _test_as_list(env_key, conf_key, default): return ( (env_key, conf_key, None, default.split(",")), @@ -63,40 +65,46 @@ def _test_as_list(env_key, conf_key, default): *_test_as_bool("DD_INTEGRATION_TEST", "is_in_tests", default=False), *_test_as_bool("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", default=True), *_test_as_bool("DD_TRACE_OTEL_ENABLED", "otel_enabled", default=False), - *_test_as_bool("DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", default=False), + *_test_as_bool( + "DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", default=False + ), *_test_as_bool("DD_MERGE_XRAY_TRACES", "merge_xray_traces", default=False), *_test_as_bool("DD_PROFILING_ENABLED", "profiling_enabled", default=False), *_test_as_bool("DD_LLMOBS_ENABLED", "llmobs_enabled", default=False), - *_test_as_bool("DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", default=False), - *_test_as_bool("DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", default=False), + *_test_as_bool( + "DD_EXCEPTION_REPLAY_ENABLED", "exception_replay_enabled", default=False + ), + *_test_as_bool( + "DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", default=False + ), *_test_as_bool("DD_LOCAL_TEST", "local_test", default=False), - - *_test_int("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", default=10), - *_test_int("DD_MIN_COLD_START_DURATION", "min_cold_start_trace_duration", default=3), - - *_test_as_list("DD_COLD_START_TRACE_SKIP_LIB", "cold_start_trace_skip_lib", default="ddtrace.internal.compat,ddtrace.filters"), - + *_test_int( + "DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", default=10 + ), + *_test_int( + "DD_MIN_COLD_START_DURATION", "min_cold_start_trace_duration", default=3 + ), + *_test_as_list( + "DD_COLD_START_TRACE_SKIP_LIB", + "cold_start_trace_skip_lib", + default="ddtrace.internal.compat,ddtrace.filters", + ), ("DD_SERVICE", "service", None, None), ("DD_SERVICE", "service", "", ""), ("DD_SERVICE", "service", "my_service", "my_service"), - ("AWS_LAMBDA_FUNCTION_NAME", "function_name", None, "function"), ("AWS_LAMBDA_FUNCTION_NAME", "function_name", "", ""), ("AWS_LAMBDA_FUNCTION_NAME", "function_name", "my_function", "my_function"), - ("AWS_REGION", "is_gov_region", None, False), ("AWS_REGION", "is_gov_region", "", False), ("AWS_REGION", "is_gov_region", "us-gov-1", True), ("AWS_REGION", "is_gov_region", "us-est-1", False), - ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", None, False), ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", "", False), ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", "my_function", True), - ("DD_TRACE_EXTRACTOR", "trace_extractor", None, None), ("DD_TRACE_EXTRACTOR", "trace_extractor", "", ""), ("DD_TRACE_EXTRACTOR", "trace_extractor", "my_extractor", "my_extractor"), - ("DD_ENV", "env", None, None), ("DD_ENV", "env", "", ""), ("DD_ENV", "env", "my_env", "my_env"), @@ -113,9 +121,14 @@ def test_config_from_environ(env_key, conf_key, env_val, conf_val, setenv): *_test_as_bool("DD_TRACE_MANAGED_SERVICES", "make_inferred_span", default=True), ) -@pytest.mark.parametrize('env_key,conf_key,env_val,conf_val', _test_config_from_environ_depends_on_tracing) -@pytest.mark.parametrize('trace_enabled', [True, False]) -def test_config_from_environ_depends_on_tracing(env_key, conf_key, env_val, conf_val, setenv, trace_enabled): + +@pytest.mark.parametrize( + "env_key,conf_key,env_val,conf_val", _test_config_from_environ_depends_on_tracing +) +@pytest.mark.parametrize("trace_enabled", [True, False]) +def test_config_from_environ_depends_on_tracing( + env_key, conf_key, env_val, conf_val, setenv, trace_enabled +): setenv(env_key, env_val) setenv("DD_TRACE_ENABLED", "true" if trace_enabled else "false") if trace_enabled: From e405282936ca9ee9edfe72a305be9da4d708bd11 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 14:03:04 -0700 Subject: [PATCH 36/52] Rename. --- datadog_lambda/config.py | 2 +- datadog_lambda/patch.py | 2 +- tests/test_config.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 82595e8b..2c018963 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -69,7 +69,7 @@ class Config: logs_injection = _get_env("DD_LOGS_INJECTION", "true", as_bool) function_name = _get_env("AWS_LAMBDA_FUNCTION_NAME", "function") is_gov_region = _get_env("AWS_REGION", "", lambda x: x.startswith("us-gov-")) - is_in_tests = _get_env("DD_INTEGRATION_TEST", "false", as_bool) + integration_test = _get_env("DD_INTEGRATION_TEST", "false", as_bool) is_lambda_context = _get_env("AWS_LAMBDA_FUNCTION_NAME", None, bool) otel_enabled = _get_env("DD_TRACE_OTEL_ENABLED", "false", as_bool) telemetry_enabled = _get_env( diff --git a/datadog_lambda/patch.py b/datadog_lambda/patch.py index 11b25c65..6d2af0dc 100644 --- a/datadog_lambda/patch.py +++ b/datadog_lambda/patch.py @@ -41,7 +41,7 @@ def _patch_for_integration_tests(): Patch `requests` to log the outgoing requests for integration tests. """ global _integration_tests_patched - if not _integration_tests_patched and config.is_in_tests: + if not _integration_tests_patched and config.integration_test: wrap("requests", "Session.send", _log_request) _integration_tests_patched = True diff --git a/tests/test_config.py b/tests/test_config.py index e13d5c97..87278cfd 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -62,7 +62,7 @@ def _test_as_list(env_key, conf_key, default): *_test_as_bool("DD_COLD_START_TRACING", "cold_start_tracing", default=True), *_test_as_bool("DD_FLUSH_IN_THREAD", "flush_in_thread", default=False), *_test_as_bool("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", default=True), - *_test_as_bool("DD_INTEGRATION_TEST", "is_in_tests", default=False), + *_test_as_bool("DD_INTEGRATION_TEST", "integration_test", default=False), *_test_as_bool("DD_BOTOCORE_ADD_SPAN_POINTERS", "add_span_pointers", default=True), *_test_as_bool("DD_TRACE_OTEL_ENABLED", "otel_enabled", default=False), *_test_as_bool( From 2b2148c1a2e990a1a284adb0d19a0ba632e93bf2 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 14:05:22 -0700 Subject: [PATCH 37/52] reorder. --- datadog_lambda/config.py | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 2c018963..203e97fd 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -49,6 +49,10 @@ class Config: service = _get_env("DD_SERVICE") env = _get_env("DD_ENV") + function_name = _get_env("AWS_LAMBDA_FUNCTION_NAME", "function") + is_gov_region = _get_env("AWS_REGION", "", lambda x: x.startswith("us-gov-")) + is_lambda_context = _get_env("AWS_LAMBDA_FUNCTION_NAME", None, bool) + cold_start_tracing = _get_env( "DD_COLD_START_TRACING", "true", as_bool, depends_on_tracing=True ) @@ -62,37 +66,36 @@ class Config: capture_payload_max_depth = _get_env("DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", 10, int) capture_payload_enabled = _get_env("DD_CAPTURE_LAMBDA_PAYLOAD", "false", as_bool) + trace_enabled = _get_env("DD_TRACE_ENABLED", "true", as_bool) + make_inferred_span = _get_env( + "DD_TRACE_MANAGED_SERVICES", "true", as_bool, depends_on_tracing=True + ) + encode_authorizer_context = _get_env( + "DD_ENCODE_AUTHORIZER_CONTEXT", "true", as_bool, depends_on_tracing=True + ) + decode_authorizer_context = _get_env( + "DD_DECODE_AUTHORIZER_CONTEXT", "true", as_bool, depends_on_tracing=True + ) add_span_pointers = _get_env("DD_BOTOCORE_ADD_SPAN_POINTERS", "true", as_bool) + trace_extractor = _get_env("DD_TRACE_EXTRACTOR") + enhanced_metrics_enabled = _get_env("DD_ENHANCED_METRICS", "true", as_bool) + flush_in_thread = _get_env("DD_FLUSH_IN_THREAD", "false", as_bool) flush_to_log = _get_env("DD_FLUSH_TO_LOG", "false", as_bool) logs_injection = _get_env("DD_LOGS_INJECTION", "true", as_bool) - function_name = _get_env("AWS_LAMBDA_FUNCTION_NAME", "function") - is_gov_region = _get_env("AWS_REGION", "", lambda x: x.startswith("us-gov-")) - integration_test = _get_env("DD_INTEGRATION_TEST", "false", as_bool) - is_lambda_context = _get_env("AWS_LAMBDA_FUNCTION_NAME", None, bool) - otel_enabled = _get_env("DD_TRACE_OTEL_ENABLED", "false", as_bool) + merge_xray_traces = _get_env("DD_MERGE_XRAY_TRACES", "false", as_bool) + telemetry_enabled = _get_env( "DD_INSTRUMENTATION_TELEMETRY_ENABLED", "false", as_bool ) - trace_enabled = _get_env("DD_TRACE_ENABLED", "true", as_bool) - merge_xray_traces = _get_env("DD_MERGE_XRAY_TRACES", "false", as_bool) - trace_extractor = _get_env("DD_TRACE_EXTRACTOR") + otel_enabled = _get_env("DD_TRACE_OTEL_ENABLED", "false", as_bool) profiling_enabled = _get_env("DD_PROFILING_ENABLED", "false", as_bool) llmobs_enabled = _get_env("DD_LLMOBS_ENABLED", "false", as_bool) exception_replay_enabled = _get_env("DD_EXCEPTION_REPLAY_ENABLED", "false", as_bool) - make_inferred_span = _get_env( - "DD_TRACE_MANAGED_SERVICES", "true", as_bool, depends_on_tracing=True - ) - encode_authorizer_context = _get_env( - "DD_ENCODE_AUTHORIZER_CONTEXT", "true", as_bool, depends_on_tracing=True - ) - decode_authorizer_context = _get_env( - "DD_DECODE_AUTHORIZER_CONTEXT", "true", as_bool, depends_on_tracing=True - ) - local_test = _get_env("DD_LOCAL_TEST", "false", as_bool) + integration_test = _get_env("DD_INTEGRATION_TEST", "false", as_bool) @property def fips_mode_enabled(self): From 740be3f9bbce1ae0921fba7415bca4ca1e0916d3 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 14:10:01 -0700 Subject: [PATCH 38/52] Clean up depends on tests. --- tests/test_config.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index 87278cfd..59e2cb6e 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -60,6 +60,9 @@ def _test_as_list(env_key, conf_key, default): *_test_as_bool("DD_LOGS_INJECTION", "logs_injection", default=True), *_test_as_bool("DD_TRACE_ENABLED", "trace_enabled", default=True), *_test_as_bool("DD_COLD_START_TRACING", "cold_start_tracing", default=True), + *_test_as_bool("DD_TRACE_MANAGED_SERVICES", "make_inferred_span", default=True), + *_test_as_bool("DD_ENCODE_AUTHORIZER_CONTEXT", "encode_authorizer_context", default=True), + *_test_as_bool("DD_DECODE_AUTHORIZER_CONTEXT", "decode_authorizer_context", default=True), *_test_as_bool("DD_FLUSH_IN_THREAD", "flush_in_thread", default=False), *_test_as_bool("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", default=True), *_test_as_bool("DD_INTEGRATION_TEST", "integration_test", default=False), @@ -118,23 +121,20 @@ def test_config_from_environ(env_key, conf_key, env_val, conf_val, setenv): _test_config_from_environ_depends_on_tracing = ( + *_test_as_bool("DD_COLD_START_TRACING", "cold_start_tracing", default=True), *_test_as_bool("DD_TRACE_MANAGED_SERVICES", "make_inferred_span", default=True), + *_test_as_bool("DD_ENCODE_AUTHORIZER_CONTEXT", "encode_authorizer_context", default=True), + *_test_as_bool("DD_DECODE_AUTHORIZER_CONTEXT", "decode_authorizer_context", default=True), ) @pytest.mark.parametrize( "env_key,conf_key,env_val,conf_val", _test_config_from_environ_depends_on_tracing ) -@pytest.mark.parametrize("trace_enabled", [True, False]) -def test_config_from_environ_depends_on_tracing( - env_key, conf_key, env_val, conf_val, setenv, trace_enabled -): +def test_config_from_environ_depends_on_tracing(env_key, conf_key, env_val, conf_val, setenv): setenv(env_key, env_val) - setenv("DD_TRACE_ENABLED", "true" if trace_enabled else "false") - if trace_enabled: - assert getattr(config, conf_key) is conf_val - else: - assert getattr(config, conf_key) is False + setenv("DD_TRACE_ENABLED", "false") + assert getattr(config, conf_key) is False _test_fips_mode_from_environ = ( From f492d30d2c70acd448d178532d7ad8b4fa11566a Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 14:14:49 -0700 Subject: [PATCH 39/52] Cold start already depends on tracing. --- datadog_lambda/cold_start.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datadog_lambda/cold_start.py b/datadog_lambda/cold_start.py index 96cb2074..a40e2fcb 100644 --- a/datadog_lambda/cold_start.py +++ b/datadog_lambda/cold_start.py @@ -145,7 +145,7 @@ def wrapped_find_spec(*args, **kwargs): def initialize_cold_start_tracing(): - if is_new_sandbox() and config.trace_enabled and config.cold_start_tracing: + if is_new_sandbox() and config.cold_start_tracing: from sys import meta_path for importer in meta_path: From 23ac6b985183753475936f7151ff48954cff48cd Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 14:31:37 -0700 Subject: [PATCH 40/52] Testing for warning log. --- tests/test_config.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/test_config.py b/tests/test_config.py index 59e2cb6e..f5e61eaa 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,6 +1,6 @@ import pytest -from datadog_lambda.config import config +from datadog_lambda.config import config, _get_env @pytest.fixture @@ -178,3 +178,30 @@ def test_fips_mode_from_environ(fips_mode, region, conf_val, setenv): setenv("DD_LAMBDA_FIPS_MODE", fips_mode) setenv("AWS_REGION", region) assert config.fips_mode_enabled == conf_val + + +def test__get_env_does_not_log_when_env_not_set(setenv, monkeypatch): + setenv("TEST_1", None) + setenv("TEST_2", None) + setenv("TEST_3", None) + setenv("TEST_4", None) + + class Testing: + test_1 = _get_env("TEST_1") + test_2 = _get_env("TEST_2", "purple") + test_3 = _get_env("TEST_3", "true", bool) + test_4 = _get_env("TEST_4", "true", bool, depends_on_tracing=True) + + logs = [] + def cap_warn(*args, **kwargs): + logs.append(args) + + monkeypatch.setattr("datadog_lambda.config.logger.warning", cap_warn) + + testing = Testing() + testing.test_1 + testing.test_2 + testing.test_3 + testing.test_4 + + assert not logs From bbe0da49c55ea785675267854f3a7640d0645096 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Wed, 4 Jun 2025 14:49:07 -0700 Subject: [PATCH 41/52] More black. --- tests/test_config.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index f5e61eaa..fd59def3 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -61,8 +61,12 @@ def _test_as_list(env_key, conf_key, default): *_test_as_bool("DD_TRACE_ENABLED", "trace_enabled", default=True), *_test_as_bool("DD_COLD_START_TRACING", "cold_start_tracing", default=True), *_test_as_bool("DD_TRACE_MANAGED_SERVICES", "make_inferred_span", default=True), - *_test_as_bool("DD_ENCODE_AUTHORIZER_CONTEXT", "encode_authorizer_context", default=True), - *_test_as_bool("DD_DECODE_AUTHORIZER_CONTEXT", "decode_authorizer_context", default=True), + *_test_as_bool( + "DD_ENCODE_AUTHORIZER_CONTEXT", "encode_authorizer_context", default=True + ), + *_test_as_bool( + "DD_DECODE_AUTHORIZER_CONTEXT", "decode_authorizer_context", default=True + ), *_test_as_bool("DD_FLUSH_IN_THREAD", "flush_in_thread", default=False), *_test_as_bool("DD_ENHANCED_METRICS", "enhanced_metrics_enabled", default=True), *_test_as_bool("DD_INTEGRATION_TEST", "integration_test", default=False), @@ -123,15 +127,21 @@ def test_config_from_environ(env_key, conf_key, env_val, conf_val, setenv): _test_config_from_environ_depends_on_tracing = ( *_test_as_bool("DD_COLD_START_TRACING", "cold_start_tracing", default=True), *_test_as_bool("DD_TRACE_MANAGED_SERVICES", "make_inferred_span", default=True), - *_test_as_bool("DD_ENCODE_AUTHORIZER_CONTEXT", "encode_authorizer_context", default=True), - *_test_as_bool("DD_DECODE_AUTHORIZER_CONTEXT", "decode_authorizer_context", default=True), + *_test_as_bool( + "DD_ENCODE_AUTHORIZER_CONTEXT", "encode_authorizer_context", default=True + ), + *_test_as_bool( + "DD_DECODE_AUTHORIZER_CONTEXT", "decode_authorizer_context", default=True + ), ) @pytest.mark.parametrize( "env_key,conf_key,env_val,conf_val", _test_config_from_environ_depends_on_tracing ) -def test_config_from_environ_depends_on_tracing(env_key, conf_key, env_val, conf_val, setenv): +def test_config_from_environ_depends_on_tracing( + env_key, conf_key, env_val, conf_val, setenv +): setenv(env_key, env_val) setenv("DD_TRACE_ENABLED", "false") assert getattr(config, conf_key) is False @@ -193,6 +203,7 @@ class Testing: test_4 = _get_env("TEST_4", "true", bool, depends_on_tracing=True) logs = [] + def cap_warn(*args, **kwargs): logs.append(args) From d8041b13b79eefad5c5d00b1f059760a4e476692 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Thu, 5 Jun 2025 11:24:27 -0700 Subject: [PATCH 42/52] testing.x --- scripts/run_integration_tests.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/run_integration_tests.sh b/scripts/run_integration_tests.sh index 9ea2f013..bdbcd906 100755 --- a/scripts/run_integration_tests.sh +++ b/scripts/run_integration_tests.sh @@ -186,6 +186,14 @@ for handler_name in "${LAMBDA_HANDLERS[@]}"; do exit 1 fi + mkdir -p raw_logs + PYTHON_VERSION=${!python_version} RUNTIME=$parameters_set SERVERLESS_RUNTIME=${!serverless_runtime} ARCH=${ARCH} SLS_ARCH=${SERVERLESS_FRAMEWORK_ARCH} serverless logs --stage ${!run_id} -f $function_name --startTime $script_utc_start_time > raw_logs/${handler_name}-${parameters_set}.log + echo '----------------------------------------' + echo "Raw logs for $function_name with parameters set $parameters_set:" + echo '----------------------------------------' + cat raw_logs/${handler_name}-${parameters_set}.log + echo '----------------------------------------' + # Replace invocation-specific data like timestamps and IDs with XXXX to normalize logs across executions logs=$( echo "$raw_logs" | From 603c0e0c5cfcaa0a56e4c89f15d029c9a8091086 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Thu, 5 Jun 2025 14:29:29 -0700 Subject: [PATCH 43/52] Debugging. --- datadog_lambda/wrapper.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index 53491a8d..e8c7bb68 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -316,8 +316,12 @@ def _after(self, event, context): self.inferred_span.set_tag("peer.service", config.service) if InferredSpanInfo.is_async(self.inferred_span) and self.span: + print("FINISHING 1") + print('self.inferred_span.resource: ', self.inferred_span.resource) self.inferred_span.finish(finish_time=self.span.start) else: + print("FINISHING 2") + print('self.inferred_span.resource: ', self.inferred_span.resource) self.inferred_span.finish() if should_trace_cold_start: From 92936f3beceab22cebcb0d3be87701fba17ac99b Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Thu, 5 Jun 2025 14:44:20 -0700 Subject: [PATCH 44/52] More debug. --- Dockerfile | 2 + datadog_lambda/config.py | 1 + debug_encoding.py | 173 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 debug_encoding.py diff --git a/Dockerfile b/Dockerfile index 0e79d884..077ab66b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,6 +16,8 @@ ENV PATH=/root/.cargo/bin:$PATH COPY . . RUN pip install --no-cache-dir . -t ./python/lib/$runtime/site-packages +COPY debug_encoding.py ./python/lib/$runtime/site-packages/ddtrace/internal/encoding.py + # Remove botocore (40MB) to reduce package size. aws-xray-sdk # installs it, while it's already provided by the Lambda Runtime. RUN rm -rf ./python/lib/$runtime/site-packages/botocore* diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 203e97fd..355318eb 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -29,6 +29,7 @@ def _getter(self): default, ) val = default + print('key,val: ', key,val) setattr(self, prop_key, val) return getattr(self, prop_key) diff --git a/debug_encoding.py b/debug_encoding.py new file mode 100644 index 00000000..b3e299fa --- /dev/null +++ b/debug_encoding.py @@ -0,0 +1,173 @@ +import json +from typing import TYPE_CHECKING +from typing import Any # 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 ..settings._agent import config as agent_config # noqa:F401 +from ._encoding import ListStringTable +from ._encoding import MsgpackEncoderV04 +from ._encoding import MsgpackEncoderV05 +from .compat import ensure_text +from .logger import get_logger + + +__all__ = ["MsgpackEncoderV04", "MsgpackEncoderV05", "ListStringTable", "MSGPACK_ENCODERS"] + + +if TYPE_CHECKING: # pragma: no cover + from ddtrace._trace.span import Span # noqa:F401 + +log = get_logger(__name__) + + +class _EncoderBase(object): + """ + Encoder interface that provides the logic to encode traces and service. + """ + + def encode_traces(self, traces): + # type: (List[List[Span]]) -> str + """ + Encodes a list of traces, expecting a list of items where each items + is a list of spans. Before dumping the string in a serialized format all + traces are normalized according to the encoding format. The trace + nesting is not changed. + + :param traces: A list of traces that should be serialized + """ + raise NotImplementedError() + + def encode(self, obj): + # type: (List[List[Any]]) -> Tuple[str, int] + """ + Defines the underlying format used during traces or services encoding. + This method must be implemented and should only be used by the internal + functions. + """ + raise NotImplementedError() + + @staticmethod + def _span_to_dict(span): + # type: (Span) -> Dict[str, Any] + d = { + "trace_id": span._trace_id_64bits, + "parent_id": span.parent_id, + "span_id": span.span_id, + "service": span.service, + "resource": span.resource, + "name": span.name, + "error": span.error, + } # type: Dict[str, Any] + + # a common mistake is to set the error field to a boolean instead of an + # int. let's special case that here, because it's sure to happen in + # customer code. + err = d.get("error") + if err and type(err) == bool: + d["error"] = 1 + + if span.start_ns: + d["start"] = span.start_ns + + if span.duration_ns: + d["duration"] = span.duration_ns + + if span._meta: + d["meta"] = span._meta + + if span._metrics: + d["metrics"] = span._metrics + + if span.span_type: + d["type"] = span.span_type + + if span._links: + d["span_links"] = [link.to_dict() for link in span._links] + + if span._events and agent_config.trace_native_span_events: + d["span_events"] = [dict(event) for event in span._events] + + return d + + +class JSONEncoder(_EncoderBase): + content_type = "application/json" + + def encode_traces(self, traces): + normalized_traces = [ + [JSONEncoder._normalize_span(JSONEncoder._span_to_dict(span)) for span in trace] for trace in traces + ] + return self.encode(normalized_traces)[0] + + @staticmethod + def _normalize_span(span): + # Ensure all string attributes are actually strings and not bytes + # DEV: We are deferring meta/metrics to reduce any performance issues. + # Meta/metrics may still contain `bytes` and have encoding issues. + print('----------------------------------------') + print('span["resource"],type(span["resource"]): ', + span["resource"],type(span["resource"])) + print('span["name"],type(span["name"]): ', + span["name"], type(span["name"])) + print('span["service"],type(span["service"]): ', + span["service"], type(span["service"])) + import traceback; traceback.print_stack() + import time; time.sleep(0.5) + print('----------------------------------------') + span["resource"] = JSONEncoder._normalize_str(span["resource"]) + span["name"] = JSONEncoder._normalize_str(span["name"]) + span["service"] = JSONEncoder._normalize_str(span["service"]) + return span + + @staticmethod + def _normalize_str(obj): + if obj is None: + return obj + + return ensure_text(obj, errors="backslashreplace") + + def encode(self, obj): + return json.JSONEncoder().encode(obj), len(obj) + + +class JSONEncoderV2(JSONEncoder): + """ + JSONEncoderV2 encodes traces to the new intake API format. + """ + + content_type = "application/json" + + def encode_traces(self, traces): + # type: (List[List[Span]]) -> str + normalized_traces = [[JSONEncoderV2._convert_span(span) for span in trace] for trace in traces] + return self.encode({"traces": normalized_traces})[0] + + @staticmethod + def _convert_span(span): + # type: (Span) -> Dict[str, Any] + sp = JSONEncoderV2._span_to_dict(span) + sp = JSONEncoderV2._normalize_span(sp) + sp["trace_id"] = JSONEncoderV2._encode_id_to_hex(sp.get("trace_id")) + sp["parent_id"] = JSONEncoderV2._encode_id_to_hex(sp.get("parent_id")) + sp["span_id"] = JSONEncoderV2._encode_id_to_hex(sp.get("span_id")) + return sp + + @staticmethod + def _encode_id_to_hex(dd_id): + # type: (Optional[int]) -> str + if not dd_id: + return "0000000000000000" + return "%0.16X" % int(dd_id) + + def encode(self, obj): + res, _ = super().encode(obj) + return res, len(obj.get("traces", [])) + + +MSGPACK_ENCODERS = { + "v0.4": MsgpackEncoderV04, + "v0.5": MsgpackEncoderV05, +} From 1c32c44de934173aa8e627dfd6c41ed15d023b2a Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Thu, 5 Jun 2025 15:27:30 -0700 Subject: [PATCH 45/52] Undebug. --- Dockerfile | 2 - datadog_lambda/config.py | 1 - datadog_lambda/wrapper.py | 4 - debug_encoding.py | 173 ------------------------------- scripts/run_integration_tests.sh | 8 -- 5 files changed, 188 deletions(-) delete mode 100644 debug_encoding.py diff --git a/Dockerfile b/Dockerfile index 077ab66b..0e79d884 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,8 +16,6 @@ ENV PATH=/root/.cargo/bin:$PATH COPY . . RUN pip install --no-cache-dir . -t ./python/lib/$runtime/site-packages -COPY debug_encoding.py ./python/lib/$runtime/site-packages/ddtrace/internal/encoding.py - # Remove botocore (40MB) to reduce package size. aws-xray-sdk # installs it, while it's already provided by the Lambda Runtime. RUN rm -rf ./python/lib/$runtime/site-packages/botocore* diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 355318eb..203e97fd 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -29,7 +29,6 @@ def _getter(self): default, ) val = default - print('key,val: ', key,val) setattr(self, prop_key, val) return getattr(self, prop_key) diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index e8c7bb68..53491a8d 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -316,12 +316,8 @@ def _after(self, event, context): self.inferred_span.set_tag("peer.service", config.service) if InferredSpanInfo.is_async(self.inferred_span) and self.span: - print("FINISHING 1") - print('self.inferred_span.resource: ', self.inferred_span.resource) self.inferred_span.finish(finish_time=self.span.start) else: - print("FINISHING 2") - print('self.inferred_span.resource: ', self.inferred_span.resource) self.inferred_span.finish() if should_trace_cold_start: diff --git a/debug_encoding.py b/debug_encoding.py deleted file mode 100644 index b3e299fa..00000000 --- a/debug_encoding.py +++ /dev/null @@ -1,173 +0,0 @@ -import json -from typing import TYPE_CHECKING -from typing import Any # 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 ..settings._agent import config as agent_config # noqa:F401 -from ._encoding import ListStringTable -from ._encoding import MsgpackEncoderV04 -from ._encoding import MsgpackEncoderV05 -from .compat import ensure_text -from .logger import get_logger - - -__all__ = ["MsgpackEncoderV04", "MsgpackEncoderV05", "ListStringTable", "MSGPACK_ENCODERS"] - - -if TYPE_CHECKING: # pragma: no cover - from ddtrace._trace.span import Span # noqa:F401 - -log = get_logger(__name__) - - -class _EncoderBase(object): - """ - Encoder interface that provides the logic to encode traces and service. - """ - - def encode_traces(self, traces): - # type: (List[List[Span]]) -> str - """ - Encodes a list of traces, expecting a list of items where each items - is a list of spans. Before dumping the string in a serialized format all - traces are normalized according to the encoding format. The trace - nesting is not changed. - - :param traces: A list of traces that should be serialized - """ - raise NotImplementedError() - - def encode(self, obj): - # type: (List[List[Any]]) -> Tuple[str, int] - """ - Defines the underlying format used during traces or services encoding. - This method must be implemented and should only be used by the internal - functions. - """ - raise NotImplementedError() - - @staticmethod - def _span_to_dict(span): - # type: (Span) -> Dict[str, Any] - d = { - "trace_id": span._trace_id_64bits, - "parent_id": span.parent_id, - "span_id": span.span_id, - "service": span.service, - "resource": span.resource, - "name": span.name, - "error": span.error, - } # type: Dict[str, Any] - - # a common mistake is to set the error field to a boolean instead of an - # int. let's special case that here, because it's sure to happen in - # customer code. - err = d.get("error") - if err and type(err) == bool: - d["error"] = 1 - - if span.start_ns: - d["start"] = span.start_ns - - if span.duration_ns: - d["duration"] = span.duration_ns - - if span._meta: - d["meta"] = span._meta - - if span._metrics: - d["metrics"] = span._metrics - - if span.span_type: - d["type"] = span.span_type - - if span._links: - d["span_links"] = [link.to_dict() for link in span._links] - - if span._events and agent_config.trace_native_span_events: - d["span_events"] = [dict(event) for event in span._events] - - return d - - -class JSONEncoder(_EncoderBase): - content_type = "application/json" - - def encode_traces(self, traces): - normalized_traces = [ - [JSONEncoder._normalize_span(JSONEncoder._span_to_dict(span)) for span in trace] for trace in traces - ] - return self.encode(normalized_traces)[0] - - @staticmethod - def _normalize_span(span): - # Ensure all string attributes are actually strings and not bytes - # DEV: We are deferring meta/metrics to reduce any performance issues. - # Meta/metrics may still contain `bytes` and have encoding issues. - print('----------------------------------------') - print('span["resource"],type(span["resource"]): ', - span["resource"],type(span["resource"])) - print('span["name"],type(span["name"]): ', - span["name"], type(span["name"])) - print('span["service"],type(span["service"]): ', - span["service"], type(span["service"])) - import traceback; traceback.print_stack() - import time; time.sleep(0.5) - print('----------------------------------------') - span["resource"] = JSONEncoder._normalize_str(span["resource"]) - span["name"] = JSONEncoder._normalize_str(span["name"]) - span["service"] = JSONEncoder._normalize_str(span["service"]) - return span - - @staticmethod - def _normalize_str(obj): - if obj is None: - return obj - - return ensure_text(obj, errors="backslashreplace") - - def encode(self, obj): - return json.JSONEncoder().encode(obj), len(obj) - - -class JSONEncoderV2(JSONEncoder): - """ - JSONEncoderV2 encodes traces to the new intake API format. - """ - - content_type = "application/json" - - def encode_traces(self, traces): - # type: (List[List[Span]]) -> str - normalized_traces = [[JSONEncoderV2._convert_span(span) for span in trace] for trace in traces] - return self.encode({"traces": normalized_traces})[0] - - @staticmethod - def _convert_span(span): - # type: (Span) -> Dict[str, Any] - sp = JSONEncoderV2._span_to_dict(span) - sp = JSONEncoderV2._normalize_span(sp) - sp["trace_id"] = JSONEncoderV2._encode_id_to_hex(sp.get("trace_id")) - sp["parent_id"] = JSONEncoderV2._encode_id_to_hex(sp.get("parent_id")) - sp["span_id"] = JSONEncoderV2._encode_id_to_hex(sp.get("span_id")) - return sp - - @staticmethod - def _encode_id_to_hex(dd_id): - # type: (Optional[int]) -> str - if not dd_id: - return "0000000000000000" - return "%0.16X" % int(dd_id) - - def encode(self, obj): - res, _ = super().encode(obj) - return res, len(obj.get("traces", [])) - - -MSGPACK_ENCODERS = { - "v0.4": MsgpackEncoderV04, - "v0.5": MsgpackEncoderV05, -} diff --git a/scripts/run_integration_tests.sh b/scripts/run_integration_tests.sh index bdbcd906..9ea2f013 100755 --- a/scripts/run_integration_tests.sh +++ b/scripts/run_integration_tests.sh @@ -186,14 +186,6 @@ for handler_name in "${LAMBDA_HANDLERS[@]}"; do exit 1 fi - mkdir -p raw_logs - PYTHON_VERSION=${!python_version} RUNTIME=$parameters_set SERVERLESS_RUNTIME=${!serverless_runtime} ARCH=${ARCH} SLS_ARCH=${SERVERLESS_FRAMEWORK_ARCH} serverless logs --stage ${!run_id} -f $function_name --startTime $script_utc_start_time > raw_logs/${handler_name}-${parameters_set}.log - echo '----------------------------------------' - echo "Raw logs for $function_name with parameters set $parameters_set:" - echo '----------------------------------------' - cat raw_logs/${handler_name}-${parameters_set}.log - echo '----------------------------------------' - # Replace invocation-specific data like timestamps and IDs with XXXX to normalize logs across executions logs=$( echo "$raw_logs" | From 20d733ced33a5f4c41c3f536f66336a065d0d6af Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Thu, 5 Jun 2025 15:42:15 -0700 Subject: [PATCH 46/52] Fix multiple env accessing for same key. --- datadog_lambda/config.py | 23 +++++++++++++++++++---- tests/test_config.py | 23 ++++++++++++++++++++--- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 203e97fd..231251a1 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -49,10 +49,6 @@ class Config: service = _get_env("DD_SERVICE") env = _get_env("DD_ENV") - function_name = _get_env("AWS_LAMBDA_FUNCTION_NAME", "function") - is_gov_region = _get_env("AWS_REGION", "", lambda x: x.startswith("us-gov-")) - is_lambda_context = _get_env("AWS_LAMBDA_FUNCTION_NAME", None, bool) - cold_start_tracing = _get_env( "DD_COLD_START_TRACING", "true", as_bool, depends_on_tracing=True ) @@ -94,9 +90,28 @@ class Config: llmobs_enabled = _get_env("DD_LLMOBS_ENABLED", "false", as_bool) exception_replay_enabled = _get_env("DD_EXCEPTION_REPLAY_ENABLED", "false", as_bool) + is_gov_region = _get_env("AWS_REGION", "", lambda x: x.startswith("us-gov-")) + local_test = _get_env("DD_LOCAL_TEST", "false", as_bool) integration_test = _get_env("DD_INTEGRATION_TEST", "false", as_bool) + aws_lambda_function_name = _get_env("AWS_LAMBDA_FUNCTION_NAME") + + @property + def function_name(self): + if not hasattr(self, "_config_function_name"): + if self.aws_lambda_function_name is None: + self._config_function_name = "function" + else: + self._config_function_name = self.aws_lambda_function_name + return self._config_function_name + + @property + def is_lambda_context(self): + if not hasattr(self, "_config_is_lambda_context"): + self._config_is_lambda_context = bool(self.aws_lambda_function_name) + return self._config_is_lambda_context + @property def fips_mode_enabled(self): if not hasattr(self, "_config_fips_mode_enabled"): diff --git a/tests/test_config.py b/tests/test_config.py index fd59def3..ffd0f734 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -99,16 +99,24 @@ def _test_as_list(env_key, conf_key, default): ("DD_SERVICE", "service", None, None), ("DD_SERVICE", "service", "", ""), ("DD_SERVICE", "service", "my_service", "my_service"), + ("AWS_LAMBDA_FUNCTION_NAME", "aws_lambda_function_name", None, None), + ("AWS_LAMBDA_FUNCTION_NAME", "aws_lambda_function_name", "", ""), + ( + "AWS_LAMBDA_FUNCTION_NAME", + "aws_lambda_function_name", + "my_function", + "my_function", + ), ("AWS_LAMBDA_FUNCTION_NAME", "function_name", None, "function"), ("AWS_LAMBDA_FUNCTION_NAME", "function_name", "", ""), ("AWS_LAMBDA_FUNCTION_NAME", "function_name", "my_function", "my_function"), + ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", None, False), + ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", "", False), + ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", "my_function", True), ("AWS_REGION", "is_gov_region", None, False), ("AWS_REGION", "is_gov_region", "", False), ("AWS_REGION", "is_gov_region", "us-gov-1", True), ("AWS_REGION", "is_gov_region", "us-est-1", False), - ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", None, False), - ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", "", False), - ("AWS_LAMBDA_FUNCTION_NAME", "is_lambda_context", "my_function", True), ("DD_TRACE_EXTRACTOR", "trace_extractor", None, None), ("DD_TRACE_EXTRACTOR", "trace_extractor", "", ""), ("DD_TRACE_EXTRACTOR", "trace_extractor", "my_extractor", "my_extractor"), @@ -147,6 +155,15 @@ def test_config_from_environ_depends_on_tracing( assert getattr(config, conf_key) is False +def test_config_aws_lambda_function_name(setenv): + # these config values all access the same environment variable, test to + # ensure the wrong value is not cached + setenv("AWS_LAMBDA_FUNCTION_NAME", "my_function") + assert config.aws_lambda_function_name == "my_function" + assert config.function_name == "my_function" + assert config.is_lambda_context is True + + _test_fips_mode_from_environ = ( (None, None, False), (None, "", False), From 29572877b9fe0fac159c2100a1fda597300140ec Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Tue, 10 Jun 2025 12:56:03 -0700 Subject: [PATCH 47/52] Add data_streams_enabled. --- datadog_lambda/config.py | 3 +++ datadog_lambda/wrapper.py | 26 ++------------------------ tests/test_config.py | 1 + tests/test_wrapper.py | 8 ++++---- 4 files changed, 10 insertions(+), 28 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 231251a1..d2bb9d44 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -89,6 +89,9 @@ class Config: profiling_enabled = _get_env("DD_PROFILING_ENABLED", "false", as_bool) llmobs_enabled = _get_env("DD_LLMOBS_ENABLED", "false", as_bool) exception_replay_enabled = _get_env("DD_EXCEPTION_REPLAY_ENABLED", "false", as_bool) + data_streams_enabled = _get_env( + "DD_DATA_STREAMS_ENABLED", "false", as_bool, depends_on_tracing=True + ) is_gov_region = _get_env("AWS_REGION", "", lambda x: x.startswith("us-gov-")) diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index 53491a8d..42a167e5 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -60,8 +60,6 @@ DD_REQUESTS_SERVICE_NAME = "DD_REQUESTS_SERVICE_NAME" DD_SERVICE = "DD_SERVICE" -DD_DATA_STREAMS_ENABLED = "DD_DATA_STREAMS_ENABLED" - init_timestamp_ns = time_ns() @@ -121,26 +119,6 @@ def __init__(self, func): self.trace_extractor = None self.span = None self.inferred_span = None - depends_on_dd_tracing_enabled = ( - lambda original_boolean: config.trace_enabled and original_boolean - ) - self.cold_start_tracing = depends_on_dd_tracing_enabled( - os.environ.get(DD_COLD_START_TRACING, "true").lower() == "true" - ) - self.data_streams_enabled = ( - os.environ.get(DD_DATA_STREAMS_ENABLED, "false").lower() == "true" - ) - self.cold_start_trace_skip_lib = [ - "ddtrace.internal.compat", - "ddtrace.filters", - ] - if DD_COLD_START_TRACE_SKIP_LIB in os.environ: - try: - self.cold_start_trace_skip_lib = os.environ[ - DD_COLD_START_TRACE_SKIP_LIB - ].split(",") - except Exception: - logger.debug(f"Malformatted for env {DD_COLD_START_TRACE_SKIP_LIB}") self.response = None if config.profiling_enabled: @@ -261,7 +239,7 @@ def _before(self, event, context): self.inferred_span = create_inferred_span( event, context, event_source, config.decode_authorizer_context ) - if self.data_streams_enabled: + if config.data_streams_enabled: set_dsm_context(event, event_source) self.span = create_function_execution_span( context=context, @@ -348,7 +326,7 @@ def _after(self, event, context): LLMObs.flush() # Flush exception replay - if exception_replay_env_var: + if config.exception_replay_enabled: LogsIntakeUploaderV1._instance.periodic() if config.encode_authorizer_context and is_authorizer_response( diff --git a/tests/test_config.py b/tests/test_config.py index ffd0f734..d242534e 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -85,6 +85,7 @@ def _test_as_list(env_key, conf_key, default): "DD_CAPTURE_LAMBDA_PAYLOAD", "capture_payload_enabled", default=False ), *_test_as_bool("DD_LOCAL_TEST", "local_test", default=False), + *_test_as_bool("DD_DATA_STREAMS_ENABLED", "data_streams_enabled", default=False), *_test_int( "DD_CAPTURE_LAMBDA_PAYLOAD_MAX_DEPTH", "capture_payload_max_depth", default=10 ), diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index ad1fcb71..f0240905 100644 --- a/tests/test_wrapper.py +++ b/tests/test_wrapper.py @@ -564,7 +564,7 @@ def return_type_test(event, context): def test_set_dsm_context_called_when_DSM_and_tracing_enabled(self): os.environ["DD_DATA_STREAMS_ENABLED"] = "true" - wrapper.dd_tracing_enabled = True + os.environ["DD_TRACE_ENABLED"] = "true" @wrapper.datadog_lambda_wrapper def lambda_handler(event, context): @@ -578,7 +578,7 @@ def lambda_handler(event, context): def test_set_dsm_context_not_called_when_only_DSM_enabled(self): os.environ["DD_DATA_STREAMS_ENABLED"] = "true" - wrapper.dd_tracing_enabled = False + os.environ["DD_TRACE_ENABLED"] = "false" @wrapper.datadog_lambda_wrapper def lambda_handler(event, context): @@ -592,7 +592,7 @@ def lambda_handler(event, context): def test_set_dsm_context_not_called_when_only_tracing_enabled(self): os.environ["DD_DATA_STREAMS_ENABLED"] = "false" - wrapper.dd_tracing_enabled = True + os.environ["DD_TRACE_ENABLED"] = "true" @wrapper.datadog_lambda_wrapper def lambda_handler(event, context): @@ -606,7 +606,7 @@ def lambda_handler(event, context): def test_set_dsm_context_not_called_when_tracing_and_DSM_disabled(self): os.environ["DD_DATA_STREAMS_ENABLED"] = "false" - wrapper.dd_tracing_enabled = False + os.environ["DD_TRACE_ENABLED"] = "false" @wrapper.datadog_lambda_wrapper def lambda_handler(event, context): From 76b8ce9a1aecaf12c3b468023becdd82ffc007d3 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Tue, 10 Jun 2025 12:59:06 -0700 Subject: [PATCH 48/52] Shorter line. --- datadog_lambda/config.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index d2bb9d44..0c205413 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -21,13 +21,11 @@ def _getter(self): try: val = cast(val) except (ValueError, TypeError): - logger.warning( - "Failed to cast environment variable '%s' with value '%s' to type %s. Using default value '%s'.", - key, - val, - cast.__name__, - default, + msg = ( + "Failed to cast environment variable '%s' with " + "value '%s' to type %s. Using default value '%s'." ) + logger.warning(msg, key, val, cast.__name__, default) val = default setattr(self, prop_key, val) return getattr(self, prop_key) From fad3d08cd5ea0ce748574c970d716ecbf770b0f1 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Tue, 10 Jun 2025 13:01:38 -0700 Subject: [PATCH 49/52] Fix tag_object. --- datadog_lambda/wrapper.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index 42a167e5..87063411 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -27,6 +27,7 @@ from datadog_lambda.module_name import modify_module_name from datadog_lambda.patch import patch_all from datadog_lambda.span_pointers import calculate_span_pointers +from datadog_lambda.tag_object import tag_object from datadog_lambda.tracing import ( extract_dd_trace_context, create_dd_dummy_metadata_subsegment, @@ -279,8 +280,8 @@ def _after(self, event, context): if self.span: if config.capture_payload_enabled: - tag_object.tag_object(self.span, "function.request", event) - tag_object.tag_object(self.span, "function.response", self.response) + tag_object(self.span, "function.request", event) + tag_object(self.span, "function.response", self.response) if status_code: self.span.set_tag("http.status_code", status_code) From c90af2723174b87b79b3a8edc585d23695d9b1a0 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Tue, 10 Jun 2025 13:19:41 -0700 Subject: [PATCH 50/52] Telemetry depends on tracing. --- datadog_lambda/config.py | 5 ++++- datadog_lambda/tracing.py | 2 +- tests/test_config.py | 4 ++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index 0c205413..b2aaaae0 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -81,7 +81,10 @@ class Config: merge_xray_traces = _get_env("DD_MERGE_XRAY_TRACES", "false", as_bool) telemetry_enabled = _get_env( - "DD_INSTRUMENTATION_TELEMETRY_ENABLED", "false", as_bool + "DD_INSTRUMENTATION_TELEMETRY_ENABLED", + "false", + as_bool, + depends_on_tracing=True, ) otel_enabled = _get_env("DD_TRACE_OTEL_ENABLED", "false", as_bool) profiling_enabled = _get_env("DD_PROFILING_ENABLED", "false", as_bool) diff --git a/datadog_lambda/tracing.py b/datadog_lambda/tracing.py index c449bf54..3d5f671e 100644 --- a/datadog_lambda/tracing.py +++ b/datadog_lambda/tracing.py @@ -54,7 +54,7 @@ logger = logging.getLogger(__name__) dd_trace_context = None -if config.trace_enabled and config.telemetry_enabled: +if config.telemetry_enabled: # Enable the telemetry client if the user has opted in from ddtrace.internal.telemetry import telemetry_writer diff --git a/tests/test_config.py b/tests/test_config.py index d242534e..435f73ff 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -142,6 +142,10 @@ def test_config_from_environ(env_key, conf_key, env_val, conf_val, setenv): *_test_as_bool( "DD_DECODE_AUTHORIZER_CONTEXT", "decode_authorizer_context", default=True ), + *_test_as_bool("DD_DATA_STREAMS_ENABLED", "data_streams_enabled", default=False), + *_test_as_bool( + "DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", default=False + ), ) From 43e31fc21bffa48864f9a31b3a009240e72a6bcb Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Tue, 10 Jun 2025 13:28:20 -0700 Subject: [PATCH 51/52] Remove unneeded comment. --- tests/test_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_config.py b/tests/test_config.py index 435f73ff..7404d399 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -22,7 +22,7 @@ def _test_as_bool(env_key, conf_key, default): (env_key, conf_key, "TRUE", True), (env_key, conf_key, "false", False), (env_key, conf_key, "FALSE", False), - (env_key, conf_key, "1", True), # CHANGED + (env_key, conf_key, "1", True), (env_key, conf_key, "0", False), (env_key, conf_key, "purple", False), ) From 7bc87517f6a5d24a79ff638b3fd9a0ee2d528413 Mon Sep 17 00:00:00 2001 From: Rey Abolofia Date: Tue, 10 Jun 2025 14:51:08 -0700 Subject: [PATCH 52/52] Cut memory overhead. --- datadog_lambda/config.py | 30 ++++++++++++++++-------------- tests/test_config.py | 4 ++-- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/datadog_lambda/config.py b/datadog_lambda/config.py index b2aaaae0..7a08d8a7 100644 --- a/datadog_lambda/config.py +++ b/datadog_lambda/config.py @@ -13,20 +13,7 @@ def _get_env(key, default=None, cast=None, depends_on_tracing=False): @property def _getter(self): if not hasattr(self, prop_key): - if depends_on_tracing and not config.trace_enabled: - val = False - else: - val = os.environ.get(key, default) - if cast is not None: - try: - val = cast(val) - except (ValueError, TypeError): - msg = ( - "Failed to cast environment variable '%s' with " - "value '%s' to type %s. Using default value '%s'." - ) - logger.warning(msg, key, val, cast.__name__, default) - val = default + val = self._resolve_env(key, default, cast, depends_on_tracing) setattr(self, prop_key, val) return getattr(self, prop_key) @@ -43,6 +30,21 @@ def as_list(val): class Config: + def _resolve_env(self, key, default=None, cast=None, depends_on_tracing=False): + if depends_on_tracing and not self.trace_enabled: + return False + val = os.environ.get(key, default) + if cast is not None: + try: + val = cast(val) + except (ValueError, TypeError): + msg = ( + "Failed to cast environment variable '%s' with " + "value '%s' to type %s. Using default value '%s'." + ) + logger.warning(msg, key, val, cast.__name__, default) + val = default + return val service = _get_env("DD_SERVICE") env = _get_env("DD_ENV") diff --git a/tests/test_config.py b/tests/test_config.py index 7404d399..92002439 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,6 +1,6 @@ import pytest -from datadog_lambda.config import config, _get_env +from datadog_lambda.config import config, _get_env, Config @pytest.fixture @@ -218,7 +218,7 @@ def test__get_env_does_not_log_when_env_not_set(setenv, monkeypatch): setenv("TEST_3", None) setenv("TEST_4", None) - class Testing: + class Testing(Config): test_1 = _get_env("TEST_1") test_2 = _get_env("TEST_2", "purple") test_3 = _get_env("TEST_3", "true", bool)