Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
30d436a
wip
pabloDeputter Oct 19, 2026
d5767c4
fix wrong parent ID
pabloDeputter Oct 23, 2026
ae57d8b
wip
pabloDeputter Oct 23, 2026
0266db9
Merge branch 'major/3.0' into pablo/boto3-add-lambda-extension
pabloDeputter Oct 27, 2026
7ff8eb7
Merge branch 'major/3.0' into pablo/boto3-add-lambda-extension
pabloDeputter Oct 28, 2026
7ea5204
feat(aws-lambda): Continue the trace from Invoke ClientContext
sentry-junior[bot] Nov 9, 2026
61bf998
feat(aws-lambda): Detect Lambda triggers
sentry-junior[bot] Nov 9, 2026
f864c51
ref(aws-lambda): Don't set messaging.batch.message_count on the Lambd…
sentry-junior[bot] Nov 9, 2026
120a441
fix(aws-lambda): Don't assume the context has client_context
sentry-junior[bot] Nov 9, 2026
7118a80
Merge branch 'junior/aws-lambda-invoke-trace-continuation' into junio…
sentry-junior[bot] Nov 9, 2026
d877f00
test(aws-lambda): Keep one test_trigger case per code path
sentry-junior[bot] Nov 9, 2026
e58c212
Merge remote-tracking branch 'origin/major/3.0' into junior/aws-lambd…
sentry-junior[bot] Nov 11, 2026
1b7058c
Merge remote-tracking branch 'origin/major/3.0' into junior/aws-lambd…
sentry-junior[bot] Nov 11, 2026
380eec4
Merge branch 'junior/aws-lambda-invoke-trace-continuation' into junio…
sentry-junior[bot] Nov 11, 2026
10e4fcd
Merge remote-tracking branch 'origin/major/3.0' into junior/aws-lambd…
sentry-junior[bot] Nov 12, 2026
8fad3d8
fix(aws-lambda): Detect ActiveMQ triggers sent as aws:amq
sentry-junior[bot] Nov 12, 2026
2a29e8e
ref(aws-lambda): Leave messaging.batch.message_count to #8229
sentry-junior[bot] Nov 12, 2026
151b3a4
Merge remote-tracking branch 'origin/major/3.0' into junior/aws-lambd…
sentry-junior[bot] Nov 12, 2026
6592fcd
ref(aws-lambda): Inline records source lookup, drop _TRIGGERS annotation
sentry-junior[bot] Nov 12, 2026
61c3b58
test(aws-lambda): Use constants for trigger test events
sentry-junior[bot] Nov 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions sentry_sdk/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,12 @@ class SPANDATA:
Example: "my_function"
"""

FAAS_TRIGGER = "faas.trigger"
"""
The type of the trigger on which the function is executed.
Example: "pubsub"
"""

FAAS_VERSION = "faas.version"
"""
The version of the function that was invoked.
Expand Down
4 changes: 4 additions & 0 deletions sentry_sdk/integrations/aws_lambda/_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
_get_user_from_event,
_make_request_event_processor,
)
from sentry_sdk.integrations.aws_lambda._trigger import _get_trigger
from sentry_sdk.integrations.aws_lambda.consts import (
CLOUD_PLATFORM,
CLOUD_PROVIDER,
Expand Down Expand Up @@ -231,6 +232,9 @@ def sentry_handler(
**additional_attributes,
}

with capture_internal_exceptions():
attributes[SPANDATA.FAAS_TRIGGER] = _get_trigger(aws_event)

with capture_internal_exceptions():
invoked_function_arn = aws_context.invoked_function_arn
function_version = aws_context.function_version
Expand Down
88 changes: 88 additions & 0 deletions sentry_sdk/integrations/aws_lambda/_trigger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from typing import Any

# `faas.trigger` of each `eventSource` of `Records` or of a top-level `eventSource`.
# https://docs.aws.amazon.com/lambda/latest/dg/lambda-services.html
_TRIGGERS = {
"aws:sqs": "pubsub",
"aws:sns": "pubsub",
"aws:kinesis": "pubsub",
"aws:ses": "pubsub",
"aws:kafka": "pubsub",
"SelfManagedKafka": "pubsub",
# ActiveMQ: the Lambda docs show `aws:mq`, real deliveries use `aws:amq`.
"aws:mq": "pubsub",
Comment thread
sentry-junior[bot] marked this conversation as resolved.
Resolved
"aws:amq": "pubsub",
"aws:rmq": "pubsub",
"aws:s3": "datasource",
"aws:dynamodb": "datasource",
"aws:docdb": "datasource",
}

# https://docs.aws.amazon.com/eventbridge/latest/ref/overiew-event-structure.html
_EVENTBRIDGE_KEYS = frozenset(
(
"version",
"id",
"detail-type",
"source",
"account",
"time",
"region",
"resources",
"detail",
)
)


def _is_appsync_event(aws_event: "Any") -> bool:
# https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference-js.html
return (
isinstance(aws_event, dict)
and "arguments" in aws_event
and "fieldName" in aws_event.get("info", {})
)


def _get_trigger(aws_event: "Any") -> str:
"""`faas.trigger` from the envelope of the delivered event, never from its payload."""
if isinstance(aws_event, list):
# an AppSync batch resolver delivers a list of resolver events.
if aws_event and all(_is_appsync_event(event) for event in aws_event):
return "http"
return "other"
Comment thread
sentry-junior[bot] marked this conversation as resolved.
Resolved

if not isinstance(aws_event, dict):
return "other"

# API Gateway REST APIs and ALB send `httpMethod`, HTTP APIs and function URLs
# `requestContext.http`.
request_context = aws_event.get("requestContext")
if request_context is not None and (
"httpMethod" in aws_event or "http" in request_context
):
return "http"

if _is_appsync_event(aws_event):
return "http"

records = aws_event.get("Records")
if records:
# CloudFront and Lambda@Edge records have no `eventSource`.
if "cf" in records[0]:
return "http"
# SNS spells it `EventSource`.
source = records[0].get("eventSource") or records[0].get("EventSource")
return _TRIGGERS.get(source, "other")

if _EVENTBRIDGE_KEYS <= aws_event.keys():
Comment thread
sentry-junior[bot] marked this conversation as resolved.
Resolved
if (
aws_event["source"] == "aws.events"
and aws_event["detail-type"] == "Scheduled Event"
):
return "timer"
return "pubsub"
Comment thread
sentry-junior[bot] marked this conversation as resolved.
Resolved

return _TRIGGERS.get(aws_event.get("eventSource", ""), "other")
94 changes: 94 additions & 0 deletions tests/integrations/aws_lambda/test_aws_lambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,32 @@
SAM_TEMPLATE_FILE = "sam.template.yaml"
SAM_SHUTDOWN_TIMEOUT = 10

SQS_RECORD = {
"messageId": "059f36b4-87a3-44ab-83d2-661975830a7d",
"body": "{}",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:us-east-1:012345678912:orders",
}
SNS_RECORD = {
"EventSource": "aws:sns",
"Sns": {"TopicArn": "arn:aws:sns:us-east-1:012345678912:alerts"},
}
APPSYNC_EVENT = {
"arguments": {"id": "1"},
"info": {"fieldName": "getOrder", "parentTypeName": "Query"},
}
EVENTBRIDGE_EVENT = {
"version": "0",
"id": "53dc4d37-cffa-4f76-80c9-8b7d4a4d2eaa",
"detail-type": "Object Created",
"source": "aws.s3",
"account": "012345678912",
"time": "2026-10-26T12:00:00Z",
"region": "us-east-1",
"resources": [],
"detail": {},
}


@pytest.fixture(scope="session", autouse=True)
def test_environment():
Expand Down Expand Up @@ -767,6 +793,7 @@ def test_no_error(lambda_client, test_environment):
assert _get_span_attr(attrs, SPANDATA.AWS_LOG_STREAM_NAMES) == [
LATEST_FUNCTION_VERSION
]
assert _get_span_attr(attrs, SPANDATA.FAAS_TRIGGER) == "other"


def test_error(lambda_client, test_environment):
Expand Down Expand Up @@ -820,6 +847,73 @@ def test_error(lambda_client, test_environment):
assert _get_span_attr(attrs, SPANDATA.AWS_LOG_STREAM_NAMES) == [
LATEST_FUNCTION_VERSION
]
assert _get_span_attr(attrs, SPANDATA.FAAS_TRIGGER) == "other"


@pytest.mark.parametrize(
"aws_event,trigger",
[
pytest.param(
{"httpMethod": "GET", "path": "/", "requestContext": {"stage": "prod"}},
"http",
id="api-gateway-rest",
),
pytest.param(
{
"version": "2.0",
"routeKey": "GET /",
"requestContext": {"http": {"method": "GET"}},
},
"http",
id="api-gateway-http",
),
pytest.param(
{"Records": [{"cf": {"request": {"uri": "/"}}}]},
"http",
id="cloudfront",
),
pytest.param(APPSYNC_EVENT, "http", id="appsync"),
pytest.param([APPSYNC_EVENT, APPSYNC_EVENT], "http", id="appsync-batch"),
pytest.param({"Records": [SQS_RECORD, SQS_RECORD]}, "pubsub", id="sqs"),
pytest.param({"Records": [SNS_RECORD]}, "pubsub", id="sns"),
pytest.param(
{"eventSource": "aws:kafka", "records": {"orders-0": [{}]}},
"pubsub",
id="msk",
),
pytest.param(
EVENTBRIDGE_EVENT,
"pubsub",
id="eventbridge",
),
pytest.param(
{
**EVENTBRIDGE_EVENT,
"source": "aws.events",
"detail-type": "Scheduled Event",
},
"timer",
id="eventbridge-schedule",
),
pytest.param({"Records": [{"eventSource": "aws:s3"}]}, "datasource", id="s3"),
pytest.param(
[{"good dog": "Maisey"}, {"good dog": "Charlie"}],
"other",
id="list",
),
],
)
def test_trigger(lambda_client, test_environment, aws_event, trigger):
lambda_client.invoke(
FunctionName="BasicOkSpanStreaming",
Payload=json.dumps(aws_event),
)

(segment_span,) = [
s for s in test_environment["server"].span_items if s["is_segment"]
]
attrs = segment_span["attributes"]
assert _get_span_attr(attrs, SPANDATA.FAAS_TRIGGER) == trigger


def test_trace_continuation(lambda_client, test_environment):
Expand Down
Loading