Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
134 changes: 88 additions & 46 deletions sentry_sdk/integrations/pymongo.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
import json

import sentry_sdk
from sentry_sdk.consts import SPANSTATUS, SPANDATA, OP
from sentry_sdk.consts import OP, SPANDATA, SPANSTATUS
from sentry_sdk.integrations import DidNotEnable, Integration
from sentry_sdk.scope import should_send_default_pii
from sentry_sdk.traces import SpanStatus, StreamedSpan
from sentry_sdk.tracing import Span
from sentry_sdk.tracing_utils import has_span_streaming_enabled
from sentry_sdk.utils import capture_internal_exceptions

try:
Expand Down Expand Up @@ -107,7 +109,7 @@ def _get_db_data(event: "Any") -> "Dict[str, Any]":

class CommandTracer(monitoring.CommandListener):
def __init__(self) -> None:
self._ongoing_operations: "Dict[int, Span]" = {}
self._ongoing_operations: "Dict[int, Union[Span, StreamedSpan]]" = {}

def _operation_key(
self,
Expand All @@ -116,7 +118,8 @@ def _operation_key(
return event.request_id

def started(self, event: "CommandStartedEvent") -> None:
if sentry_sdk.get_client().get_integration(PyMongoIntegration) is None:
client = sentry_sdk.get_client()
if client.get_integration(PyMongoIntegration) is None:
return

with capture_internal_exceptions():
Expand All @@ -126,56 +129,87 @@ def started(self, event: "CommandStartedEvent") -> None:
command.pop("$clusterTime", None)
command.pop("$signature", None)

tags = {
"db.name": event.database_name,
SPANDATA.DB_SYSTEM: "mongodb",
SPANDATA.DB_DRIVER_NAME: "pymongo",
SPANDATA.DB_OPERATION: event.command_name,
SPANDATA.DB_MONGODB_COLLECTION: command.get(event.command_name),
}

try:
Comment thread
ericapisani marked this conversation as resolved.
tags["net.peer.name"] = event.connection_id[0]
tags["net.peer.port"] = str(event.connection_id[1])
except TypeError:
pass

data: "Dict[str, Any]" = {"operation_ids": {}}
data["operation_ids"]["operation"] = event.operation_id
data["operation_ids"]["request"] = event.request_id

data.update(_get_db_data(event))

try:
lsid = command.pop("lsid")["id"]
data["operation_ids"]["session"] = str(lsid)
except KeyError:
pass
db_data = _get_db_data(event)

lsid = command.pop("lsid", None)
if not should_send_default_pii():
command = _strip_pii(command)

query = json.dumps(command, default=str)
span = sentry_sdk.start_span(
op=OP.DB,
name=query,
origin=PyMongoIntegration.origin,
)

for tag, value in tags.items():
# set the tag for backwards-compatibility.
# TODO: remove the set_tag call in the next major release!
span.set_tag(tag, value)
if has_span_streaming_enabled(client.options):
span_first_data = {
"db.name": event.database_name,
SPANDATA.DB_SYSTEM: "mongodb",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
SPANDATA.DB_SYSTEM: "mongodb",
SPANDATA.DB_SYSTEM_NAME: "mongodb",

SPANDATA.DB_DRIVER_NAME: "pymongo",
SPANDATA.DB_OPERATION: event.command_name,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
SPANDATA.DB_OPERATION: event.command_name,
SPANDATA.DB_OPERATION_NAME: event.command_name,

https://getsentry.github.io/sentry-conventions/attributes/db/#db-operation

"db.collection.name": command.get(event.command_name),
"sentry.op": OP.DB,
"sentry.origin": PyMongoIntegration.origin,
**db_data,
}

span = sentry_sdk.traces.start_span(
name=query, attributes=span_first_data
)

with capture_internal_exceptions():
sentry_sdk.add_breadcrumb(
message=query,
category="query",
type=OP.DB,
data=span_first_data,
)

else:
tags = {
"db.name": event.database_name,
SPANDATA.DB_SYSTEM: "mongodb",
SPANDATA.DB_DRIVER_NAME: "pymongo",
SPANDATA.DB_OPERATION: event.command_name,
# The below is a deprecated field, but leaving for legacy reasons.
# The v2 spans will use `db.collection.name` instead.
SPANDATA.DB_MONGODB_COLLECTION: command.get(event.command_name),
}

try:
tags["net.peer.name"] = event.connection_id[0]
tags["net.peer.port"] = str(event.connection_id[1])
except TypeError:
pass

data: "Dict[str, Any]" = {"operation_ids": {}}
data["operation_ids"]["operation"] = event.operation_id
data["operation_ids"]["request"] = event.request_id
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are also kept for legacy reasons, are being removed for streamed spans.


data.update(db_data)

try:
if lsid:
lsid_id = lsid["id"]
data["operation_ids"]["session"] = str(lsid_id)
except KeyError:
pass

span = sentry_sdk.start_span(
op=OP.DB,
name=query,
origin=PyMongoIntegration.origin,
)

span.set_data(tag, value)
for tag, value in tags.items():
# set the tag for backwards-compatibility.
# TODO: remove the set_tag call in the next major release!
span.set_tag(tag, value)
span.set_data(tag, value)

for key, value in data.items():
span.set_data(key, value)
for key, value in data.items():
span.set_data(key, value)

with capture_internal_exceptions():
sentry_sdk.add_breadcrumb(
message=query, category="query", type=OP.DB, data=tags
)
with capture_internal_exceptions():
sentry_sdk.add_breadcrumb(
message=query, category="query", type=OP.DB, data=tags
)

self._ongoing_operations[self._operation_key(event)] = span.__enter__()

Expand All @@ -185,7 +219,11 @@ def failed(self, event: "CommandFailedEvent") -> None:

try:
span = self._ongoing_operations.pop(self._operation_key(event))
span.set_status(SPANSTATUS.INTERNAL_ERROR)
# Ignoring NoOpStreamedSpan as it will always have a status of "ok"
if type(span) is StreamedSpan:
span.status = SpanStatus.ERROR
elif type(span) is Span:
span.set_status(SPANSTATUS.INTERNAL_ERROR)
span.__exit__(None, None, None)
except KeyError:
return
Expand All @@ -196,7 +234,11 @@ def succeeded(self, event: "CommandSucceededEvent") -> None:

try:
span = self._ongoing_operations.pop(self._operation_key(event))
span.set_status(SPANSTATUS.OK)
# Ignoring NoOpStreamedSpan as it will always have a status of "ok"
if type(span) is StreamedSpan:
span.status = SpanStatus.OK
Comment on lines +238 to +239
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK is the default value for StreamedSpans, so technically we don't need to set it here

elif type(span) is Span:
span.set_status(SPANSTATUS.OK)
span.__exit__(None, None, None)
except KeyError:
pass
Expand Down
Loading
Loading