|
19 | 19 | import time |
20 | 20 | from threading import Lock, RLock |
21 | 21 | import weakref |
22 | | -from typing import TYPE_CHECKING |
| 22 | + |
| 23 | +if sys.version_info[:2] >= (3, 5): |
| 24 | + from typing import TYPE_CHECKING # noqa: F401 |
23 | 25 |
|
24 | 26 | try: |
25 | 27 | import queue |
|
29 | 31 |
|
30 | 32 |
|
31 | 33 | # pylint: disable=unused-import |
32 | | -from typing import Any, Optional, List, Text, Type, Union |
| 34 | +if sys.version_info[:2] >= (3, 5): |
| 35 | + from typing import Any, Optional, List, Text, Type, Union, Iterable, Callable, overload # noqa: F401 |
| 36 | + |
| 37 | +try: |
| 38 | + from typing import SupportsIndex |
| 39 | +except ImportError: |
| 40 | + SupportsIndex = int # type: ignore[assignment,misc] |
33 | 41 | # pylint: enable=unused-import |
34 | 42 |
|
35 | 43 | # Datadog libraries |
|
45 | 53 | from datadog.util.format import normalize_tags, validate_cardinality |
46 | 54 | from datadog.version import __version__ |
47 | 55 |
|
48 | | -if TYPE_CHECKING: |
49 | | - from socket import socket as _Socket |
| 56 | + |
| 57 | +if sys.version_info[:2] >= (3, 5): |
| 58 | + if TYPE_CHECKING: |
| 59 | + from socket import socket as _Socket |
| 60 | + |
| 61 | + BaseListClass = List[str] |
| 62 | +else: |
| 63 | + BaseListClass = list |
| 64 | + |
| 65 | +class TagList(BaseListClass): |
| 66 | + """A list subclass that calls on_change() after any mutation.""" |
| 67 | + |
| 68 | + def __init__(self, iterable = (), on_change = None): |
| 69 | + # type: (Iterable[str], Optional[Callable[[], None]]) -> None |
| 70 | + super(TagList, self).__init__(iterable) |
| 71 | + self._on_change = on_change |
| 72 | + |
| 73 | + def _notify(self): |
| 74 | + # type: () -> None |
| 75 | + if self._on_change is not None: |
| 76 | + self._on_change() |
| 77 | + |
| 78 | + if sys.version_info[:2] >= (3, 5): |
| 79 | + @overload |
| 80 | + def __setitem__(self, index, value): # noqa: F811 |
| 81 | + # type: (SupportsIndex, str) -> None |
| 82 | + pass |
| 83 | + |
| 84 | + @overload |
| 85 | + def __setitem__(self, index, value): # noqa: F811 |
| 86 | + # type: (slice, Iterable[str]) -> None |
| 87 | + pass |
| 88 | + |
| 89 | + def __setitem__(self, index, value): # noqa: F811 |
| 90 | + # type: (Union[SupportsIndex, slice], Union[str, Iterable[str]]) -> None |
| 91 | + super(TagList, self).__setitem__(index, value) # type: ignore |
| 92 | + self._notify() |
| 93 | + |
| 94 | + def __delitem__(self, index): # noqa: F811 |
| 95 | + # type: (Union[SupportsIndex, slice]) -> None |
| 96 | + super(TagList, self).__delitem__(index) |
| 97 | + self._notify() |
| 98 | + |
| 99 | + def __iadd__(self, other): # type: ignore[misc,override] # noqa: F811 |
| 100 | + # type: (Iterable[str]) -> "TagList" |
| 101 | + super(TagList, self).__iadd__(other) |
| 102 | + self._notify() |
| 103 | + return self |
| 104 | + |
| 105 | + def __imul__(self, n): # noqa: F811 |
| 106 | + # type: (SupportsIndex) -> "TagList" |
| 107 | + super(TagList, self).__imul__(n) |
| 108 | + self._notify() |
| 109 | + return self |
| 110 | + |
| 111 | + def append(self, value): # noqa: F811 |
| 112 | + # type: (str) -> None |
| 113 | + super(TagList, self).append(value) |
| 114 | + self._notify() |
| 115 | + |
| 116 | + def extend(self, iterable): # noqa: F811 |
| 117 | + # type: (Iterable[str]) -> None |
| 118 | + super(TagList, self).extend(iterable) |
| 119 | + self._notify() |
| 120 | + |
| 121 | + def insert(self, index, value): # noqa: F811 |
| 122 | + # type: (SupportsIndex, str) -> None |
| 123 | + super(TagList, self).insert(index, value) |
| 124 | + self._notify() |
| 125 | + |
| 126 | + def remove(self, value): # noqa: F811 |
| 127 | + # type: (str) -> None |
| 128 | + super(TagList, self).remove(value) |
| 129 | + self._notify() |
| 130 | + |
| 131 | + def pop(self, index = -1): # noqa: F811 |
| 132 | + # type: (SupportsIndex) -> str |
| 133 | + value = super(TagList, self).pop(index) |
| 134 | + self._notify() |
| 135 | + return value |
| 136 | + |
| 137 | + def clear(self): # noqa: F811 |
| 138 | + # type: () -> None |
| 139 | + super(TagList, self).__delitem__(slice(None)) |
| 140 | + self._notify() |
| 141 | + |
| 142 | + def sort(self, *args, **kwargs): |
| 143 | + # type: (*Any, **Any) -> None |
| 144 | + super(TagList, self).sort(*args, **kwargs) |
| 145 | + self._notify() |
| 146 | + |
| 147 | + def reverse(self): |
| 148 | + # type: () -> None |
| 149 | + super(TagList, self).reverse() |
| 150 | + self._notify() |
| 151 | + |
50 | 152 |
|
51 | 153 | # Logging |
52 | 154 | log = logging.getLogger("datadog.dogstatsd") |
@@ -442,9 +544,18 @@ def __init__( |
442 | 544 | value = os.environ.get(var, "") |
443 | 545 | if value: |
444 | 546 | env_tags.append("{name}:{value}".format(name=tag_name, value=value)) |
| 547 | + |
| 548 | + # This lock is used for all cases where client configuration is being changed: buffering, |
| 549 | + # aggregation, sender mode. |
| 550 | + self._config_lock = RLock() |
| 551 | + |
445 | 552 | if constant_tags is None: |
446 | 553 | constant_tags = [] |
447 | | - self.constant_tags = constant_tags + env_tags |
| 554 | + |
| 555 | + self._constant_tags_str = "" |
| 556 | + self._constant_tags = TagList() |
| 557 | + self.constant_tags = TagList(constant_tags + env_tags) |
| 558 | + |
448 | 559 | if namespace is not None: |
449 | 560 | namespace = text(namespace) |
450 | 561 | self.namespace = namespace |
@@ -476,10 +587,6 @@ def __init__( |
476 | 587 |
|
477 | 588 | self._reset_buffer() |
478 | 589 |
|
479 | | - # This lock is used for all cases where client configuration is being changed: buffering, |
480 | | - # aggregation, sender mode. |
481 | | - self._config_lock = RLock() |
482 | | - |
483 | 590 | self._disable_buffering = disable_buffering |
484 | 591 | self._disable_aggregation = disable_aggregation |
485 | 592 |
|
@@ -1267,9 +1374,18 @@ def _serialize_metric( |
1267 | 1374 | parts.append("|@") |
1268 | 1375 | parts.append(text(sample_rate)) |
1269 | 1376 |
|
1270 | | - if tags: |
| 1377 | + with self._config_lock: |
| 1378 | + constant_tags_str = self._constant_tags_str |
| 1379 | + |
| 1380 | + if tags or constant_tags_str: |
1271 | 1381 | parts.append("|#") |
1272 | | - parts.append(",".join(normalize_tags(tags))) |
| 1382 | + if tags: |
| 1383 | + parts.append(",".join(normalize_tags(tags))) |
| 1384 | + if constant_tags_str: |
| 1385 | + parts.append(",") |
| 1386 | + parts.append(constant_tags_str) |
| 1387 | + else: |
| 1388 | + parts.append(constant_tags_str) |
1273 | 1389 |
|
1274 | 1390 | if self._container_id: |
1275 | 1391 | parts.append("|c:") |
@@ -1323,8 +1439,6 @@ def _report(self, metric, metric_type, value, tags, sample_rate, timestamp=0, sa |
1323 | 1439 |
|
1324 | 1440 | validate_cardinality(cardinality) |
1325 | 1441 |
|
1326 | | - # Resolve the full tag list |
1327 | | - tags = self._add_constant_tags(tags) |
1328 | 1442 | payload = self._serialize_metric( |
1329 | 1443 | metric, metric_type, value, tags, sample_rate, timestamp, cardinality |
1330 | 1444 | ) |
@@ -1642,13 +1756,40 @@ def service_check( |
1642 | 1756 |
|
1643 | 1757 | self._send(string) |
1644 | 1758 |
|
| 1759 | + @staticmethod |
| 1760 | + def _normalize_and_join_tags(tags): |
| 1761 | + # type: (List[str]) -> str |
| 1762 | + """Normalize a tag list and join into a comma-separated string.""" |
| 1763 | + if tags: |
| 1764 | + return ",".join(normalize_tags(tags)) |
| 1765 | + |
| 1766 | + return "" |
| 1767 | + |
| 1768 | + def _rebuild_constant_tags_str(self): |
| 1769 | + # type: () -> None |
| 1770 | + with self._config_lock: |
| 1771 | + self._constant_tags_str = self._normalize_and_join_tags(self._constant_tags) |
| 1772 | + |
| 1773 | + @property |
| 1774 | + def constant_tags(self): |
| 1775 | + # type: () -> TagList |
| 1776 | + return self._constant_tags |
| 1777 | + |
| 1778 | + @constant_tags.setter |
| 1779 | + def constant_tags(self, value): |
| 1780 | + # type: (Union[TagList, List[str]]) -> None |
| 1781 | + with self._config_lock: |
| 1782 | + self._constant_tags = TagList(value or [], on_change=self._rebuild_constant_tags_str) |
| 1783 | + self._rebuild_constant_tags_str() |
| 1784 | + |
1645 | 1785 | def _add_constant_tags(self, tags): |
1646 | 1786 | # type: (Optional[List[str]]) -> Optional[List[str]] |
1647 | | - if self.constant_tags: |
1648 | | - if tags: |
1649 | | - return tags + self.constant_tags |
| 1787 | + with self._config_lock: |
| 1788 | + if self._constant_tags: |
| 1789 | + if tags: |
| 1790 | + return tags + self._constant_tags |
1650 | 1791 |
|
1651 | | - return self.constant_tags |
| 1792 | + return list(self._constant_tags) |
1652 | 1793 | return tags |
1653 | 1794 |
|
1654 | 1795 | def _is_origin_detection_enabled(self, container_id, origin_detection_enabled): |
|
0 commit comments