Skip to content

Commit fccaff3

Browse files
mergify[bot]majorgreysbrettlangdonKyle-Verhoog
authored
fix(grpc): handle None values for tags (#2443) (#2445)
* fix(grpc): handle None values for tags * handle empty hostname in Python 2.7 * correct coercion of hostname Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> (cherry picked from commit c08f02a) Co-authored-by: Tahir H. Butt <[email protected]> Co-authored-by: Brett Langdon <[email protected]> Co-authored-by: Kyle Verhoog <[email protected]>
1 parent 6266d8b commit fccaff3

File tree

4 files changed

+86
-15
lines changed

4 files changed

+86
-15
lines changed

ddtrace/contrib/grpc/client_interceptor.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010
from ddtrace.vendor import wrapt
1111

1212
from . import constants
13+
from . import utils
1314
from .. import trace_utils
1415
from ...constants import ANALYTICS_SAMPLE_RATE_KEY
1516
from ...constants import SPAN_MEASURED_KEY
1617
from ...internal.logger import get_logger
1718
from ...propagation.http import HTTPPropagator
18-
from .utils import set_grpc_method_meta
1919

2020

2121
log = get_logger(__name__)
@@ -179,10 +179,8 @@ def _intercept_client_call(self, method_kind, client_call_details):
179179
)
180180
span.set_tag(SPAN_MEASURED_KEY)
181181

182-
set_grpc_method_meta(span, client_call_details.method, method_kind)
183-
span._set_str_tag(constants.GRPC_HOST_KEY, self._host)
184-
if self._port:
185-
span._set_str_tag(constants.GRPC_PORT_KEY, str(self._port))
182+
utils.set_grpc_method_meta(span, client_call_details.method, method_kind)
183+
utils.set_grpc_client_meta(span, self._host, self._port)
186184
span._set_str_tag(constants.GRPC_SPAN_KIND_KEY, constants.GRPC_SPAN_KIND_VALUE_CLIENT)
187185

188186
sample_rate = config.grpc.get_analytics_sample_rate()

ddtrace/contrib/grpc/utils.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,24 @@ def parse_method_path(method_path):
2525
def set_grpc_method_meta(span, method, method_kind):
2626
method_path = method
2727
method_package, method_service, method_name = parse_method_path(method_path)
28-
span._set_str_tag(constants.GRPC_METHOD_PATH_KEY, method_path)
28+
if method_path is not None:
29+
span._set_str_tag(constants.GRPC_METHOD_PATH_KEY, method_path)
2930
if method_package is not None:
3031
span._set_str_tag(constants.GRPC_METHOD_PACKAGE_KEY, method_package)
31-
span._set_str_tag(constants.GRPC_METHOD_SERVICE_KEY, method_service)
32-
span._set_str_tag(constants.GRPC_METHOD_NAME_KEY, method_name)
33-
span._set_str_tag(constants.GRPC_METHOD_KIND_KEY, method_kind)
32+
if method_service is not None:
33+
span._set_str_tag(constants.GRPC_METHOD_SERVICE_KEY, method_service)
34+
if method_name is not None:
35+
span._set_str_tag(constants.GRPC_METHOD_NAME_KEY, method_name)
36+
if method_kind is not None:
37+
span._set_str_tag(constants.GRPC_METHOD_KIND_KEY, method_kind)
38+
39+
40+
def set_grpc_client_meta(span, host, port):
41+
if host:
42+
span._set_str_tag(constants.GRPC_HOST_KEY, host)
43+
if port:
44+
span._set_str_tag(constants.GRPC_PORT_KEY, str(port))
45+
span._set_str_tag(constants.GRPC_SPAN_KIND_KEY, constants.GRPC_SPAN_KIND_VALUE_CLIENT)
3446

3547

3648
def _parse_target_from_args(args, kwargs):
@@ -51,6 +63,11 @@ def _parse_target_from_args(args, kwargs):
5163
port = parsed.port
5264
except ValueError:
5365
log.warning("Non-integer port in target '%s'", target)
54-
return parsed.hostname, port
66+
67+
# an empty hostname in Python 2.7 will be an empty string rather than
68+
# None
69+
hostname = parsed.hostname if parsed.hostname is not None and len(parsed.hostname) > 0 else None
70+
71+
return hostname, port
5572
except ValueError:
5673
log.warning("Malformed target '%s'.", target)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
fixes:
3+
- |
4+
grpc: handle None values for span tags.
Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
import mock
22
import pytest
33

