Skip to content

Commit 5175651

Browse files
authored
chore(dbm): generate sql comments for DBM (#4321)
## Description Adds utils for generating DBM comments ## Checklist - [ ] Title must conform to [conventional commit](https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-conventional). - [ ] Add additional sections for `feat` and `fix` pull requests. - [ ] Ensure tests are passing for affected code. - [ ] [Library documentation](https://github.com/DataDog/dd-trace-py/tree/1.x/docs) and/or [Datadog's documentation site](https://github.com/DataDog/documentation/) is updated. Link to doc PR in description. ## Motivation ## Design ## Testing strategy ## Relevant issue(s) ## Testing strategy ## Reviewer Checklist - [ ] Title is accurate. - [ ] Description motivates each change. - [ ] No unnecessary changes were introduced in this PR. - [ ] PR cannot be broken up into smaller PRs. - [ ] Avoid breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes unless absolutely necessary. - [ ] Tests provided or description of manual testing performed is included in the code or PR. - [ ] Release note has been added for fixes and features, or else `changelog/no-changelog` label added. - [ ] All relevant GitHub issues are correctly linked. - [ ] Backports are identified and tagged with Mergifyio. - [ ] Add to milestone.
1 parent b119dd4 commit 5175651

File tree

4 files changed

+125
-2
lines changed

4 files changed

+125
-2
lines changed

ddtrace/context.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,15 @@ def sampling_priority(self, value):
9494
return
9595
self._metrics[SAMPLING_PRIORITY_KEY] = value
9696

97+
@property
98+
def _traceparent(self):
99+
# type: () -> str
100+
if self.trace_id is None or self.span_id is None:
101+
return ""
102+
103+
sampled = 1 if self.sampling_priority and self.sampling_priority > 0 else 0
104+
return "00-{:032x}-{:016x}-{:02x}".format(self.trace_id, self.span_id, sampled)
105+
97106
@property
98107
def dd_origin(self):
99108
# type: () -> Optional[Text]

ddtrace/settings/database_monitoring.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
1+
from typing import TYPE_CHECKING
2+
13
from envier import En
24
from envier import validators
35

6+
from ddtrace.vendor.sqlcommenter import generate_sql_comment as _generate_sql_comment
7+
8+
from . import _config as dd_config
9+
10+
11+
if TYPE_CHECKING:
12+
from ddtrace import Span
13+
14+
DBM_PARENT_SERVICE_NAME_KEY = "ddps"
15+
DBM_DATABASE_SERVICE_NAME_KEY = "dddbs"
16+
DBM_ENVIRONMENT_KEY = "dde"
17+
DBM_VERSION_KEY = "ddpv"
18+
DBM_TRACE_PARENT_KEY = "traceparent"
19+
420

521
class DatabaseMonitoringConfig(En):
622
__prefix__ = "dd_trace"
@@ -14,4 +30,23 @@ class DatabaseMonitoringConfig(En):
1430
)
1531

1632

17-
config = DatabaseMonitoringConfig()
33+
dbm_config = DatabaseMonitoringConfig()
34+
35+
36+
def _get_dbm_comment(db_span):
37+
# type: (Span) -> str
38+
if dbm_config.injection_mode == "disabled":
39+
return ""
40+
41+
dbm_tags = {
42+
DBM_PARENT_SERVICE_NAME_KEY: dd_config.service,
43+
DBM_ENVIRONMENT_KEY: dd_config.env,
44+
DBM_VERSION_KEY: dd_config.version,
45+
DBM_DATABASE_SERVICE_NAME_KEY: db_span.service,
46+
}
47+
48+
if dbm_config.injection_mode == "full":
49+
# TODO: add _dd.dbm_trace_injected tag to db_span
50+
dbm_tags[DBM_TRACE_PARENT_KEY] = db_span.context._traceparent
51+
52+
return _generate_sql_comment(**dbm_tags)

tests/internal/test_database_monitoring.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
def test_injection_mode_configuration():
88
# Ensure Database Monitoring support is disabled by default
99
# TODO: Enable DBM support by default
10-
assert database_monitoring.config.injection_mode == "disabled"
10+
assert database_monitoring.dbm_config.injection_mode == "disabled"
1111

