|
43 | 43 | is_step_function_event, |
44 | 44 | EventTypes, |
45 | 45 | EventSubtypes, |
| 46 | + resolve_multivalue_headers, |
46 | 47 | ) |
47 | 48 | from datadog_lambda.durable import extract_context_from_durable_execution |
48 | 49 |
|
@@ -207,7 +208,7 @@ def extract_context_from_http_event_or_context( |
207 | 208 | if _is_context_complete(context): |
208 | 209 | return context |
209 | 210 |
|
210 | | - headers = event.get("headers") |
| 211 | + headers = resolve_multivalue_headers(event) |
211 | 212 | context = propagator.extract(headers) |
212 | 213 |
|
213 | 214 | if not _is_context_complete(context): |
@@ -740,7 +741,9 @@ def extract_dd_trace_context( |
740 | 741 | context = extract_context_from_request_header_or_context( |
741 | 742 | event, lambda_context, event_source |
742 | 743 | ) |
743 | | - elif isinstance(event, (set, dict)) and "headers" in event: |
| 744 | + elif isinstance(event, (set, dict)) and ( |
| 745 | + "headers" in event or "multiValueHeaders" in event |
| 746 | + ): |
744 | 747 | context = extract_context_from_http_event_or_context( |
745 | 748 | event, lambda_context, event_source, decode_authorizer_context |
746 | 749 | ) |
@@ -919,6 +922,9 @@ def create_inferred_span( |
919 | 922 | elif event_source.equals(EventTypes.LAMBDA_FUNCTION_URL): |
920 | 923 | logger.debug("Function URL event detected. Inferring a span") |
921 | 924 | return create_inferred_span_from_lambda_function_url_event(event, context) |
| 925 | + elif event_source.event_type == EventTypes.ALB: |
| 926 | + logger.debug("ALB event detected. Inferring a span") |
| 927 | + return create_inferred_span_from_alb_event(event, context) |
922 | 928 | elif event_source.equals( |
923 | 929 | EventTypes.API_GATEWAY, subtype=EventSubtypes.HTTP_API |
924 | 930 | ): |
@@ -982,11 +988,14 @@ def determine_service_name( |
982 | 988 | if mapped_service: |
983 | 989 | return mapped_service |
984 | 990 |
|
| 991 | + # When integration service names are removed, inferred (synthetic) spans use |
| 992 | + # the base service name (DD_SERVICE) instead of the AWS resource/instance |
| 993 | + # representation. |
| 994 | + if config.remove_integration_service_names_enabled and config.service: |
| 995 | + return config.service |
| 996 | + |
985 | 997 | # Check if AWS service representation is disabled |
986 | | - aws_service_representation = os.environ.get( |
987 | | - "DD_TRACE_AWS_SERVICE_REPRESENTATION_ENABLED", "" |
988 | | - ).lower() |
989 | | - if aws_service_representation in ("false", "0"): |
| 998 | + if not config.aws_service_representation_enabled: |
990 | 999 | return fallback |
991 | 1000 |
|
992 | 1001 | # Use extracted_key if it exists and is not empty, otherwise use fallback |
@@ -1034,6 +1043,54 @@ def create_inferred_span_from_lambda_function_url_event(event, context): |
1034 | 1043 | return span |
1035 | 1044 |
|
1036 | 1045 |
|
| 1046 | +def create_inferred_span_from_alb_event(event, context): |
| 1047 | + request_context = event.get("requestContext") or {} |
| 1048 | + elb = request_context.get("elb") or {} |
| 1049 | + target_group_arn = elb.get("targetGroupArn") |
| 1050 | + |
| 1051 | + headers = resolve_multivalue_headers(event) |
| 1052 | + host = headers.get("host") |
| 1053 | + method = event.get("httpMethod") |
| 1054 | + path = event.get("path") |
| 1055 | + proto = headers.get("x-forwarded-proto", "http") |
| 1056 | + |
| 1057 | + # ALB has no api id; key the service mapping off the load-balancer host and |
| 1058 | + # fall back to it when DD_TRACE_AWS_SERVICE_REPRESENTATION_ENABLED is on. |
| 1059 | + service_name = determine_service_name(service_mapping, host, "lambda_alb", host) |
| 1060 | + |
| 1061 | + http_url = "%s://%s%s" % (proto, host, path) if host and path is not None else None |
| 1062 | + if method and path is not None: |
| 1063 | + resource = f"{method} {path}" |
| 1064 | + else: |
| 1065 | + resource = method or path |
| 1066 | + |
| 1067 | + tags = { |
| 1068 | + "operation_name": "aws.alb", |
| 1069 | + "span.kind": "server", |
| 1070 | + "http.method": method, |
| 1071 | + "http.url": http_url, |
| 1072 | + "http.useragent": headers.get("user-agent"), |
| 1073 | + "endpoint": path, |
| 1074 | + "resource_names": resource, |
| 1075 | + "request_id": context.aws_request_id, |
| 1076 | + "target_group_arn": target_group_arn, |
| 1077 | + } |
| 1078 | + # Drop tags we couldn't derive so the span never carries malformed values. |
| 1079 | + tags = {key: value for key, value in tags.items() if value is not None} |
| 1080 | + |
| 1081 | + tracer.set_tags(_dd_origin) |
| 1082 | + # ALB events carry no request timestamp (unlike API GW requestTimeEpoch / |
| 1083 | + # Function URL timeEpoch), so the span starts at handler time. |
| 1084 | + span = tracer.trace( |
| 1085 | + "aws.alb", service=service_name, resource=resource, span_type="http" |
| 1086 | + ) |
| 1087 | + InferredSpanInfo.set_tags(tags, tag_source="self", synchronicity="sync") |
| 1088 | + if span: |
| 1089 | + span.set_tags(tags) |
| 1090 | + span.set_metric(InferredSpanInfo.METRIC, 1.0) |
| 1091 | + return span |
| 1092 | + |
| 1093 | + |
1037 | 1094 | def is_api_gateway_invocation_async(event): |
1038 | 1095 | hdrs = event.get("headers") |
1039 | 1096 | if not hdrs: |
|
0 commit comments