4-
from ddtrace.contrib.grpc.utils import _parse_target_from_args
5-
from ddtrace.contrib.grpc.utils import parse_method_path
4+
from ddtrace.contrib.grpc import utils
65

76

87
def test_parse_method_path_with_package():
98
method_path = "/package.service/method"
10-
parsed = parse_method_path(method_path)
9+
parsed = utils.parse_method_path(method_path)
1110
assert parsed == ("package", "service", "method")
1211

1312

1413
def test_parse_method_path_without_package():
1514
method_path = "/service/method"
16-
parsed = parse_method_path(method_path)
15+
parsed = utils.parse_method_path(method_path)
1716
assert parsed == (None, "service", "method")
1817

1918

@@ -23,19 +22,72 @@ def test_parse_method_path_without_package():
2322
[
2423
(("localhost:1234",), dict(), ("localhost", 1234), None),
2524
(("localhost",), dict(), ("localhost", None), None),
25+
((":1234",), dict(), (None, 1234), None),
2626
(("[::]:1234",), dict(), ("::", 1234), None),
2727
(("[::]",), dict(), ("::", None), None),
2828
(None, dict(target="localhost:1234"), ("localhost", 1234), None),
2929
(None, dict(target="localhost"), ("localhost", None), None),
30+
(None, dict(target=":1234"), (None, 1234), None),
3031
(None, dict(target="[::]:1234"), ("::", 1234), None),
3132
(None, dict(target="[::]"), ("::", None), None),
3233
(("localhost:foo",), dict(), ("localhost", None), ("Non-integer port in target '%s'", "localhost:foo")),
3334
(("",), dict(), (None, None), None),
3435
],
3536
)
3637
def test_parse_target_from_args(mock_log, args, kwargs, result, log_warning_call):
37-
assert _parse_target_from_args(args, kwargs) == result
38+
assert utils._parse_target_from_args(args, kwargs) == result
3839
if log_warning_call:
3940
mock_log.warning.assert_called_once_with(*log_warning_call)
4041
else:
4142
mock_log.warning.assert_not_called()
43+
44+
45+
@pytest.mark.parametrize(
46+
"host, port, calls",
47+
[
48+
(
49+
"localhost",
50+
1234,
51+
[mock.call("grpc.host", "localhost"), mock.call("grpc.port", "1234"), mock.call("span.kind", "client")],
52+
),
53+
("localhost", None, [mock.call("grpc.host", "localhost"), mock.call("span.kind", "client")]),
54+
(None, 1234, [mock.call("grpc.port", "1234"), mock.call("span.kind", "client")]),
55+
(None, None, [mock.call("span.kind", "client")]),
56+
],
57+
)
58+
def test_set_grpc_client_meta(host, port, calls):
59+
span = mock.MagicMock()
60+
utils.set_grpc_client_meta(span, host, port)
61+
span._set_str_tag.assert_has_calls(calls)
62+
63+
64+
@pytest.mark.parametrize(
65+
"method, method_kind, calls",
66+
[
67+
(
68+
"/package.service/method",
69+
"unary",
70+
[
71+
mock.call("grpc.method.path", "/package.service/method"),
72+
mock.call("grpc.method.package", "package"),
73+
mock.call("grpc.method.service", "service"),
74+
mock.call("grpc.method.name", "method"),
75+
mock.call("grpc.method.kind", "unary"),
76+
],
77+
),
78+
(
79+
"/service/method",
80+
"unary",
81+
[
82+
mock.call("grpc.method.path", "/service/method"),
83+
mock.call("grpc.method.service", "service"),
84+
mock.call("grpc.method.name", "method"),
85+
mock.call("grpc.method.kind", "unary"),
86+
],
87+
),
88+
],
89+
)
90+
def test_set_grpc_method_meta(method, method_kind, calls):
91+
span = mock.MagicMock()
92+
utils.set_grpc_method_meta(span, method, method_kind)
93+
span._set_str_tag.assert_has_calls(calls)

0 commit comments

Comments
 (0)