From 2dbe9cfc04194777726338fbe1622fa7fdadef5b Mon Sep 17 00:00:00 2001 From: Teague Bick Date: Thu, 13 Jul 2023 07:12:18 -0400 Subject: [PATCH] chore(service.naming) - schematize django db calls (#6335) Django has a separate mechanism for determining service name when using an underlying database. This change adds the schematized service.naming to those Django DB calls. ## Checklist - [x] Change(s) are motivated and described in the PR description. - [x] Testing strategy is described if automated tests are not included in the PR. - [x] Risk is outlined (performance impact, potential for breakage, maintainability, etc). - [x] Change is maintainable (easy to change, telemetry, documentation). - [x] [Library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) are followed. If no release note is required, add label `changelog/no-changelog`. - [x] Documentation is included (in-code, generated user docs, [public corp docs](https://github.com/DataDog/documentation/)). - [x] Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [ ] Title is accurate. - [ ] No unnecessary changes are introduced. - [ ] Description motivates each change. - [ ] Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes unless absolutely necessary. - [ ] Testing strategy adequately addresses listed risk(s). - [ ] Change is maintainable (easy to change, telemetry, documentation). - [ ] Release note makes sense to a user of the library. - [ ] Reviewer has explicitly acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment. - [ ] Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting) --- ddtrace/contrib/django/patch.py | 1 + tests/contrib/django/test_django.py | 54 +++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/ddtrace/contrib/django/patch.py b/ddtrace/contrib/django/patch.py index 0f2bfe2b002..76f0f6589ae 100644 --- a/ddtrace/contrib/django/patch.py +++ b/ddtrace/contrib/django/patch.py @@ -107,6 +107,7 @@ def cursor(django, pin, func, instance, args, kwargs): else: database_prefix = config.django.database_service_name_prefix service = "{}{}{}".format(database_prefix, alias, "db") + service = schematize_service_name(service) vendor = getattr(conn, "vendor", "db") prefix = sqlx.normalize_vendor(vendor) diff --git a/tests/contrib/django/test_django.py b/tests/contrib/django/test_django.py index 31c4705c0c8..d57a33e308a 100644 --- a/tests/contrib/django/test_django.py +++ b/tests/contrib/django/test_django.py @@ -1530,6 +1530,60 @@ def test(client, test_spans): assert status == 0, (out, err) +@pytest.mark.parametrize("global_service_name", [None, "mysvc"]) +@pytest.mark.parametrize("schema_version", [None, "v0", "v1"]) +def test_schematized_default_db_service_name( + ddtrace_run_python_code_in_subprocess, schema_version, global_service_name +): + expected_service_name = { + None: "defaultdb", + "v0": "defaultdb", + "v1": global_service_name or _DEFAULT_SPAN_SERVICE_NAMES["v1"], + }[schema_version] + code = """ +import pytest +import sys + +import django + +from tests.contrib.django.conftest import * +from tests.utils import override_config + +@pytest.mark.django_db +def test_connection(client, test_spans): + from django.contrib.auth.models import User + + users = User.objects.count() + assert users == 0 + + test_spans.assert_span_count(1) + spans = test_spans.get_spans() + + span = spans[0] + assert span.name == "sqlite.query" + assert span.service == "{}" + assert span.span_type == "sql" + assert span.get_tag("django.db.vendor") == "sqlite" + assert span.get_tag("django.db.alias") == "default" + +if __name__ == "__main__": + sys.exit(pytest.main(["-x", __file__])) + """.format( + expected_service_name + ) + + env = os.environ.copy() + if schema_version is not None: + env["DD_TRACE_SPAN_ATTRIBUTE_SCHEMA"] = schema_version + if global_service_name is not None: + env["DD_SERVICE"] = global_service_name + out, err, status, _ = ddtrace_run_python_code_in_subprocess( + code, + env=env, + ) + assert status == 0, (out, err) + + @pytest.mark.parametrize("schema_version", [None, "v0", "v1"]) def test_schematized_operation_name(ddtrace_run_python_code_in_subprocess, schema_version): expected_operation_name = {None: "django.request", "v0": "django.request", "v1": "http.server.request"}[