Skip to content

Commit 24f0351

Browse files
authored
chore(telemetry): clean up payload generation and typing in the writer (#14615)
- Improves typing for the TelemetryWriter. Converts all python2 type hints to python3 typing - Adds typing where it's missing. - Adds an `TELEMETRY_EVENT_TYPE` enum to store all telemetry types. - Removes duplicate methods that simply generate telemetry payloads. Instead adds a single `_get_event()` method that accepts a `payload` and a TELEMETRY_EVENT_TYPE. - Removes `TelemetryWriter.add_event()` and `TelemetryWriter.add_events()`. These methods are no longer used in ddtrace. Telemetry writer should not support queuing arbitrary payloads, only event types in `TELEMETRY_EVENT_TYPE` are supported. ## Checklist - [x] PR author has checked that all the criteria below are met - The PR description includes an overview of the change - The PR description articulates the motivation for the change - The change includes tests OR the PR description describes a testing strategy - The PR description notes risks associated with the change, if any - Newly-added code is easy to change - The change follows the [library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) - The change includes or references documentation updates if necessary - Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [x] Reviewer has checked that all the criteria below are met - Title is accurate - All changes are related to the pull request's stated goal - Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes - Testing strategy adequately addresses listed risks - Newly-added code is easy to change - Release note makes sense to a user of the library - If necessary, author has 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)
1 parent d36d913 commit 24f0351

File tree

11 files changed

+223
-339
lines changed

11 files changed

+223
-339
lines changed

.gitlab/benchmarks/bp-runner.microbenchmarks.fail-on-breach.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,7 +1085,7 @@ experiments:
10851085
- max_rss_usage < 34.00 MB
10861086
- name: telemetryaddmetric-1-count-metrics-100-times
10871087
thresholds:
1088-
- execution_time < 0.25 ms
1088+
- execution_time < 0.22 ms
10891089
- max_rss_usage < 34.00 MB
10901090
- name: telemetryaddmetric-1-distribution-metric-1-times
10911091
thresholds:
@@ -1113,11 +1113,11 @@ experiments:
11131113
- max_rss_usage < 34.00 MB
11141114
- name: telemetryaddmetric-100-count-metrics-100-times
11151115
thresholds:
1116-
- execution_time < 23.50 ms
1116+
- execution_time < 22.0 ms
11171117
- max_rss_usage < 34.00 MB
11181118
- name: telemetryaddmetric-100-distribution-metrics-100-times
11191119
thresholds:
1120-
- execution_time < 2.25 ms
1120+
- execution_time < 2.30 ms
11211121
- max_rss_usage < 34.00 MB
11221122
- name: telemetryaddmetric-100-gauge-metrics-100-times
11231123
thresholds:

ddtrace/internal/telemetry/constants.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,20 @@ class TELEMETRY_NAMESPACE(Enum):
1111
PROFILER = "profiler"
1212

1313

14-
TELEMETRY_TYPE_GENERATE_METRICS = "generate-metrics"
15-
TELEMETRY_TYPE_DISTRIBUTION = "distributions"
16-
TELEMETRY_TYPE_LOGS = "logs"
14+
class TELEMETRY_EVENT_TYPE(Enum):
15+
STARTED = "app-started"
16+
SHUTDOWN = "app-closing"
17+
HEARTBEAT = "app-heartbeat"
18+
EXTENDED_HEARTBEAT = "app-extended-heartbeat"
19+
DEPENDENCIES_LOADED = "app-dependencies-loaded"
20+
PRODUCT_CHANGE = "app-product-change"
21+
INTEGRATIONS_CHANGE = "app-integrations-change"
22+
ENDPOINTS = "app-endpoints"
23+
CLIENT_CONFIGURATION_CHANGE = "app-client-configuration-change"
24+
LOGS = "logs"
25+
METRICS = "generate-metrics"
26+
DISTRIBUTIONS = "distributions"
27+
MESSAGE_BATCH = "message-batch"
1728

1829

1930
class TELEMETRY_LOG_LEVEL(Enum):

ddtrace/internal/telemetry/metrics_namespaces.pyx

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ from typing import Tuple
66