1212
# Ensure service is a valid injection mode
1313
with override_env(dict(DD_TRACE_SQL_COMMENT_INJECTION_MODE="service")):
@@ -27,3 +27,55 @@ def test_injection_mode_configuration():
2727
excinfo.value.args[0] == "Invalid value for environment variable DD_TRACE_SQL_COMMENT_INJECTION_MODE: "
2828
"value must be one of ['disabled', 'full', 'service']"
2929
)
30+
31+
32+
@pytest.mark.subprocess(env=dict(DD_TRACE_SQL_COMMENT_INJECTION_MODE="disabled"))
33+
def test_get_dbm_comment_disabled_mode():
34+
from ddtrace import tracer
35+
from ddtrace.settings import database_monitoring
36+
37+
dbspan = tracer.trace("dbspan", service="orders-db")
38+
sqlcomment = database_monitoring._get_dbm_comment(dbspan)
39+
assert sqlcomment == ""
40+
41+
42+
@pytest.mark.subprocess(
43+
env=dict(
44+
DD_TRACE_SQL_COMMENT_INJECTION_MODE="service",
45+
DD_SERVICE="orders-app",
46+
DD_ENV="staging",
47+
DD_VERSION="v7343437-d7ac743",
48+
)
49+
)
50+
def test_get_dbm_comment_service_mode():
51+
from ddtrace import tracer
52+
from ddtrace.settings import database_monitoring
53+
54+
dbspan = tracer.trace("dbname", service="orders-db")
55+
56+
sqlcomment = database_monitoring._get_dbm_comment(dbspan)
57+
# assert tags sqlcomment contains the correct value
58+
assert sqlcomment == " /*dddbs='orders-db',dde='staging',ddps='orders-app',ddpv='v7343437-d7ac743'*/"
59+
60+
61+
@pytest.mark.subprocess(
62+
env=dict(
63+
DD_TRACE_SQL_COMMENT_INJECTION_MODE="full",
64+
DD_SERVICE="orders-app",
65+
DD_ENV="staging",
66+
DD_VERSION="v7343437-d7ac743",
67+
)
68+
)
69+
def test_get_dbm_comment_full_mode():
70+
from ddtrace import tracer
71+
from ddtrace.settings import database_monitoring
72+
73+
dbspan = tracer.trace("dbname", service="orders-db")
74+
75+
sqlcomment = database_monitoring._get_dbm_comment(dbspan)
76+
# assert tags sqlcomment contains the correct value
77+
assert (
78+
sqlcomment
79+
== " /*dddbs='orders-db',dde='staging',ddps='orders-app',ddpv='v7343437-d7ac743',traceparent='%s'*/"
80+
% (dbspan.context._traceparent,)
81+
)

tests/tracer/test_context.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,30 @@ def test_eq(ctx1, ctx2):
4848
)
4949
def test_not_eq(ctx1, ctx2):
5050
assert ctx1 != ctx2
51+
52+
53+
def test_traceparent():
54+
def validate_traceparent(context, sampled_expected):
55+
version_hex, traceid_hex, spanid_hex, sampled_hex = context._traceparent.split("-")
56+
assert version_hex == "00"
57+
58+
assert len(traceid_hex) == 32
59+
assert traceid_hex == "{:032x}".format(context.trace_id)
60+
61+
assert len(spanid_hex) == 16
62+
assert spanid_hex == "{:016x}".format(context.span_id)
63+
64+
assert len(sampled_hex) == 2
65+
assert sampled_hex == sampled_expected
66+
67+
span = Span("span_a")
68+
span.context.sampling_priority = -1
69+
validate_traceparent(span.context, "00")
70+
71+
span = Span("span_b")
72+
span.context.sampling_priority = 0
73+
validate_traceparent(span.context, "00")
74+
75+
span = Span("span_c")
76+
span.context.sampling_priority = 1
77+
validate_traceparent(span.context, "01")

0 commit comments

Comments
 (0)