diff --git a/datadog/dogstatsd/base.py b/datadog/dogstatsd/base.py index 0e707a286..01fdd0195 100644 --- a/datadog/dogstatsd/base.py +++ b/datadog/dogstatsd/base.py @@ -274,6 +274,14 @@ def event(self, title, text, alert_type=None, aggregation_key=None, """ title = self._escape_event_content(title) text = self._escape_event_content(text) + + # Append all client level tags to every event + if self.constant_tags: + if tags: + tags += self.constant_tags + else: + tags = self.constant_tags + string = u'_e{%d,%d}:%s|%s' % (len(title), len(text), title, text) if date_happened: string = '%s|d:%d' % (string, date_happened) diff --git a/datadog/threadstats/base.py b/datadog/threadstats/base.py index afbf1575f..62f9b96cc 100644 --- a/datadog/threadstats/base.py +++ b/datadog/threadstats/base.py @@ -22,10 +22,16 @@ class ThreadStats(object): - def __init__(self): - """ Initialize a dogstats object. """ + def __init__(self, constant_tags=None): + """ + Initialize a dogstats object. + + :param constant_tags: Tags to attach to every metric reported by this client + :type constant_tags: list of strings + """ # Don't collect until start is called. self._disabled = True + self.constant_tags = constant_tags def start(self, flush_interval=10, roll_up_interval=10, device=None, flush_in_thread=True, flush_in_greenlet=False, disabled=False): @@ -116,6 +122,13 @@ def event(self, title, text, alert_type=None, aggregation_key=None, 'The web server is up again', alert_type='success') """ if not self._disabled: + # Append all client level tags to every event + if self.constant_tags: + if tags: + tags += self.constant_tags + else: + tags = self.constant_tags + self._event_aggregator.add_event( title=title, text=text, alert_type=alert_type, aggregation_key=aggregation_key, source_type_name=source_type_name, date_happened=date_happened, priority=priority, @@ -292,6 +305,13 @@ def _get_aggregate_metrics(self, flush_time=None): # FIXME: emit a dictionary from the aggregator metrics = [] for timestamp, value, name, tags, host in rolled_up_metrics: + # Append all client level tags to every metric + if self.constant_tags: + if tags: + tags += self.constant_tags + else: + tags = self.constant_tags + metric = { 'metric': name, 'points': [[timestamp, value]], diff --git a/tests/unit/dogstatsd/test_statsd.py b/tests/unit/dogstatsd/test_statsd.py index 34e8d31a9..cadf32ad0 100644 --- a/tests/unit/dogstatsd/test_statsd.py +++ b/tests/unit/dogstatsd/test_statsd.py @@ -127,6 +127,15 @@ def test_event(self): aggregation_key='key', tags=['t1', 't2:v2']) t.assert_equal(u'_e{5,19}:Title|♬ †øU †øU ¥ºu T0µ ♪|k:key|#t1,t2:v2', self.recv()) + def test_event_constant_tags(self): + self.statsd.constant_tags = ['bar:baz', 'foo'] + self.statsd.event('Title', u'L1\nL2', priority='low', date_happened=1375296969) + t.assert_equal(u'_e{5,6}:Title|L1\\nL2|d:1375296969|p:low|#bar:baz,foo', self.recv()) + + self.statsd.event('Title', u'♬ †øU †øU ¥ºu T0µ ♪', + aggregation_key='key', tags=['t1', 't2:v2']) + t.assert_equal(u'_e{5,19}:Title|♬ †øU †øU ¥ºu T0µ ♪|k:key|#t1,t2:v2,bar:baz,foo', self.recv()) + def test_service_check(self): now = int(time.time()) self.statsd.service_check( diff --git a/tests/unit/threadstats/test_threadstats.py b/tests/unit/threadstats/test_threadstats.py index 567abd95f..cb0c77f95 100644 --- a/tests/unit/threadstats/test_threadstats.py +++ b/tests/unit/threadstats/test_threadstats.py @@ -103,8 +103,9 @@ def test_event(self): reporter.events = [] event1_priority = "low" event1_date_happened = 1375296969 + event1_tag = "Event 2 tag" dog.event(event1_title, event1_text, priority=event1_priority, - date_happened=event1_date_happened) + date_happened=event1_date_happened, tags=[event1_tag]) # Flush and test dog.flush() @@ -113,6 +114,49 @@ def test_event(self): nt.assert_equal(event['text'], event1_text) nt.assert_equal(event['priority'], event1_priority) nt.assert_equal(event['date_happened'], event1_date_happened) + nt.assert_equal(event['tags'], [event1_tag]) + + def test_event_constant_tags(self): + constant_tag = 'type:constant' + dog = ThreadStats(constant_tags=[constant_tag]) + dog.start(roll_up_interval=10, flush_in_thread=False) + reporter = dog.reporter = MemoryReporter() + + # Add two events + event1_title = "Event 1 title" + event2_title = "Event 1 title" + event1_text = "Event 1 text" + event2_text = "Event 2 text" + dog.event(event1_title, event1_text) + dog.event(event2_title, event2_text) + + # Flush and test + dog.flush() + event1, event2 = reporter.events + nt.assert_equal(event1['title'], event1_title) + nt.assert_equal(event1['text'], event1_text) + nt.assert_equal(event1['tags'], [constant_tag]) + nt.assert_equal(event2['title'], event2_title) + nt.assert_equal(event2['text'], event2_text) + nt.assert_equal(event2['text'], event2_text) + nt.assert_equal(event2['tags'], [constant_tag]) + + # Test more parameters + reporter.events = [] + event1_priority = "low" + event1_date_happened = 1375296969 + event1_tag = "Event 2 tag" + dog.event(event1_title, event1_text, priority=event1_priority, + date_happened=event1_date_happened, tags=[event1_tag]) + + # Flush and test + dog.flush() + event, = reporter.events + nt.assert_equal(event['title'], event1_title) + nt.assert_equal(event['text'], event1_text) + nt.assert_equal(event['priority'], event1_priority) + nt.assert_equal(event['date_happened'], event1_date_happened) + nt.assert_equal(event['tags'], [event1_tag, constant_tag]) def test_histogram(self): dog = ThreadStats() @@ -345,6 +389,42 @@ def test_tags(self): nt.assert_equal(g3['tags'], ['env:staging']) nt.assert_equal(g3['points'][0][1], 20) + def test_constant_tags(self): + dog = ThreadStats(constant_tags=['type:constant']) + dog.start(roll_up_interval=10, flush_in_thread=False) + reporter = dog.reporter = MemoryReporter() + + # Post the same metric with different tags. + dog.gauge('gauge', 10, timestamp=100.0) + dog.gauge('gauge', 15, timestamp=100.0, tags=['env:production', 'db']) + dog.gauge('gauge', 20, timestamp=100.0, tags=['env:staging']) + + dog.increment('counter', timestamp=100.0) + dog.increment('counter', timestamp=100.0, tags=['env:production', 'db']) + dog.increment('counter', timestamp=100.0, tags=['env:staging']) + + dog.flush(200.0) + + metrics = self.sort_metrics(reporter.metrics) + nt.assert_equal(len(metrics), 6) + + [c1, c2, c3, g1, g2, g3] = metrics + (nt.assert_equal(c['metric'], 'counter') for c in [c1, c2, c3]) + nt.assert_equal(c1['tags'], ['env:production', 'db', 'type:constant']) + nt.assert_equal(c1['points'][0][1], 1) + nt.assert_equal(c2['tags'], ['env:staging', 'type:constant']) + nt.assert_equal(c2['points'][0][1], 1) + nt.assert_equal(c3['tags'], ['type:constant']) + nt.assert_equal(c3['points'][0][1], 1) + + (nt.assert_equal(c['metric'], 'gauge') for c in [g1, g2, g3]) + nt.assert_equal(g1['tags'], ['env:production', 'db', 'type:constant']) + nt.assert_equal(g1['points'][0][1], 15) + nt.assert_equal(g2['tags'], ['env:staging', 'type:constant']) + nt.assert_equal(g2['points'][0][1], 20) + nt.assert_equal(g3['tags'], ['type:constant']) + nt.assert_equal(g3['points'][0][1], 10) + # Ensure histograms work as well. @dog.timed('timed', tags=['version:1']) def test():