Repository navigation
feat(boto3): Add SNS extension #8142
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
Merged
Merged
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
70dca2a
.
pabloDeputter 0f90423
wip
pabloDeputter 053099c
.
pabloDeputter c73d9ba
fix(boto3): Add `capture_internal_exceptions()` guard to `get_span_na…
pabloDeputter 61994bf
fix(boto3): Simplify error type handling in batch operations
pabloDeputter 6ccbcd8
wip
pabloDeputter 9235dd0
Merge branch 'major/3.0' into pablo/boto3-add-sqs-extension
pabloDeputter be20b35
Merge branch 'pablo/boto3-add-sqs-extension' into pablo/boto3-add-sns…
pabloDeputter 50a49d1
add "publish_batch" method requirement
pabloDeputter 4738824
fix
pabloDeputter c640799
fix(boto3): make sure span is sent
pabloDeputter f6c8099
Merge remote-tracking branch 'origin/major/3.0' into pablo/boto3-add-…
sentry-junior[bot] 0cb573d
Merge branch 'pablo/boto3-add-sqs-extension' into pablo/boto3-add-sns…
sentry-junior[bot] 48eae1c
ref(boto3): Guard SQS ARN and batch-count reads separately
sentry-junior[bot] 0e58846
Merge branch 'pablo/boto3-add-sqs-extension' into pablo/boto3-add-sns…
sentry-junior[bot] 54710b7
ref(boto3): Share the batch error.type code with SQS
sentry-junior[bot] 438ded1
merge major branch
alexander-alderman-webb 6588b5b
merge
alexander-alderman-webb 4d8e8f5
Merge branch 'major/3.0' into pablo/boto3-add-sns-extension
sentry-junior[bot] e53db10
fix(boto3): Guard SNS response attributes
sentry-junior[bot] ae87c83
fix(boto3): Guard SNS request attribute reads
sentry-junior[bot] a7efd55
test(boto3): Skip SNS invalid-params test when the operation is missing
sentry-junior[bot] 165dcf3
docs(boto3): Document SNS span op and name changes in migration guide
sentry-junior[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,36 +1,131 @@ | ||
| from typing import TYPE_CHECKING | ||
|
|
||
| import sentry_sdk | ||
| from sentry_sdk.consts import OP, SPANDATA | ||
| from sentry_sdk.integrations.boto3._services.base import _ServiceExtension | ||
| from sentry_sdk.integrations.boto3._utils import _get_aws_arn_attributes | ||
| from sentry_sdk.integrations.boto3._utils import ( | ||
| _get_aws_arn_attributes, | ||
| _get_batch_error_type, | ||
| ) | ||
| from sentry_sdk.traces import SpanStatus | ||
| from sentry_sdk.utils import capture_internal_exceptions | ||
|
|
||
| if TYPE_CHECKING: | ||
| from typing import Any | ||
| from typing import Any, Optional | ||
|
|
||
| from sentry_sdk._types import Attributes | ||
| from sentry_sdk.integrations.boto3._context import AwsCallContext | ||
| from sentry_sdk.traces import Span | ||
|
|
||
| _PUBLISH_OPERATIONS = ("Publish", "PublishBatch") | ||
|
|
||
|
|
||
| class _SNSExtension(_ServiceExtension): | ||
| def get_span_op(self, ctx: "AwsCallContext") -> "Optional[str]": | ||
| if ctx.operation_name in _PUBLISH_OPERATIONS: | ||
| return OP.QUEUE_PUBLISH | ||
| # defaults to `rpc` for other operations. | ||
| return None | ||
|
|
||
| def get_span_name( | ||
| self, ctx: "AwsCallContext", attributes: "Attributes" | ||
| ) -> "Optional[str]": | ||
| if ctx.operation_name not in _PUBLISH_OPERATIONS: | ||
| return None | ||
|
|
||
| destination = attributes.get(SPANDATA.MESSAGING_DESTINATION_NAME) | ||
| if destination is None and SPANDATA.SERVER_ADDRESS in attributes: | ||
| destination = f"{attributes[SPANDATA.SERVER_ADDRESS]}:{attributes[SPANDATA.SERVER_PORT]}" | ||
|
|
||
| # for example: `send <topic name>` when `messaging.operation.type=send` (always send). | ||
| # https://opentelemetry.io/docs/specs/semconv/messaging/messaging-spans/#span-name | ||
| return f"send {destination}" if destination else "send" | ||
|
|
||
| def get_request_attributes(self, ctx: "AwsCallContext") -> "Attributes": | ||
| attributes: "Attributes" = {} | ||
|
|
||
| # botocore validates params after this hook, so a malformed value must only | ||
| # lose the attributes read from it, not the span. | ||
| target_arn = ctx.params.get("TargetArn") | ||
| endpoint_arn = ctx.params.get("EndpointArn") | ||
| topic_arn = None | ||
| with capture_internal_exceptions(): | ||
| # `TargetArn` can be either a topic or a platform endpoint; a topic ARN | ||
| # should never contain ":endpoint/". | ||
| if target_arn and ":endpoint/" in target_arn: | ||
| endpoint_arn, target_arn = target_arn, None | ||
|
|
||
| # `TopicArn` and `ResourceArn` are always topic ARNs. | ||
| topic_arn = ( | ||
| ctx.params.get("TopicArn") | ||
| or ctx.params.get("ResourceArn") | ||
| or target_arn | ||
| ) | ||
|
|
||
| if topic_arn: | ||
| attributes[SPANDATA.AWS_SNS_TOPIC_ARN] = topic_arn | ||
| # `arn:aws:sns:eu-north-1:123456789012:alerts` -> `alerts` | ||
| topic_name = topic_arn.split(":")[-1] | ||
| attributes[SPANDATA.MESSAGING_DESTINATION_NAME] = topic_name | ||
| elif ctx.operation_name == "CreateTopic" and "Name" in ctx.params: | ||
| attributes[SPANDATA.MESSAGING_DESTINATION_NAME] = ctx.params["Name"] | ||
|
|
||
| if ctx.operation_name in _PUBLISH_OPERATIONS: | ||
| attributes.update( | ||
| _get_aws_arn_attributes( | ||
| ( | ||
| ctx.params.get("EndpointArn"), | ||
| ctx.params.get("PlatformApplicationArn"), | ||
| ctx.params.get("SubscriptionArn"), | ||
| ctx.params.get("TargetArn"), | ||
| ctx.params.get("TopicArn"), | ||
| ), | ||
| ("sns",), | ||
| ) | ||
| { | ||
| SPANDATA.SENTRY_KIND: "producer", | ||
| SPANDATA.MESSAGING_SYSTEM: "aws.sns", | ||
| SPANDATA.MESSAGING_OPERATION_TYPE: "send", | ||
| SPANDATA.MESSAGING_OPERATION_NAME: "publish", | ||
| } | ||
| ) | ||
|
|
||
| # phone numbers and device endpoints are PII. | ||
| if "PhoneNumber" in ctx.params or endpoint_arn: | ||
| attributes[SPANDATA.MESSAGING_DESTINATION_NAME] = "[Filtered]" | ||
| if "PublishBatchRequestEntries" in ctx.params: | ||
| with capture_internal_exceptions(): | ||
| attributes[SPANDATA.MESSAGING_BATCH_MESSAGE_COUNT] = len( | ||
| ctx.params["PublishBatchRequestEntries"] | ||
| ) | ||
|
|
||
| # if scrubbing fails, drop all ARN attributes rather than send a raw | ||
| # endpoint or subscription ARN. | ||
| with capture_internal_exceptions(): | ||
| # endpoints and subscriptions identify a single device or subscriber. | ||
| subscription_arn = ctx.params.get("SubscriptionArn") | ||
| if not sentry_sdk.get_client().options["data_collection"]["user_info"]: | ||
| if endpoint_arn: | ||
| endpoint_arn = endpoint_arn.rsplit("/", 1)[0].replace( | ||
| ":endpoint/", ":app/", 1 | ||
| ) | ||
| if subscription_arn: | ||
| subscription_arn = subscription_arn.rsplit(":", 1)[0] | ||
|
|
||
| arns = ( | ||
| ctx.params.get("PlatformApplicationArn"), | ||
| topic_arn, | ||
| endpoint_arn, | ||
| subscription_arn, | ||
| ) | ||
| attributes.update(_get_aws_arn_attributes(arns, ("sns",))) | ||
| return attributes | ||
|
|
||
| def on_success_response( | ||
| self, ctx: "AwsCallContext", response: "Any", span: "Span" | ||
| ) -> None: | ||
| attributes: "Attributes" = {} | ||
|
|
||
| # the call already succeeded; an unexpected response shape, e.g. from a | ||
| # `Stubber` or an SNS-compatible service, must only lose these attributes. | ||
| with capture_internal_exceptions(): | ||
| if ctx.operation_name == "Publish" and "MessageId" in response: | ||
| attributes[SPANDATA.MESSAGING_MESSAGE_ID] = response["MessageId"] | ||
|
|
||
| error_type = _get_batch_error_type(response) | ||
| if error_type is not None: | ||
| span.status = SpanStatus.ERROR | ||
| attributes[SPANDATA.ERROR_TYPE] = error_type | ||
|
|
||
| span.set_attributes(attributes) | ||
| span.end() | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.