Skip to content

Commit

Permalink
feat(origindetection): implement cardinality common field
Browse files Browse the repository at this point in the history
Signed-off-by: Wassim DHIF <[email protected]>
  • Loading branch information
wdhif committed Jan 31, 2025
1 parent 9a3f236 commit 99bdf37
Show file tree
Hide file tree
Showing 9 changed files with 203 additions and 75 deletions.
10 changes: 10 additions & 0 deletions datadog/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def initialize(
statsd_constant_tags=None, # type: Optional[List[str]]
return_raw_response=False, # type: bool
hostname_from_config=True, # type: bool
cardinality=None, # type: Optional[str]
**kwargs # type: Any
):
# type: (...) -> None
Expand Down Expand Up @@ -117,6 +118,12 @@ def initialize(
:param hostname_from_config: Set the hostname from the Datadog agent config (agent 5). Will be deprecated
:type hostname_from_config: boolean
:param cardinality: Set the global cardinality for all metrics. \
Possible values are "none", "low", "orchestrator" and "high".
Can also be set via the DATADOG_CARDINALITY or DD_CARDINALITY environment variables.
:type cardinality: string
"""
# API configuration
api._api_key = api_key or api._api_key or os.environ.get("DATADOG_API_KEY", os.environ.get("DD_API_KEY"))
Expand Down Expand Up @@ -151,6 +158,9 @@ def initialize(
statsd.disable_buffering = statsd_disable_buffering
api._return_raw_response = return_raw_response

# Set the global cardinality for all metrics
statsd.cardinality = cardinality or os.environ.get("DATADOG_CARDINALITY", os.environ.get("DD_CARDINALITY"))

# HTTP client and API options
for key, value in iteritems(kwargs):
attribute = "_{}".format(key)
Expand Down
42 changes: 25 additions & 17 deletions datadog/dogstatsd/aggregator.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
)
from datadog.dogstatsd.metric_types import MetricType
from datadog.dogstatsd.max_sample_metric_context import MaxSampleMetricContexts
from datadog.util.format import validate_cardinality


class Aggregator(object):
def __init__(self, max_samples_per_context=0):
def __init__(self, max_samples_per_context=0, cardinality=None):
self.max_samples_per_context = max_samples_per_context
self.metrics_map = {
MetricType.COUNT: {},
Expand All @@ -31,6 +32,7 @@ def __init__(self, max_samples_per_context=0):
MetricType.GAUGE: threading.RLock(),
MetricType.SET: threading.RLock(),
}
self.cardinality = cardinality

def flush_aggregated_metrics(self):
metrics = []
Expand Down Expand Up @@ -58,53 +60,59 @@ def get_context(self, name, tags):
tags_str = ",".join(tags) if tags is not None else ""
return "{}:{}".format(name, tags_str)

def count(self, name, value, tags, rate, timestamp=0):
def count(self, name, value, tags, rate, timestamp=0, cardinality=None):
return self.add_metric(
MetricType.COUNT, CountMetric, name, value, tags, rate, timestamp
MetricType.COUNT, CountMetric, name, value, tags, rate, timestamp, cardinality
)

def gauge(self, name, value, tags, rate, timestamp=0):
def gauge(self, name, value, tags, rate, timestamp=0, cardinality=None):
return self.add_metric(
MetricType.GAUGE, GaugeMetric, name, value, tags, rate, timestamp
MetricType.GAUGE, GaugeMetric, name, value, tags, rate, timestamp, cardinality
)

def set(self, name, value, tags, rate, timestamp=0):
def set(self, name, value, tags, rate, timestamp=0, cardinality=None):
return self.add_metric(
MetricType.SET, SetMetric, name, value, tags, rate, timestamp
MetricType.SET, SetMetric, name, value, tags, rate, timestamp, cardinality
)

def add_metric(
self, metric_type, metric_class, name, value, tags, rate, timestamp=0
self, metric_type, metric_class, name, value, tags, rate, timestamp=0, cardinality=None
):
context = self.get_context(name, tags)
with self._locks[metric_type]:
if context in self.metrics_map[metric_type]:
self.metrics_map[metric_type][context].aggregate(value)
else:
if cardinality is None:
cardinality = self.cardinality
validate_cardinality(cardinality)
self.metrics_map[metric_type][context] = metric_class(
name, value, tags, rate, timestamp
name, value, tags, rate, timestamp, cardinality
)

def histogram(self, name, value, tags, rate):
def histogram(self, name, value, tags, rate, cardinality=None):
return self.add_max_sample_metric(
MetricType.HISTOGRAM, name, value, tags, rate
MetricType.HISTOGRAM, name, value, tags, rate, cardinality
)

def distribution(self, name, value, tags, rate):
def distribution(self, name, value, tags, rate, cardinality=None):
return self.add_max_sample_metric(
MetricType.DISTRIBUTION, name, value, tags, rate
MetricType.DISTRIBUTION, name, value, tags, rate, cardinality
)

def timing(self, name, value, tags, rate):
def timing(self, name, value, tags, rate, cardinality=None):
return self.add_max_sample_metric(
MetricType.TIMING, name, value, tags, rate
MetricType.TIMING, name, value, tags, rate, cardinality
)

def add_max_sample_metric(
self, metric_type, name, value, tags, rate
self, metric_type, name, value, tags, rate, cardinality=None
):
if rate is None:
rate = 1
context_key = self.get_context(name, tags)
metric_context = self.max_sample_metric_map[metric_type]
return metric_context.sample(name, value, tags, rate, context_key, self.max_samples_per_context)
if cardinality is None:
cardinality = self.cardinality
validate_cardinality(cardinality)
return metric_context.sample(name, value, tags, rate, context_key, self.max_samples_per_context, cardinality)
Loading

0 comments on commit 99bdf37

Please sign in to comment.