-
Notifications
You must be signed in to change notification settings - Fork 45
Infer API Gateway spans #172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
19f9da3
c4cedfa
2cfaee7
e077667
c1f11ee
0cd45fd
40c79ca
c4d751e
96b7844
586033d
7d91108
2605f2e
4ecc91d
ceea1cc
f180439
ef9439c
07d2e97
99ca466
dd7840e
33274e3
8503277
279ffe4
a580e45
df376c9
3a3cc4e
e277053
657ee36
a6170ff
e75a3a0
5bd5cb5
38b6987
a1c0b9d
b325ee7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,9 +21,14 @@ | |
from ddtrace import __version__ as ddtrace_version | ||
from ddtrace.propagation.http import HTTPPropagator | ||
from datadog_lambda import __version__ as datadog_lambda_version | ||
from datadog_lambda.trigger import parse_event_source, EventTypes, EventSubtypes | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
SPAN_TYPE_TAG = "_dd.span_type" | ||
SPAN_TYPE_INFERRED = "inferred" | ||
|
||
dd_trace_context = {} | ||
dd_tracing_enabled = os.environ.get("DD_TRACE_ENABLED", "false").lower() == "true" | ||
|
||
|
@@ -377,13 +382,121 @@ def set_dd_trace_py_root(trace_context_source, merge_xray_traces): | |
) | ||
|
||
|
||
def create_inferred_span(event, context, function_name): | ||
event_source = parse_event_source(event) | ||
try: | ||
if event_source.equals( | ||
EventTypes.API_GATEWAY, subtype=EventSubtypes.API_GATEWAY | ||
): | ||
logger.debug("API Gateway event detected. Inferring a span") | ||
return create_inferred_span_from_api_gateway_event(event, context) | ||
elif event_source.equals( | ||
EventTypes.API_GATEWAY, subtype=EventSubtypes.HTTP_API | ||
): | ||
logger.debug("HTTP API event detected. Inferring a span") | ||
return create_inferred_span_from_http_api_event(event, context) | ||
elif event_source.equals( | ||
EventTypes.API_GATEWAY, subtype=EventSubtypes.WEBSOCKET | ||
): | ||
logger.debug("API Gateway Websocket event detected. Inferring a span") | ||
return create_inferred_span_from_api_gateway_websocket_event(event, context) | ||
except Exception as e: | ||
logger.debug( | ||
"Unable to infer span. Detected type: {}. Reason: {}", | ||
event_source.to_string(), | ||
e, | ||
) | ||
return None | ||
logger.debug("Unable to infer a span: unknown event type") | ||
return None | ||
|
||
|
||
def create_inferred_span_from_api_gateway_websocket_event(event, context): | ||
domain = event["requestContext"]["domainName"] | ||
endpoint = event["requestContext"]["routeKey"] | ||
tags = { | ||
"operation_name": "aws.apigateway.websocket", | ||
"service.name": domain, | ||
"http.url": domain + endpoint, | ||
"endpoint": endpoint, | ||
"resource_name": domain + endpoint, | ||
"request_id": context.aws_request_id, | ||
"connection_id": event["requestContext"]["connectionId"], | ||
SPAN_TYPE_TAG: SPAN_TYPE_INFERRED, | ||
} | ||
request_time_epoch = event["requestContext"]["requestTimeEpoch"] | ||
args = { | ||
"resource": domain + endpoint, | ||
"span_type": "web", | ||
} | ||
tracer.set_tags({"_dd.origin": "lambda"}) | ||
span = tracer.trace("aws.apigateway.websocket", **args) | ||
if span: | ||
span.set_tags(tags) | ||
span.start = request_time_epoch / 1000 | ||
return span | ||
|
||
|
||
def create_inferred_span_from_api_gateway_event(event, context): | ||
domain = event["requestContext"]["domainName"] | ||
path = event["path"] | ||
tags = { | ||
"operation_name": "aws.apigateway.rest", | ||
"service.name": domain, | ||
"http.url": domain + path, | ||
"endpoint": path, | ||
"http.method": event["httpMethod"], | ||
"resource_name": domain + path, | ||
"request_id": context.aws_request_id, | ||
SPAN_TYPE_TAG: SPAN_TYPE_INFERRED, | ||
} | ||
request_time_epoch = event["requestContext"]["requestTimeEpoch"] | ||
args = { | ||
"resource": domain + path, | ||
"span_type": "http", | ||
} | ||
tracer.set_tags({"_dd.origin": "lambda"}) | ||
span = tracer.trace("aws.apigateway", **args) | ||
if span: | ||
span.set_tags(tags) | ||
span.start = request_time_epoch / 1000 | ||
return span | ||
|
||
|
||
def create_inferred_span_from_http_api_event(event, context): | ||
domain = event["requestContext"]["domainName"] | ||
path = event["rawPath"] | ||
tags = { | ||
"operation_name": "aws.httpapi", | ||
"service.name": domain, | ||
"http.url": domain + path, | ||
"endpoint": path, | ||
"http.method": event["requestContext"]["http"]["method"], | ||
"resource_name": domain + path, | ||
"request_id": context.aws_request_id, | ||
SPAN_TYPE_TAG: SPAN_TYPE_INFERRED, | ||
} | ||
request_time_epoch = event["requestContext"]["timeEpoch"] | ||
args = { | ||
"resource": domain + path, | ||
"span_type": "http", | ||
} | ||
tracer.set_tags({"_dd.origin": "lambda"}) | ||
span = tracer.trace("aws.httpapi", **args) | ||
if span: | ||
span.set_tags(tags) | ||
span.start = request_time_epoch / 1000 | ||
return span | ||
|
||
|
||
def create_function_execution_span( | ||
context, | ||
function_name, | ||
is_cold_start, | ||
trace_context_source, | ||
merge_xray_traces, | ||
trigger_tags, | ||
upstream=None, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is "parent" a better name? |
||
): | ||
tags = {} | ||
if context: | ||
|
@@ -402,6 +515,7 @@ def create_function_execution_span( | |
else None, | ||
"datadog_lambda": datadog_lambda_version, | ||
"dd_trace": ddtrace_version, | ||
"span.name": "aws.lambda", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you need this? Doesn't |
||
} | ||
if trace_context_source == TraceContextSource.XRAY and merge_xray_traces: | ||
tags["_dd.parent_source"] = trace_context_source | ||
|
@@ -415,4 +529,6 @@ def create_function_execution_span( | |
span = tracer.trace("aws.lambda", **args) | ||
if span: | ||
span.set_tags(tags) | ||
if upstream: | ||
span.parent_id = upstream.span_id | ||
return span |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this actually true? I know we said we wouldn't do any specific work to make it function in the forwarder, but the span will still get generated. I thin originally I said the forwarder might remap the service name, but I don't know if we verified that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We did verify it. The forwarder adds tags from the tags cache to the trace payloads it sends back. If we want, we can always fix this and add forwarder support later on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, i guess the forwarder applies the service tag on all the trace payloads from the same event object? if so, even the trace payload carrying the inferred span has no lambda function arn, it still assumes it's from the same function with other payloads and apply the tag? If yes, we would need to make some changes in the forwarder, hopefully a small one.