Skip to content
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

Adds DB connection attributes in spans #2274

Merged
merged 27 commits into from
Jul 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
62c713c
Added connection data to sqlalchemy spans.
antonpirker Jul 27, 2023
f8dcc41
Add connection data for mongo db spans
antonpirker Jul 27, 2023
2590506
Add connection data to django db spans.
antonpirker Jul 27, 2023
97ee7e5
Cleanup
antonpirker Jul 27, 2023
62d31c2
Cleanup
antonpirker Jul 27, 2023
ac3e3f5
Added db connection data to redis spans
antonpirker Jul 27, 2023
561c50f
Fixed tests
antonpirker Jul 27, 2023
41182c0
Made redis db name a string
antonpirker Jul 27, 2023
0163e8b
Fixed some tests and some redis tinkering
antonpirker Jul 28, 2023
5fcdee7
Reverted redis changes (will do in a separate pr)
antonpirker Jul 28, 2023
a47b993
Fixing hosts in tests
antonpirker Jul 28, 2023
a064190
Fixing tests
antonpirker Jul 28, 2023
c7aa223
Fixing tests
antonpirker Jul 28, 2023
e6991b9
Updated chalice versions
antonpirker Jul 28, 2023
c0330e4
Updated chalice deps
antonpirker Jul 28, 2023
6da2894
Removed mockupdb from general deps because it is only used in mongodb…
antonpirker Jul 28, 2023
23581da
Do not test newest chalice versions
antonpirker Jul 28, 2023
5e3a07f
Better fetching of db attributes
antonpirker Jul 28, 2023
dfa12a4
fix
antonpirker Jul 28, 2023
0be3b1b
fix tests
antonpirker Jul 28, 2023
8e9c8ba
remove old unused test args
sentrivana Jul 28, 2023
dd83203
Merge branch 'master' into antonpirker/2270-db-connection-attributes-…
sentrivana Jul 28, 2023
f0f9770
ok, maybe they were not unused
sentrivana Jul 28, 2023
2914c0c
add django test
sentrivana Jul 28, 2023
ce0089e
hello, github?
sentrivana Jul 28, 2023
b170287
fix test
sentrivana Jul 28, 2023
50735ce
github having a day again
sentrivana Jul 28, 2023
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
31 changes: 31 additions & 0 deletions sentry_sdk/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ class SPANDATA:
See: https://develop.sentry.dev/sdk/performance/span-data-conventions/
"""

DB_NAME = "db.name"
"""
The name of the database being accessed. For commands that switch the database, this should be set to the target database (even if the command fails).
Example: myDatabase
"""

DB_OPERATION = "db.operation"
"""
The name of the operation being executed, e.g. the MongoDB command name such as findAndModify, or the SQL keyword.
Expand Down Expand Up @@ -118,6 +124,31 @@ class SPANDATA:
Example: 418
"""

SERVER_ADDRESS = "server.address"
"""
Name of the database host.
Example: example.com
"""

SERVER_PORT = "server.port"
"""
Logical server port number
Example: 80; 8080; 443
"""

SERVER_SOCKET_ADDRESS = "server.socket.address"
"""
Physical server IP address or Unix socket address.
Example: 10.5.3.2
"""

SERVER_SOCKET_PORT = "server.socket.port"
"""
Physical server port.
Recommended: If different than server.port.
Example: 16456
"""


class OP:
CACHE_GET_ITEM = "cache.get_item"
Expand Down
28 changes: 21 additions & 7 deletions sentry_sdk/integrations/django/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,7 @@
with record_sql_queries(
hub, self.cursor, sql, params, paramstyle="format", executemany=False
) as span:
_set_db_system_on_span(span, self.db.vendor)
_set_db_data(span, self.db.vendor, self.db.get_connection_params())

Check warning on line 615 in sentry_sdk/integrations/django/__init__.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/__init__.py#L615

Added line #L615 was not covered by tests
return real_execute(self, sql, params)

def executemany(self, sql, param_list):
Expand All @@ -624,7 +624,7 @@
with record_sql_queries(
hub, self.cursor, sql, param_list, paramstyle="format", executemany=True
) as span:
_set_db_system_on_span(span, self.db.vendor)
_set_db_data(span, self.db.vendor, self.db.get_connection_params())

Check warning on line 627 in sentry_sdk/integrations/django/__init__.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/__init__.py#L627

Added line #L627 was not covered by tests
return real_executemany(self, sql, param_list)

def connect(self):
Expand All @@ -637,7 +637,7 @@
hub.add_breadcrumb(message="connect", category="query")

with hub.start_span(op=OP.DB, description="connect") as span:
_set_db_system_on_span(span, self.vendor)
_set_db_data(span, self.vendor, self.get_connection_params())

Check warning on line 640 in sentry_sdk/integrations/django/__init__.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/__init__.py#L640

Added line #L640 was not covered by tests
return real_connect(self)

CursorWrapper.execute = execute
Expand All @@ -646,8 +646,22 @@
ignore_logger("django.db.backends")


# https://github.com/django/django/blob/6a0dc2176f4ebf907e124d433411e52bba39a28e/django/db/backends/base/base.py#L29
# Avaliable in Django 1.8+
def _set_db_system_on_span(span, vendor):
# type: (Span, str) -> None
def _set_db_data(span, vendor, connection_params):

Check warning on line 649 in sentry_sdk/integrations/django/__init__.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/__init__.py#L649

Added line #L649 was not covered by tests
# type: (Span, str, Dict[str, str]) -> None
span.set_data(SPANDATA.DB_SYSTEM, vendor)

db_name = connection_params.get("dbname") or connection_params.get("database")

Check warning on line 653 in sentry_sdk/integrations/django/__init__.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/__init__.py#L653

Added line #L653 was not covered by tests
if db_name is not None:
span.set_data(SPANDATA.DB_NAME, db_name)

Check warning on line 655 in sentry_sdk/integrations/django/__init__.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/__init__.py#L655

Added line #L655 was not covered by tests

server_address = connection_params.get("host")

Check warning on line 657 in sentry_sdk/integrations/django/__init__.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/__init__.py#L657

Added line #L657 was not covered by tests
if server_address is not None:
span.set_data(SPANDATA.SERVER_ADDRESS, server_address)

Check warning on line 659 in sentry_sdk/integrations/django/__init__.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/__init__.py#L659

Added line #L659 was not covered by tests

server_port = connection_params.get("port")

Check warning on line 661 in sentry_sdk/integrations/django/__init__.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/__init__.py#L661

Added line #L661 was not covered by tests
if server_port is not None:
span.set_data(SPANDATA.SERVER_PORT, server_port)

Check warning on line 663 in sentry_sdk/integrations/django/__init__.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/__init__.py#L663

Added line #L663 was not covered by tests

server_socket_address = connection_params.get("unix_socket")

Check warning on line 665 in sentry_sdk/integrations/django/__init__.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/__init__.py#L665

Added line #L665 was not covered by tests
if server_socket_address is not None:
span.set_data(SPANDATA.SERVER_SOCKET_ADDRESS, server_socket_address)

Check warning on line 667 in sentry_sdk/integrations/django/__init__.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/__init__.py#L667

Added line #L667 was not covered by tests
25 changes: 23 additions & 2 deletions sentry_sdk/integrations/pymongo.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,27 @@ def _strip_pii(command):
return command


def _get_db_data(event):
# type: (Any) -> Dict[str, Any]
data = {}

data[SPANDATA.DB_SYSTEM] = "mongodb"

db_name = event.database_name
if db_name is not None:
data[SPANDATA.DB_NAME] = db_name

server_address = event.connection_id[0]
if server_address is not None:
data[SPANDATA.SERVER_ADDRESS] = server_address

server_port = event.connection_id[1]
if server_port is not None:
data[SPANDATA.SERVER_PORT] = server_port

return data


class CommandTracer(monitoring.CommandListener):
def __init__(self):
# type: () -> None
Expand Down Expand Up @@ -121,10 +142,10 @@ def started(self, event):
pass

data = {"operation_ids": {}} # type: Dict[str, Any]

data["operation_ids"]["operation"] = event.operation_id
data["operation_ids"]["request"] = event.request_id
data[SPANDATA.DB_SYSTEM] = "mongodb"

data.update(_get_db_data(event))

try:
lsid = command.pop("lsid")["id"]
Expand Down
23 changes: 20 additions & 3 deletions sentry_sdk/integrations/sqlalchemy.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,7 @@
span = ctx_mgr.__enter__()

if span is not None:
db_system = _get_db_system(conn.engine.name)
if db_system is not None:
span.set_data(SPANDATA.DB_SYSTEM, db_system)
_set_db_data(span, conn)

Check warning on line 70 in sentry_sdk/integrations/sqlalchemy.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/sqlalchemy.py#L70

Added line #L70 was not covered by tests
context._sentry_sql_span = span


Expand Down Expand Up @@ -128,3 +126,22 @@
return "oracle"

return None


def _set_db_data(span, conn):

Check warning on line 131 in sentry_sdk/integrations/sqlalchemy.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/sqlalchemy.py#L131

Added line #L131 was not covered by tests
# type: (Span, Any) -> None
db_system = _get_db_system(conn.engine.name)

Check warning on line 133 in sentry_sdk/integrations/sqlalchemy.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/sqlalchemy.py#L133

Added line #L133 was not covered by tests
if db_system is not None:
span.set_data(SPANDATA.DB_SYSTEM, db_system)

Check warning on line 135 in sentry_sdk/integrations/sqlalchemy.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/sqlalchemy.py#L135

Added line #L135 was not covered by tests

db_name = conn.engine.url.database

Check warning on line 137 in sentry_sdk/integrations/sqlalchemy.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/sqlalchemy.py#L137

Added line #L137 was not covered by tests
if db_name is not None:
span.set_data(SPANDATA.DB_NAME, db_name)

Check warning on line 139 in sentry_sdk/integrations/sqlalchemy.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/sqlalchemy.py#L139

Added line #L139 was not covered by tests

server_address = conn.engine.url.host

Check warning on line 141 in sentry_sdk/integrations/sqlalchemy.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/sqlalchemy.py#L141

Added line #L141 was not covered by tests
if server_address is not None:
span.set_data(SPANDATA.SERVER_ADDRESS, server_address)

Check warning on line 143 in sentry_sdk/integrations/sqlalchemy.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/sqlalchemy.py#L143

Added line #L143 was not covered by tests

server_port = conn.engine.url.port

Check warning on line 145 in sentry_sdk/integrations/sqlalchemy.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/sqlalchemy.py#L145

Added line #L145 was not covered by tests
if server_port is not None:
span.set_data(SPANDATA.SERVER_PORT, server_port)

Check warning on line 147 in sentry_sdk/integrations/sqlalchemy.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/sqlalchemy.py#L147

Added line #L147 was not covered by tests
1 change: 0 additions & 1 deletion test-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,3 @@ asttokens
responses
pysocks
ipdb
mockupdb
56 changes: 42 additions & 14 deletions tests/integrations/django/test_basic.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from __future__ import absolute_import

import json
import os
import random
import re
import pytest
import random
from functools import partial

from werkzeug.test import Client
Expand Down Expand Up @@ -584,9 +585,7 @@ def test_django_connect_trace(sentry_init, client, capture_events, render_span_t

@pytest.mark.forked
@pytest_mark_django_db_decorator(transaction=True)
def test_django_connect_breadcrumbs(
sentry_init, client, capture_events, render_span_tree
):
def test_django_connect_breadcrumbs(sentry_init, capture_events):
"""
Verify we record a breadcrumb when opening a new database.
"""
Expand Down Expand Up @@ -620,6 +619,43 @@ def test_django_connect_breadcrumbs(
]


@pytest.mark.forked
@pytest_mark_django_db_decorator(transaction=True)
def test_db_connection_span_data(sentry_init, client, capture_events):
sentry_init(
integrations=[DjangoIntegration()],
send_default_pii=True,
traces_sample_rate=1.0,
)
from django.db import connections

if "postgres" not in connections:
pytest.skip("postgres tests disabled")

# trigger Django to open a new connection by marking the existing one as None.
connections["postgres"].connection = None

events = capture_events()

content, status, headers = client.get(reverse("postgres_select"))
assert status == "200 OK"

(event,) = events

for span in event["spans"]:
if span.get("op") == "db":
data = span.get("data")
assert data.get(SPANDATA.DB_SYSTEM) == "postgresql"
assert (
data.get(SPANDATA.DB_NAME)
== connections["postgres"].get_connection_params()["database"]
)
assert data.get(SPANDATA.SERVER_ADDRESS) == os.environ.get(
"SENTRY_PYTHON_TEST_POSTGRES_HOST", "localhost"
)
assert data.get(SPANDATA.SERVER_PORT) == 5432


@pytest.mark.parametrize(
"transaction_style,client_url,expected_transaction,expected_source,expected_response",
[
Expand Down Expand Up @@ -1059,11 +1095,7 @@ def dummy(a, b):
@pytest_mark_django_db_decorator()
@pytest.mark.skipif(DJANGO_VERSION < (1, 9), reason="Requires Django >= 1.9")
def test_cache_spans_disabled_middleware(
sentry_init,
client,
capture_events,
use_django_caching_with_middlewares,
settings,
sentry_init, client, capture_events, use_django_caching_with_middlewares
):
sentry_init(
integrations=[
Expand Down Expand Up @@ -1141,11 +1173,7 @@ def test_cache_spans_disabled_templatetag(
@pytest_mark_django_db_decorator()
@pytest.mark.skipif(DJANGO_VERSION < (1, 9), reason="Requires Django >= 1.9")
def test_cache_spans_middleware(
sentry_init,
client,
capture_events,
use_django_caching_with_middlewares,
settings,
sentry_init, client, capture_events, use_django_caching_with_middlewares
):
sentry_init(
integrations=[
Expand Down
3 changes: 3 additions & 0 deletions tests/integrations/pymongo/test_pymongo.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ def test_transactions(sentry_init, capture_events, mongo_server, with_pii):
}
for span in find, insert_success, insert_fail:
assert span["data"][SPANDATA.DB_SYSTEM] == "mongodb"
assert span["data"][SPANDATA.DB_NAME] == "test_db"
assert span["data"][SPANDATA.SERVER_ADDRESS] == "localhost"
assert span["data"][SPANDATA.SERVER_PORT] == mongo_server.port
for field, value in common_tags.items():
assert span["tags"][field] == value

Expand Down
3 changes: 3 additions & 0 deletions tests/integrations/sqlalchemy/test_sqlalchemy.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ class Address(Base):

for span in event["spans"]:
assert span["data"][SPANDATA.DB_SYSTEM] == "sqlite"
assert span["data"][SPANDATA.DB_NAME] == ":memory:"
assert SPANDATA.SERVER_ADDRESS not in span["data"]
assert SPANDATA.SERVER_PORT not in span["data"]

assert (
render_span_tree(event)
Expand Down
Loading