diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 26b4b78e..a7f48dfe 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,6 +1 @@ -* @DataDog/serverless-aws -datadog_lambda/tracing.py @DataDog/apm-serverless -datadog_lambda/patch.py @DataDog/apm-serverless -datadog_lambda/span_points.py @DataDog/apm-serverless -datadog_lambda/cold_start.py @DataDog/apm-serverless -datadog_lambda/wrapper.py @DataDog/apm-serverless +* @DataDog/serverless-aws @DataDog/apm-serverless diff --git a/.github/workflows/update_deps.yml b/.github/workflows/update_deps.yml index 31025402..33a524b2 100644 --- a/.github/workflows/update_deps.yml +++ b/.github/workflows/update_deps.yml @@ -3,14 +3,24 @@ name: update-deps on: schedule: - cron: "0 10 * * *" # Run at 10 am every day + workflow_dispatch: jobs: check: runs-on: ubuntu-latest + environment: + name: protected-main-env steps: + - name: Generate token + id: generate_token + uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 + with: + app-id: ${{ secrets.GH_APP_ID }} + private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + - uses: actions/checkout@v3 with: - ssh-key: ${{ secrets.SSH_PRIVATE_KEY }} + token: ${{ steps.generate_token.outputs.token }} - name: Set up Python uses: actions/setup-python@v4 diff --git a/CODEOWNERS b/CODEOWNERS deleted file mode 100644 index e340f1ed..00000000 --- a/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -* @DataDog/serverless \ No newline at end of file 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..a40e2fcb 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.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..7a08d8a7 --- /dev/null +++ b/datadog_lambda/config.py @@ -0,0 +1,145 @@ +# 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 + +logger = logging.getLogger(__name__) + + +def _get_env(key, default=None, cast=None, depends_on_tracing=False): + @property + def _getter(self): + if not hasattr(self, prop_key): + val = self._resolve_env(key, default, cast, depends_on_tracing) + setattr(self, prop_key, val) + return getattr(self, prop_key) + + prop_key = f"_config_{key}" + return _getter + + +def as_bool(val): + return val.lower() == "true" or val == "1" + + +def as_list(val): + return [val.strip() for val in val.split(",") if val.strip()] + + +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") + + 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, + ) + + 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) + merge_xray_traces = _get_env("DD_MERGE_XRAY_TRACES", "false", as_bool) + + telemetry_enabled = _get_env( + "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) + 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-")) + + 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"): + 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): + for attr in dir(self): + if attr.startswith("_config_"): + delattr(self, attr) + + +config = Config() + +if config.is_gov_region or config.fips_mode_enabled: + logger.debug( + "Python Lambda Layer FIPS mode is %s.", + "enabled" if config.fips_mode_enabled else "not enabled", + ) diff --git a/datadog_lambda/dsm.py b/datadog_lambda/dsm.py new file mode 100644 index 00000000..427f5e47 --- /dev/null +++ b/datadog_lambda/dsm.py @@ -0,0 +1,38 @@ +from datadog_lambda import logger +from datadog_lambda.trigger import EventTypes + + +def set_dsm_context(event, event_source): + + if event_source.equals(EventTypes.SQS): + _dsm_set_sqs_context(event) + + +def _dsm_set_sqs_context(event): + from datadog_lambda.wrapper import format_err_with_traceback + from ddtrace.internal.datastreams import data_streams_processor + from ddtrace.internal.datastreams.processor import DsmPathwayCodec + from ddtrace.internal.datastreams.botocore import ( + get_datastreams_context, + calculate_sqs_payload_size, + ) + + records = event.get("Records") + if records is None: + return + processor = data_streams_processor() + + for record in records: + try: + queue_arn = record.get("eventSourceARN", "") + + contextjson = get_datastreams_context(record) + payload_size = calculate_sqs_payload_size(record) + + ctx = DsmPathwayCodec.decode(contextjson, processor) + ctx.set_checkpoint( + ["direction:in", f"topic:{queue_arn}", "type:sqs"], + payload_size=payload_size, + ) + except Exception as e: + logger.error(format_err_with_traceback(e)) 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..6d2af0dc 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.config import config +from datadog_lambda.tracing import get_dd_trace_context from collections.abc import MutableMapping logger = logging.getLogger(__name__) @@ -32,7 +29,7 @@ def patch_all(): """ _patch_for_integration_tests() - if dd_tracing_enabled: + if config.trace_enabled: patch_all_dd() else: _patch_http() @@ -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.integration_test: 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/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/tracing.py b/datadog_lambda/tracing.py index 9a27673c..3d5f671e 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.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 - - telemetry_writer.enable() + from ddtrace.internal.telemetry import telemetry_writer -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 @@ -850,13 +840,14 @@ def create_inferred_span_from_lambda_function_url_event(event, context): http = request_context.get("http") method = http.get("method") if http else None path = http.get("path") if http else None + http_url = f"https://{domain}{path}" resource = f"{method} {path}" tags = { "operation_name": "aws.lambda.url", - "http.url": domain + path, + "http.url": http_url, "endpoint": path, "http.method": method, - "resource_names": domain + path, + "resource_names": resource, "request_id": context.aws_request_id, } request_time_epoch = request_context.get("timeEpoch") @@ -948,6 +939,7 @@ def create_inferred_span_from_api_gateway_websocket_event( request_context = event.get("requestContext") domain = request_context.get("domainName") endpoint = request_context.get("routeKey") + http_url = f"https://{domain}{endpoint}" api_id = request_context.get("apiId") service_name = determine_service_name( @@ -955,7 +947,7 @@ def create_inferred_span_from_api_gateway_websocket_event( ) tags = { "operation_name": "aws.apigateway.websocket", - "http.url": domain + endpoint, + "http.url": http_url, "endpoint": endpoint, "resource_names": endpoint, "apiid": api_id, @@ -1007,11 +999,12 @@ def create_inferred_span_from_api_gateway_event( ) method = event.get("httpMethod") path = event.get("path") + http_url = f"https://{domain}{path}" resource_path = _get_resource_path(event, request_context) resource = f"{method} {resource_path}" tags = { "operation_name": "aws.apigateway.rest", - "http.url": domain + path, + "http.url": http_url, "endpoint": path, "http.method": method, "resource_names": resource, @@ -1073,12 +1066,13 @@ def create_inferred_span_from_http_api_event( http = request_context.get("http") or {} method = http.get("method") path = event.get("rawPath") + http_url = f"https://{domain}{path}" resource_path = _get_resource_path(event, request_context) resource = f"{method} {resource_path}" tags = { "operation_name": "aws.httpapi", "endpoint": path, - "http.url": domain + path, + "http.url": http_url, "http.method": http.get("method"), "http.protocol": http.get("protocol"), "http.source_ip": http.get("sourceIp"), @@ -1476,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/trigger.py b/datadog_lambda/trigger.py index a2708a59..bbd0d027 100644 --- a/datadog_lambda/trigger.py +++ b/datadog_lambda/trigger.py @@ -153,7 +153,7 @@ def parse_event_source(event: dict) -> _EventSource: event_source = _EventSource(EventTypes.STEPFUNCTIONS) event_record = get_first_record(event) - if event_record: + if event_record and isinstance(event_record, dict): aws_event_source = event_record.get("eventSource") or event_record.get( "EventSource" ) @@ -301,7 +301,7 @@ def extract_http_tags(event): if request_context and request_context.get("stage"): domain_name = request_context.get("domainName") if domain_name: - http_tags["http.url"] = domain_name + http_tags["http.url"] = f"https://{domain_name}" path = request_context.get("path") method = request_context.get("httpMethod") diff --git a/datadog_lambda/wrapper.py b/datadog_lambda/wrapper.py index 86bbf04d..87063411 100644 --- a/datadog_lambda/wrapper.py +++ b/datadog_lambda/wrapper.py @@ -9,6 +9,7 @@ from importlib import import_module from time import time_ns +from datadog_lambda.dsm import set_dsm_context from datadog_lambda.extension import should_use_extension, flush_extension from datadog_lambda.cold_start import ( set_cold_start, @@ -17,6 +18,7 @@ is_new_sandbox, ColdStartTracer, ) +from datadog_lambda.config import config from datadog_lambda.constants import ( TraceContextSource, XraySubsegment, @@ -25,11 +27,11 @@ 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, inject_correlation_ids, - dd_tracing_enabled, mark_trace_as_error_for_5xx_responses, set_correlation_ids, set_dd_trace_py_root, @@ -45,64 +47,20 @@ 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") -if llmobs_env_var: +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 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" -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" -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_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" - - -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 - - -dd_capture_lambda_payload_enabled = ( - 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() @@ -159,56 +117,16 @@ 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" - ) - 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) self.trace_extractor = None self.span = None self.inferred_span = None - depends_on_dd_tracing_enabled = ( - lambda original_boolean: dd_tracing_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" - ) - 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" - ) - self.min_cold_start_trace_duration = get_env_as_int( - DD_MIN_COLD_START_DURATION, 3 - ) - 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", - ] - 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 profiling_env_var: - self.prof = profiler.Profiler(env=env_env_var, service=self.service) - if self.extractor_env: - extractor_parts = self.extractor_env.rsplit(".", 1) + + 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: (mod_name, extractor_name) = extractor_parts modified_extractor_name = modify_module_name(mod_name) @@ -216,7 +134,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 @@ -228,11 +146,11 @@ def __init__(self, func): patch_all() # Enable LLM Observability - if llmobs_env_var: + if config.llmobs_enabled: LLMObs.enable() # Enable Exception Replay - if exception_replay_env_var: + if config.exception_replay_enabled: logger.debug("Enabling exception replay") SpanExceptionHandler.enable() @@ -302,7 +220,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 @@ -316,26 +234,28 @@ def _before(self, event, context): XraySubsegment.TRACE_KEY, ) - if dd_tracing_enabled: - set_dd_trace_py_root(trace_context_source, self.merge_xray_traces) - if self.make_inferred_span: + if config.trace_enabled: + 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 config.data_streams_enabled: + 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, - 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), ) 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: @@ -354,14 +274,14 @@ 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() if self.span: - if dd_capture_lambda_payload_enabled: - tag_object.tag_object(self.span, "function.request", event) - tag_object.tag_object(self.span, "function.response", self.response) + if config.capture_payload_enabled: + 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) @@ -371,8 +291,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) @@ -384,33 +304,35 @@ 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, - self.cold_start_trace_skip_lib, + config.min_cold_start_trace_duration, + config.cold_start_trace_skip_lib, ).trace() 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) - 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 flush_extension() - if llmobs_env_var: + if config.llmobs_enabled: LLMObs.flush() # Flush exception replay - if exception_replay_env_var: + if config.exception_replay_enabled: 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/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..33869802 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,8 @@ +import pytest + +from datadog_lambda.config import config + + +@pytest.fixture(autouse=True) +def reset_config(): + config._reset() diff --git a/tests/integration/snapshots/logs/async-metrics_python310.log b/tests/integration/snapshots/logs/async-metrics_python310.log index 24d3fb5b..0bd7237c 100644 --- a/tests/integration/snapshots/logs/async-metrics_python310.log +++ b/tests/integration/snapshots/logs/async-metrics_python310.log @@ -55,7 +55,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.rest", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com/", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com/", "endpoint": "/", "http.method": "GET", "resource_names": "GET /", @@ -103,7 +103,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com", "http.url_details.path": "/Prod/", "http.method": "GET", "http.route": "/", @@ -594,7 +594,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_dd.origin": "lambda", "operation_name": "aws.httpapi", "endpoint": "/httpapi/get", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", "http.method": "GET", "http.protocol": "HTTP/1.1", "http.source_ip": "XXXX", @@ -644,7 +644,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.url_details.path": "/httpapi/get", "http.method": "GET", "http.route": "/httpapi/get", @@ -1436,7 +1436,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.websocket", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com$default", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com$default", "endpoint": "$default", "resource_names": "$default", "apiid": "XXXX", @@ -1486,7 +1486,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.status_code": "200", "_dd.base_service": "integration-tests-python" }, diff --git a/tests/integration/snapshots/logs/async-metrics_python311.log b/tests/integration/snapshots/logs/async-metrics_python311.log index e4fa66bc..8550a062 100644 --- a/tests/integration/snapshots/logs/async-metrics_python311.log +++ b/tests/integration/snapshots/logs/async-metrics_python311.log @@ -55,7 +55,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.rest", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com/", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com/", "endpoint": "/", "http.method": "GET", "resource_names": "GET /", @@ -103,7 +103,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com", "http.url_details.path": "/Prod/", "http.method": "GET", "http.route": "/", @@ -594,7 +594,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_dd.origin": "lambda", "operation_name": "aws.httpapi", "endpoint": "/httpapi/get", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", "http.method": "GET", "http.protocol": "HTTP/1.1", "http.source_ip": "XXXX", @@ -644,7 +644,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.url_details.path": "/httpapi/get", "http.method": "GET", "http.route": "/httpapi/get", @@ -1436,7 +1436,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.websocket", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com$default", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com$default", "endpoint": "$default", "resource_names": "$default", "apiid": "XXXX", @@ -1486,7 +1486,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.status_code": "200", "_dd.base_service": "integration-tests-python" }, diff --git a/tests/integration/snapshots/logs/async-metrics_python312.log b/tests/integration/snapshots/logs/async-metrics_python312.log index 0d632c6c..57c318ab 100644 --- a/tests/integration/snapshots/logs/async-metrics_python312.log +++ b/tests/integration/snapshots/logs/async-metrics_python312.log @@ -55,7 +55,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.rest", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com/", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com/", "endpoint": "/", "http.method": "GET", "resource_names": "GET /", @@ -103,7 +103,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com", "http.url_details.path": "/Prod/", "http.method": "GET", "http.route": "/", @@ -594,7 +594,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_dd.origin": "lambda", "operation_name": "aws.httpapi", "endpoint": "/httpapi/get", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", "http.method": "GET", "http.protocol": "HTTP/1.1", "http.source_ip": "XXXX", @@ -644,7 +644,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.url_details.path": "/httpapi/get", "http.method": "GET", "http.route": "/httpapi/get", @@ -1436,7 +1436,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.websocket", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com$default", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com$default", "endpoint": "$default", "resource_names": "$default", "apiid": "XXXX", @@ -1486,7 +1486,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.status_code": "200", "_dd.base_service": "integration-tests-python" }, diff --git a/tests/integration/snapshots/logs/async-metrics_python313.log b/tests/integration/snapshots/logs/async-metrics_python313.log index 09070709..9204499b 100644 --- a/tests/integration/snapshots/logs/async-metrics_python313.log +++ b/tests/integration/snapshots/logs/async-metrics_python313.log @@ -55,7 +55,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.rest", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com/", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com/", "endpoint": "/", "http.method": "GET", "resource_names": "GET /", @@ -103,7 +103,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com", "http.url_details.path": "/Prod/", "http.method": "GET", "http.route": "/", @@ -594,7 +594,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_dd.origin": "lambda", "operation_name": "aws.httpapi", "endpoint": "/httpapi/get", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", "http.method": "GET", "http.protocol": "HTTP/1.1", "http.source_ip": "XXXX", @@ -644,7 +644,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.url_details.path": "/httpapi/get", "http.method": "GET", "http.route": "/httpapi/get", @@ -1436,7 +1436,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.websocket", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com$default", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com$default", "endpoint": "$default", "resource_names": "$default", "apiid": "XXXX", @@ -1486,7 +1486,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.status_code": "200", "_dd.base_service": "integration-tests-python" }, diff --git a/tests/integration/snapshots/logs/async-metrics_python38.log b/tests/integration/snapshots/logs/async-metrics_python38.log index 4a506930..e6df054c 100644 --- a/tests/integration/snapshots/logs/async-metrics_python38.log +++ b/tests/integration/snapshots/logs/async-metrics_python38.log @@ -55,7 +55,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.rest", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com/", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com/", "endpoint": "/", "http.method": "GET", "resource_names": "GET /", @@ -103,7 +103,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com", "http.url_details.path": "/Prod/", "http.method": "GET", "http.route": "/", @@ -594,7 +594,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_dd.origin": "lambda", "operation_name": "aws.httpapi", "endpoint": "/httpapi/get", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", "http.method": "GET", "http.protocol": "HTTP/1.1", "http.source_ip": "XXXX", @@ -644,7 +644,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.url_details.path": "/httpapi/get", "http.method": "GET", "http.route": "/httpapi/get", @@ -1436,7 +1436,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.websocket", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com$default", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com$default", "endpoint": "$default", "resource_names": "$default", "apiid": "XXXX", @@ -1486,7 +1486,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.status_code": "200", "_dd.base_service": "integration-tests-python" }, diff --git a/tests/integration/snapshots/logs/async-metrics_python39.log b/tests/integration/snapshots/logs/async-metrics_python39.log index 54081402..9bcb7a85 100644 --- a/tests/integration/snapshots/logs/async-metrics_python39.log +++ b/tests/integration/snapshots/logs/async-metrics_python39.log @@ -55,7 +55,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.rest", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com/", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com/", "endpoint": "/", "http.method": "GET", "resource_names": "GET /", @@ -103,7 +103,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com", "http.url_details.path": "/Prod/", "http.method": "GET", "http.route": "/", @@ -594,7 +594,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_dd.origin": "lambda", "operation_name": "aws.httpapi", "endpoint": "/httpapi/get", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", "http.method": "GET", "http.protocol": "HTTP/1.1", "http.source_ip": "XXXX", @@ -644,7 +644,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.url_details.path": "/httpapi/get", "http.method": "GET", "http.route": "/httpapi/get", @@ -1436,7 +1436,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.websocket", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com$default", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com$default", "endpoint": "$default", "resource_names": "$default", "apiid": "XXXX", @@ -1486,7 +1486,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.status_code": "200", "_dd.base_service": "integration-tests-python" }, diff --git a/tests/integration/snapshots/logs/sync-metrics_python310.log b/tests/integration/snapshots/logs/sync-metrics_python310.log index e2569775..40562a6d 100644 --- a/tests/integration/snapshots/logs/sync-metrics_python310.log +++ b/tests/integration/snapshots/logs/sync-metrics_python310.log @@ -35,7 +35,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.rest", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com/", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com/", "endpoint": "/", "http.method": "GET", "resource_names": "GET /", @@ -83,7 +83,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com", "http.url_details.path": "/Prod/", "http.method": "GET", "http.route": "/", @@ -631,7 +631,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_dd.origin": "lambda", "operation_name": "aws.httpapi", "endpoint": "/httpapi/get", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", "http.method": "GET", "http.protocol": "HTTP/1.1", "http.source_ip": "XXXX", @@ -681,7 +681,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.url_details.path": "/httpapi/get", "http.method": "GET", "http.route": "/httpapi/get", @@ -1568,7 +1568,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.websocket", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com$default", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com$default", "endpoint": "$default", "resource_names": "$default", "apiid": "XXXX", @@ -1618,7 +1618,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.status_code": "200", "_dd.base_service": "integration-tests-python" }, diff --git a/tests/integration/snapshots/logs/sync-metrics_python311.log b/tests/integration/snapshots/logs/sync-metrics_python311.log index 69d4a695..52ec4c85 100644 --- a/tests/integration/snapshots/logs/sync-metrics_python311.log +++ b/tests/integration/snapshots/logs/sync-metrics_python311.log @@ -35,7 +35,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.rest", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com/", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com/", "endpoint": "/", "http.method": "GET", "resource_names": "GET /", @@ -83,7 +83,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com", "http.url_details.path": "/Prod/", "http.method": "GET", "http.route": "/", @@ -631,7 +631,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_dd.origin": "lambda", "operation_name": "aws.httpapi", "endpoint": "/httpapi/get", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", "http.method": "GET", "http.protocol": "HTTP/1.1", "http.source_ip": "XXXX", @@ -681,7 +681,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.url_details.path": "/httpapi/get", "http.method": "GET", "http.route": "/httpapi/get", @@ -1568,7 +1568,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.websocket", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com$default", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com$default", "endpoint": "$default", "resource_names": "$default", "apiid": "XXXX", @@ -1618,7 +1618,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.status_code": "200", "_dd.base_service": "integration-tests-python" }, diff --git a/tests/integration/snapshots/logs/sync-metrics_python312.log b/tests/integration/snapshots/logs/sync-metrics_python312.log index 49bae0a2..3ec0f01f 100644 --- a/tests/integration/snapshots/logs/sync-metrics_python312.log +++ b/tests/integration/snapshots/logs/sync-metrics_python312.log @@ -35,7 +35,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.rest", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com/", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com/", "endpoint": "/", "http.method": "GET", "resource_names": "GET /", @@ -83,7 +83,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com", "http.url_details.path": "/Prod/", "http.method": "GET", "http.route": "/", @@ -631,7 +631,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_dd.origin": "lambda", "operation_name": "aws.httpapi", "endpoint": "/httpapi/get", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", "http.method": "GET", "http.protocol": "HTTP/1.1", "http.source_ip": "XXXX", @@ -681,7 +681,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.url_details.path": "/httpapi/get", "http.method": "GET", "http.route": "/httpapi/get", @@ -1568,7 +1568,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.websocket", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com$default", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com$default", "endpoint": "$default", "resource_names": "$default", "apiid": "XXXX", @@ -1618,7 +1618,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.status_code": "200", "_dd.base_service": "integration-tests-python" }, diff --git a/tests/integration/snapshots/logs/sync-metrics_python313.log b/tests/integration/snapshots/logs/sync-metrics_python313.log index 2f461f6f..d2c20dc0 100644 --- a/tests/integration/snapshots/logs/sync-metrics_python313.log +++ b/tests/integration/snapshots/logs/sync-metrics_python313.log @@ -35,7 +35,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.rest", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com/", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com/", "endpoint": "/", "http.method": "GET", "resource_names": "GET /", @@ -83,7 +83,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com", "http.url_details.path": "/Prod/", "http.method": "GET", "http.route": "/", @@ -631,7 +631,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_dd.origin": "lambda", "operation_name": "aws.httpapi", "endpoint": "/httpapi/get", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", "http.method": "GET", "http.protocol": "HTTP/1.1", "http.source_ip": "XXXX", @@ -681,7 +681,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.url_details.path": "/httpapi/get", "http.method": "GET", "http.route": "/httpapi/get", @@ -1568,7 +1568,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.websocket", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com$default", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com$default", "endpoint": "$default", "resource_names": "$default", "apiid": "XXXX", @@ -1618,7 +1618,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.status_code": "200", "_dd.base_service": "integration-tests-python" }, diff --git a/tests/integration/snapshots/logs/sync-metrics_python38.log b/tests/integration/snapshots/logs/sync-metrics_python38.log index 83e33d33..57a354a6 100644 --- a/tests/integration/snapshots/logs/sync-metrics_python38.log +++ b/tests/integration/snapshots/logs/sync-metrics_python38.log @@ -35,7 +35,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.rest", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com/", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com/", "endpoint": "/", "http.method": "GET", "resource_names": "GET /", @@ -83,7 +83,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com", "http.url_details.path": "/Prod/", "http.method": "GET", "http.route": "/", @@ -631,7 +631,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_dd.origin": "lambda", "operation_name": "aws.httpapi", "endpoint": "/httpapi/get", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", "http.method": "GET", "http.protocol": "HTTP/1.1", "http.source_ip": "XXXX", @@ -681,7 +681,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.url_details.path": "/httpapi/get", "http.method": "GET", "http.route": "/httpapi/get", @@ -1568,7 +1568,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.websocket", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com$default", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com$default", "endpoint": "$default", "resource_names": "$default", "apiid": "XXXX", @@ -1618,7 +1618,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.status_code": "200", "_dd.base_service": "integration-tests-python" }, diff --git a/tests/integration/snapshots/logs/sync-metrics_python39.log b/tests/integration/snapshots/logs/sync-metrics_python39.log index 0a433c34..8b7bb31b 100644 --- a/tests/integration/snapshots/logs/sync-metrics_python39.log +++ b/tests/integration/snapshots/logs/sync-metrics_python39.log @@ -35,7 +35,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.rest", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com/", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com/", "endpoint": "/", "http.method": "GET", "resource_names": "GET /", @@ -83,7 +83,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.us-east-2.amazonaws.com", + "http.url": "https://XXXX.execute-api.us-east-2.amazonaws.com", "http.url_details.path": "/Prod/", "http.method": "GET", "http.route": "/", @@ -377,7 +377,6 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A ] } HTTP POST https://api.datadoghq.com/api/v1/distribution_points Headers: ["Accept-Encoding:gzip, deflate","Accept:*/*","Connection:keep-alive","Content-Encoding:deflate","Content-Length:XXXX","Content-Type:application/json","DD-API-KEY:XXXX","User-Agent:datadogpy/XX (python XX; os linux; arch XXXX)","traceparent:XXX","tracestate:XXX -END Duration: XXXX ms Memory Used: XXXX MB { "traces": [ [ @@ -416,6 +415,7 @@ END Duration: XXXX ms Memory Used: XXXX MB ] ] } +END Duration: XXXX ms Memory Used: XXXX MB START { "m": "aws.lambda.enhanced.invocations", @@ -631,7 +631,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "_dd.origin": "lambda", "operation_name": "aws.httpapi", "endpoint": "/httpapi/get", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com/httpapi/get", "http.method": "GET", "http.protocol": "HTTP/1.1", "http.source_ip": "XXXX", @@ -681,7 +681,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX$default", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.url_details.path": "/httpapi/get", "http.method": "GET", "http.route": "/httpapi/get", @@ -1568,7 +1568,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "runtime-id": "XXXX", "_dd.origin": "lambda", "operation_name": "aws.apigateway.websocket", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com$default", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com$default", "endpoint": "$default", "resource_names": "$default", "apiid": "XXXX", @@ -1618,7 +1618,7 @@ HTTP GET https://www.datadoghq.com/ Headers: ["Accept-Encoding:gzip, deflate","A "span.name": "aws.lambda", "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "XXXX", - "http.url": "XXXX.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://XXXX.execute-api.eu-west-1.amazonaws.com", "http.status_code": "200", "_dd.base_service": "integration-tests-python" }, 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..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): @@ -247,7 +249,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" ) @@ -257,10 +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_mock_context() handler.cold_start_tracing = True handler({}, lambda_context) diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 00000000..92002439 --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,240 @@ +import pytest + +from datadog_lambda.config import config, _get_env, 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 + + +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), + (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_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), + *_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_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 + ), + *_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", "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), + ("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"), +) + + +@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_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_DATA_STREAMS_ENABLED", "data_streams_enabled", default=False), + *_test_as_bool( + "DD_INSTRUMENTATION_TELEMETRY_ENABLED", "telemetry_enabled", default=False + ), +) + + +@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 +): + setenv(env_key, env_val) + setenv("DD_TRACE_ENABLED", "false") + 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), + (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, 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(Config): + 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 diff --git a/tests/test_dsm.py b/tests/test_dsm.py new file mode 100644 index 00000000..544212d8 --- /dev/null +++ b/tests/test_dsm.py @@ -0,0 +1,112 @@ +import unittest +from unittest.mock import patch, MagicMock + +from datadog_lambda.dsm import set_dsm_context, _dsm_set_sqs_context +from datadog_lambda.trigger import EventTypes, _EventSource + + +class TestDsmSQSContext(unittest.TestCase): + def setUp(self): + patcher = patch("datadog_lambda.dsm._dsm_set_sqs_context") + self.mock_dsm_set_sqs_context = patcher.start() + self.addCleanup(patcher.stop) + + patcher = patch("ddtrace.internal.datastreams.data_streams_processor") + self.mock_data_streams_processor = patcher.start() + self.addCleanup(patcher.stop) + + patcher = patch("ddtrace.internal.datastreams.botocore.get_datastreams_context") + self.mock_get_datastreams_context = patcher.start() + self.mock_get_datastreams_context.return_value = {} + self.addCleanup(patcher.stop) + + patcher = patch( + "ddtrace.internal.datastreams.botocore.calculate_sqs_payload_size" + ) + self.mock_calculate_sqs_payload_size = patcher.start() + self.mock_calculate_sqs_payload_size.return_value = 100 + self.addCleanup(patcher.stop) + + patcher = patch("ddtrace.internal.datastreams.processor.DsmPathwayCodec.decode") + self.mock_dsm_pathway_codec_decode = patcher.start() + self.addCleanup(patcher.stop) + + def test_non_sqs_event_source_does_nothing(self): + """Test that non-SQS event sources don't trigger DSM context setting""" + event = {} + # Use Unknown Event Source + event_source = _EventSource(EventTypes.UNKNOWN) + set_dsm_context(event, event_source) + + # DSM context should not be set for non-SQS events + self.mock_dsm_set_sqs_context.assert_not_called() + + def test_sqs_event_with_no_records_does_nothing(self): + """Test that events where Records is None don't trigger DSM processing""" + events_with_no_records = [ + {}, + {"Records": None}, + {"someOtherField": "value"}, + ] + + for event in events_with_no_records: + _dsm_set_sqs_context(event) + self.mock_data_streams_processor.assert_not_called() + + def test_sqs_event_triggers_dsm_sqs_context(self): + """Test that SQS event sources trigger the SQS-specific DSM context function""" + sqs_event = { + "Records": [ + { + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:us-east-1:123456789012:my-queue", + "body": "Hello from SQS!", + } + ] + } + + event_source = _EventSource(EventTypes.SQS) + set_dsm_context(sqs_event, event_source) + + self.mock_dsm_set_sqs_context.assert_called_once_with(sqs_event) + + def test_sqs_multiple_records_process_each_record(self): + """Test that each record in an SQS event gets processed individually""" + multi_record_event = { + "Records": [ + { + "eventSourceARN": "arn:aws:sqs:us-east-1:123456789012:queue1", + "body": "Message 1", + }, + { + "eventSourceARN": "arn:aws:sqs:us-east-1:123456789012:queue2", + "body": "Message 2", + }, + { + "eventSourceARN": "arn:aws:sqs:us-east-1:123456789012:queue3", + "body": "Message 3", + }, + ] + } + + mock_context = MagicMock() + self.mock_dsm_pathway_codec_decode.return_value = mock_context + + _dsm_set_sqs_context(multi_record_event) + + self.assertEqual(mock_context.set_checkpoint.call_count, 3) + + calls = mock_context.set_checkpoint.call_args_list + expected_arns = [ + "arn:aws:sqs:us-east-1:123456789012:queue1", + "arn:aws:sqs:us-east-1:123456789012:queue2", + "arn:aws:sqs:us-east-1:123456789012:queue3", + ] + + for i, call in enumerate(calls): + args, kwargs = call + tags = args[0] + self.assertIn("direction:in", tags) + self.assertIn(f"topic:{expected_arns[i]}", tags) + self.assertIn("type:sqs", tags) + self.assertEqual(kwargs["payload_size"], 100) diff --git a/tests/test_metric.py b/tests/test_metric.py index e7dab2c3..aa537d34 100644 --- a/tests/test_metric.py +++ b/tests/test_metric.py @@ -62,7 +62,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_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/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( diff --git a/tests/test_tracing.py b/tests/test_tracing.py index 0a961a62..a629343e 100644 --- a/tests/test_tracing.py +++ b/tests/test_tracing.py @@ -251,20 +251,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") @@ -984,11 +980,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 +1121,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 +1138,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 +1238,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 +1279,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) @@ -1730,7 +1726,7 @@ def __init__(self, service, start, span_type, parent_name=None, tags=None): "apiname": "1234567890", "endpoint": "/path/to/resource", "http.method": "POST", - "http.url": "70ixmpl4fl.execute-api.us-east-2.amazonaws.com/path/to/resource", + "http.url": "https://70ixmpl4fl.execute-api.us-east-2.amazonaws.com/path/to/resource", "operation_name": "aws.apigateway.rest", "request_id": "123", "resource_names": "POST /{proxy+}", @@ -1752,7 +1748,7 @@ def __init__(self, service, start, span_type, parent_name=None, tags=None): "apiname": "lgxbo6a518", "endpoint": "/http/get", "http.method": "GET", - "http.url": "lgxbo6a518.execute-api.eu-west-1.amazonaws.com/http/get", + "http.url": "https://lgxbo6a518.execute-api.eu-west-1.amazonaws.com/http/get", "operation_name": "aws.apigateway.rest", "request_id": "123", "resource_names": "GET /http/get", @@ -1774,7 +1770,7 @@ def __init__(self, service, start, span_type, parent_name=None, tags=None): "apiname": "lgxbo6a518", "endpoint": "/http/get", "http.method": "GET", - "http.url": "lgxbo6a518.execute-api.eu-west-1.amazonaws.com/http/get", + "http.url": "https://lgxbo6a518.execute-api.eu-west-1.amazonaws.com/http/get", "operation_name": "aws.apigateway.rest", "request_id": "123", "resource_names": "GET /http/get", @@ -1798,7 +1794,7 @@ def __init__(self, service, start, span_type, parent_name=None, tags=None): "http.method": "GET", "http.protocol": "HTTP/1.1", "http.source_ip": "38.122.226.210", - "http.url": "x02yirxc7a.execute-api.eu-west-1.amazonaws.com/httpapi/get", + "http.url": "https://x02yirxc7a.execute-api.eu-west-1.amazonaws.com/httpapi/get", "http.user_agent": "curl/7.64.1", "operation_name": "aws.httpapi", "request_id": "123", @@ -1821,7 +1817,7 @@ def __init__(self, service, start, span_type, parent_name=None, tags=None): "apiname": "mcwkra0ya4", "endpoint": "/user/42", "http.method": "GET", - "http.url": "mcwkra0ya4.execute-api.sa-east-1.amazonaws.com/user/42", + "http.url": "https://mcwkra0ya4.execute-api.sa-east-1.amazonaws.com/user/42", "operation_name": "aws.apigateway.rest", "request_id": "123", "resource_names": "GET /user/{id}", @@ -1843,7 +1839,7 @@ def __init__(self, service, start, span_type, parent_name=None, tags=None): "apiname": "9vj54we5ih", "endpoint": "/user/42", "http.method": "GET", - "http.url": "9vj54we5ih.execute-api.sa-east-1.amazonaws.com/user/42", + "http.url": "https://9vj54we5ih.execute-api.sa-east-1.amazonaws.com/user/42", "operation_name": "aws.httpapi", "request_id": "123", "resource_names": "GET /user/{id}", @@ -1866,7 +1862,7 @@ def __init__(self, service, start, span_type, parent_name=None, tags=None): "connection_id": "Fc5SzcoYGjQCJlg=", "endpoint": "$default", "event_type": "MESSAGE", - "http.url": "p62c47itsb.execute-api.eu-west-1.amazonaws.com$default", + "http.url": "https://p62c47itsb.execute-api.eu-west-1.amazonaws.com$default", "message_direction": "IN", "operation_name": "aws.apigateway.websocket", "request_id": "123", @@ -1890,7 +1886,7 @@ def __init__(self, service, start, span_type, parent_name=None, tags=None): "connection_id": "Fc2tgfl3mjQCJfA=", "endpoint": "$connect", "event_type": "CONNECT", - "http.url": "p62c47itsb.execute-api.eu-west-1.amazonaws.com$connect", + "http.url": "https://p62c47itsb.execute-api.eu-west-1.amazonaws.com$connect", "message_direction": "IN", "operation_name": "aws.apigateway.websocket", "request_id": "123", @@ -1914,7 +1910,7 @@ def __init__(self, service, start, span_type, parent_name=None, tags=None): "connection_id": "Fc2tgfl3mjQCJfA=", "endpoint": "$disconnect", "event_type": "DISCONNECT", - "http.url": "p62c47itsb.execute-api.eu-west-1.amazonaws.com$disconnect", + "http.url": "https://p62c47itsb.execute-api.eu-west-1.amazonaws.com$disconnect", "message_direction": "IN", "operation_name": "aws.apigateway.websocket", "request_id": "123", @@ -2112,7 +2108,7 @@ def __init__(self, service, start, span_type, parent_name=None, tags=None): "apiname": "None", "endpoint": "/path/to/resource", "http.method": "POST", - "http.url": "70ixmpl4fl.execute-api.us-east-2.amazonaws.com/path/to/resource", + "http.url": "https://70ixmpl4fl.execute-api.us-east-2.amazonaws.com/path/to/resource", "operation_name": "aws.apigateway.rest", "request_id": "123", "resource_names": "POST /{proxy+}", @@ -2135,7 +2131,7 @@ def __init__(self, service, start, span_type, parent_name=None, tags=None): "apiname": "amddr1rix9", "endpoint": "/hello", "http.method": "GET", - "http.url": "amddr1rix9.execute-api.eu-west-1.amazonaws.com/hello", + "http.url": "https://amddr1rix9.execute-api.eu-west-1.amazonaws.com/hello", "operation_name": "aws.apigateway.rest", "request_id": "123", "resource_names": "GET /hello", @@ -2157,7 +2153,7 @@ def __init__(self, service, start, span_type, parent_name=None, tags=None): "apiname": "amddr1rix9", "endpoint": "/hello", "http.method": "GET", - "http.url": "amddr1rix9.execute-api.eu-west-1.amazonaws.com/hello", + "http.url": "https://amddr1rix9.execute-api.eu-west-1.amazonaws.com/hello", "operation_name": "aws.apigateway.rest", "request_id": "123", "resource_names": "GET /hello", @@ -2180,7 +2176,7 @@ def __init__(self, service, start, span_type, parent_name=None, tags=None): "apiname": "amddr1rix9", "endpoint": "/hello", "http.method": "GET", - "http.url": "amddr1rix9.execute-api.eu-west-1.amazonaws.com/hello", + "http.url": "https://amddr1rix9.execute-api.eu-west-1.amazonaws.com/hello", "operation_name": "aws.apigateway.rest", "request_id": "123", "resource_names": "GET /hello", @@ -2202,7 +2198,7 @@ def __init__(self, service, start, span_type, parent_name=None, tags=None): "apiname": "amddr1rix9", "endpoint": "/hello", "http.method": "GET", - "http.url": "amddr1rix9.execute-api.eu-west-1.amazonaws.com/hello", + "http.url": "https://amddr1rix9.execute-api.eu-west-1.amazonaws.com/hello", "operation_name": "aws.apigateway.rest", "request_id": "123", "resource_names": "GET /hello", @@ -2224,7 +2220,7 @@ def __init__(self, service, start, span_type, parent_name=None, tags=None): "apiname": "amddr1rix9", "endpoint": "/hello", "http.method": "GET", - "http.url": "amddr1rix9.execute-api.eu-west-1.amazonaws.com/hello", + "http.url": "https://amddr1rix9.execute-api.eu-west-1.amazonaws.com/hello", "operation_name": "aws.httpapi", "request_id": "123", "resource_names": "GET /hello", @@ -2246,7 +2242,7 @@ def __init__(self, service, start, span_type, parent_name=None, tags=None): "apiname": "amddr1rix9", "endpoint": "/hello", "http.method": "GET", - "http.url": "amddr1rix9.execute-api.eu-west-1.amazonaws.com/hello", + "http.url": "https://amddr1rix9.execute-api.eu-west-1.amazonaws.com/hello", "operation_name": "aws.httpapi", "request_id": "123", "resource_names": "GET /hello", @@ -2270,7 +2266,7 @@ def __init__(self, service, start, span_type, parent_name=None, tags=None): "connection_id": "ZLr9QeNLmjQCIZA=", "endpoint": "$connect", "event_type": "CONNECT", - "http.url": "amddr1rix9.execute-api.eu-west-1.amazonaws.com$connect", + "http.url": "https://amddr1rix9.execute-api.eu-west-1.amazonaws.com$connect", "message_direction": "IN", "operation_name": "aws.apigateway.websocket", "request_id": "123", @@ -2294,7 +2290,7 @@ def __init__(self, service, start, span_type, parent_name=None, tags=None): "connection_id": "ZLwtceO1mjQCI8Q=", "endpoint": "main", "event_type": "MESSAGE", - "http.url": "amddr1rix9.execute-api.eu-west-1.amazonaws.commain", + "http.url": "https://amddr1rix9.execute-api.eu-west-1.amazonaws.commain", "message_direction": "IN", "operation_name": "aws.apigateway.websocket", "request_id": "123", @@ -2386,7 +2382,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 +2423,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_trigger.py b/tests/test_trigger.py index b4da7ff0..15103937 100644 --- a/tests/test_trigger.py +++ b/tests/test_trigger.py @@ -280,6 +280,13 @@ def test_detect_lambda_function_url_domain_with_invalid_input(self): # Test with string that would normally cause an exception when split self.assertFalse(detect_lambda_function_url_domain("")) + def test_event_source_with_non_dict_event_record(self): + # Test with event_record that's not a dictionary + event = {"Records": "not_a_dict"} + event_source = parse_event_source(event) + # Should handle the first non-dict record gracefully and return unknown + self.assertEqual(event_source.to_string(), "unknown") + class GetTriggerTags(unittest.TestCase): def test_extract_trigger_tags_api_gateway(self): @@ -294,7 +301,7 @@ def test_extract_trigger_tags_api_gateway(self): { "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "arn:aws:apigateway:us-west-1::/restapis/1234567890/stages/prod", - "http.url": "70ixmpl4fl.execute-api.us-east-2.amazonaws.com", + "http.url": "https://70ixmpl4fl.execute-api.us-east-2.amazonaws.com", "http.url_details.path": "/prod/path/to/resource", "http.method": "POST", "http.route": "/{proxy+}", @@ -313,7 +320,7 @@ def test_extract_trigger_tags_api_gateway_non_proxy(self): { "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "arn:aws:apigateway:us-west-1::/restapis/lgxbo6a518/stages/dev", - "http.url": "lgxbo6a518.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://lgxbo6a518.execute-api.eu-west-1.amazonaws.com", "http.url_details.path": "/dev/http/get", "http.method": "GET", "http.route": "/http/get", @@ -332,7 +339,7 @@ def test_extract_trigger_tags_api_gateway_websocket_connect(self): { "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "arn:aws:apigateway:us-west-1::/restapis/p62c47itsb/stages/dev", - "http.url": "p62c47itsb.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://p62c47itsb.execute-api.eu-west-1.amazonaws.com", }, ) @@ -348,7 +355,7 @@ def test_extract_trigger_tags_api_gateway_websocket_default(self): { "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "arn:aws:apigateway:us-west-1::/restapis/p62c47itsb/stages/dev", - "http.url": "p62c47itsb.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://p62c47itsb.execute-api.eu-west-1.amazonaws.com", }, ) @@ -364,7 +371,7 @@ def test_extract_trigger_tags_api_gateway_websocket_disconnect(self): { "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "arn:aws:apigateway:us-west-1::/restapis/p62c47itsb/stages/dev", - "http.url": "p62c47itsb.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://p62c47itsb.execute-api.eu-west-1.amazonaws.com", }, ) @@ -380,7 +387,7 @@ def test_extract_trigger_tags_api_gateway_http_api(self): { "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "arn:aws:apigateway:us-west-1::/restapis/x02yirxc7a/stages/$default", - "http.url": "x02yirxc7a.execute-api.eu-west-1.amazonaws.com", + "http.url": "https://x02yirxc7a.execute-api.eu-west-1.amazonaws.com", "http.url_details.path": "/httpapi/get", "http.method": "GET", "http.route": "/httpapi/get", diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index 4b243036..f0240905 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" ) @@ -76,9 +77,12 @@ def setUp(self): self.mock_dd_lambda_layer_tag = patcher.start() self.addCleanup(patcher.stop) - def test_datadog_lambda_wrapper(self): - wrapper.dd_tracing_enabled = False + patcher = patch("datadog_lambda.wrapper.set_dsm_context") + 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.datadog_lambda_wrapper def lambda_handler(event, context): lambda_metric("test.metric", 100) @@ -88,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( @@ -185,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): @@ -195,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() @@ -283,7 +285,7 @@ def test_5xx_sends_errors_metric_and_set_tags(self, mock_extract_trigger_tags): mock_extract_trigger_tags.return_value = { "function_trigger.event_source": "api-gateway", "function_trigger.event_source_arn": "arn:aws:apigateway:us-west-1::/restapis/1234567890/stages/prod", - "http.url": "70ixmpl4fl.execute-api.us-east-2.amazonaws.com", + "http.url": "https://70ixmpl4fl.execute-api.us-east-2.amazonaws.com", "http.url_details.path": "/prod/path/to/resource", "http.method": "GET", } @@ -453,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() @@ -511,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): @@ -537,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) @@ -563,17 +562,61 @@ def return_type_test(event, context): self.assertEqual(result, test_result) self.assertFalse(MockPrintExc.called) + def test_set_dsm_context_called_when_DSM_and_tracing_enabled(self): + os.environ["DD_DATA_STREAMS_ENABLED"] = "true" + os.environ["DD_TRACE_ENABLED"] = "true" + + @wrapper.datadog_lambda_wrapper + def lambda_handler(event, context): + return "ok" + + result = lambda_handler({}, get_mock_context()) + self.assertEqual(result, "ok") + self.mock_set_dsm_context.assert_called_once() + + del os.environ["DD_DATA_STREAMS_ENABLED"] + + def test_set_dsm_context_not_called_when_only_DSM_enabled(self): + os.environ["DD_DATA_STREAMS_ENABLED"] = "true" + os.environ["DD_TRACE_ENABLED"] = "false" + + @wrapper.datadog_lambda_wrapper + def lambda_handler(event, context): + return "ok" + + result = lambda_handler({}, get_mock_context()) + self.assertEqual(result, "ok") + self.mock_set_dsm_context.assert_not_called() + + del os.environ["DD_DATA_STREAMS_ENABLED"] + + def test_set_dsm_context_not_called_when_only_tracing_enabled(self): + os.environ["DD_DATA_STREAMS_ENABLED"] = "false" + os.environ["DD_TRACE_ENABLED"] = "true" + + @wrapper.datadog_lambda_wrapper + def lambda_handler(event, context): + return "ok" + + result = lambda_handler({}, get_mock_context()) + self.assertEqual(result, "ok") + self.mock_set_dsm_context.assert_not_called() -class TestLambdaDecoratorSettings(unittest.TestCase): - 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" - decorator = wrapper._LambdaDecorator(func=None) - self.assertFalse(decorator.make_inferred_span) - self.assertFalse(decorator.encode_authorizer_context) - self.assertFalse(decorator.decode_authorizer_context) + del os.environ["DD_DATA_STREAMS_ENABLED"] + + def test_set_dsm_context_not_called_when_tracing_and_DSM_disabled(self): + os.environ["DD_DATA_STREAMS_ENABLED"] = "false" + os.environ["DD_TRACE_ENABLED"] = "false" + + @wrapper.datadog_lambda_wrapper + def lambda_handler(event, context): + return "ok" + + result = lambda_handler({}, get_mock_context()) + self.assertEqual(result, "ok") + self.mock_set_dsm_context.assert_not_called() + + del os.environ["DD_DATA_STREAMS_ENABLED"] class TestLambdaWrapperWithTraceContext(unittest.TestCase): @@ -646,14 +689,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() @@ -661,24 +718,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) 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