77
from ddtrace.internal import forksafe
88
from ddtrace.internal.telemetry.constants import TELEMETRY_NAMESPACE
9-
from ddtrace.internal.telemetry.constants import TELEMETRY_TYPE_DISTRIBUTION
10-
from ddtrace.internal.telemetry.constants import TELEMETRY_TYPE_GENERATE_METRICS
9+
from ddtrace.internal.telemetry.constants import TELEMETRY_EVENT_TYPE
1110

1211

1312
MetricTagType = Optional[Tuple[Tuple[str, str], ...]]
@@ -23,10 +22,16 @@ class MetricType(str, enum.Enum):
2322
cdef class MetricNamespace:
2423
cdef object _metrics_data_lock
2524
cdef public dict _metrics_data
25+
# Cache enum objects at class level for maximum performance
26+
cdef readonly object _metrics_key
27+
cdef readonly object _distributions_key
2628

2729
def __cinit__(self):
2830
self._metrics_data_lock = forksafe.Lock()
2931
self._metrics_data = {}
32+
# Initialize cached enum references
33+
self._metrics_key = TELEMETRY_EVENT_TYPE.METRICS
34+
self._distributions_key = TELEMETRY_EVENT_TYPE.DISTRIBUTIONS
3035

3136
def flush(self, interval: float = None):
3237
cdef float _interval = float(interval or 1.0)
@@ -46,19 +51,21 @@ cdef class MetricNamespace:
4651

4752
now = int(time.time())
4853
data = {
49-
TELEMETRY_TYPE_GENERATE_METRICS: {},
50-
TELEMETRY_TYPE_DISTRIBUTION: {},
54+
self._metrics_key: {},
55+
self._distributions_key: {},
5156
}
5257
for metric_id, value in namespace_metrics.items():
5358
name, namespace, _tags, metric_type = metric_id
54-
tags = ["{}:{}".format(k, v).lower() for k, v in _tags] if _tags else []
59+
tags = [f"{k}:{v}".lower() for k, v in _tags] if _tags else []
5560
if metric_type is MetricType.DISTRIBUTION:
56-
data[TELEMETRY_TYPE_DISTRIBUTION].setdefault(namespace, []).append({
61+
payload_type = self._distributions_key
62+
metric = {
5763
"metric": name,
5864
"points": value,
5965
"tags": tags,
60-
})
66+
}
6167
else:
68+
payload_type = self._metrics_key
6269
if metric_type is MetricType.RATE:
6370
value = value / _interval
6471
metric = {
@@ -70,8 +77,12 @@ cdef class MetricNamespace:
7077
}
7178
if metric_type in (MetricType.RATE, MetricType.GAUGE):
7279
metric["interval"] = _interval
73-
data[TELEMETRY_TYPE_GENERATE_METRICS].setdefault(namespace, []).append(metric)
7480

81+
namespace_dict = data[payload_type]
82+
if namespace not in namespace_dict:
83+
namespace_dict[namespace] = [metric]
84+
else:
85+
namespace_dict[namespace].append(metric)
7586
return data
7687

7788
def add_metric(
@@ -86,16 +97,20 @@ cdef class MetricNamespace:
8697
Adds a new telemetry metric to the internal metrics.
8798
Telemetry metrics are stored under "dd.instrumentation_telemetry_data.<namespace>.<name>".
8899
"""
89-
cdef float v
90-
cdef tuple metric_id
91100
metric_id = (name, namespace.value, tags, metric_type)
92-
if metric_type is MetricType.DISTRIBUTION:
101+
if metric_type is MetricType.COUNT or metric_type is MetricType.RATE:
93102
with self._metrics_data_lock:
94-
self._metrics_data.setdefault(metric_id, []).append(value)
103+
existing = self._metrics_data.get(metric_id)
104+
if existing is not None:
105+
self._metrics_data[metric_id] = existing + value
106+
else:
107+
self._metrics_data[metric_id] = value
95108
elif metric_type is MetricType.GAUGE:
96-
# Dict writes are atomic, no need to lock
97109
self._metrics_data[metric_id] = value
98-
else:
110+
else: # MetricType.DISTRIBUTION
99111
with self._metrics_data_lock:
100-
v = self._metrics_data.get(metric_id, 0)
101-
self._metrics_data[metric_id] = v + value
112+
existing = self._metrics_data.get(metric_id)
113+
if existing is not None:
114+
existing.append(value)
115+
else:
116+
self._metrics_data[metric_id] = [value]

0 commit comments

Comments
 (0)