Skip to content
This repository was archived by the owner on Dec 11, 2023. It is now read-only.

Commit bda57b2

Browse files
authored
Merge pull request #172 from librato/inherit-tags
add ability to inherit tags
2 parents 737c26a + 05f2a18 commit bda57b2

5 files changed

Lines changed: 123 additions & 25 deletions

File tree

README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,43 @@ with api.new_queue() as q:
168168
Queues by default will collect metrics until they are told to submit. You may create a queue
169169
that autosubmits based on metric volume.
170170

171+
171172
```python
172173
# Submit when the 400th metric is queued
173174
q = api.new_queue(auto_submit_count=400)
174175
```
175176

177+
## Tag Inheritance
178+
179+
Tags can be inherited from the queue or connection object if `inherit_tags=True` is passed as
180+
an attribute. If inherit_tags is not passed, but tags are added to the measurement, the measurement
181+
tags will be the only tags added to that measurement.
182+
183+
When there are tag collisions, the measurement, then the batch, then the connection is the order of
184+
priority.
185+
186+
```python
187+
api = librato.connect('email', 'token', tags={'company': 'librato', 'service': 'app'})
188+
189+
# tags will be {'city': 'sf'}
190+
api.submit('temperature', 80, tags={'city': 'sf'})
191+
192+
# tags will be {'city': 'sf', 'company': 'librato', 'service': 'app'}
193+
api.submit('temperature', 80, tags={'city': 'sf'}, inherit_tags=True)
194+
195+
q = api.new_queue(tags={'service':'api'})
196+
197+
# tags will be {'location': 'downstairs'}
198+
q.add('temperature', 22.1, tags={'location': 'downstairs'})
199+
200+
# tags will be {'company': 'librato', 'service':'api'}
201+
q.add('temperature', 23.1)
202+
203+
# tags will be {'location': 'downstairs', 'company': 'librato', 'service': 'api'}
204+
q.add('temperature', 22.1, tags={'location': 'downstairs'}, inherit_tags=True)
205+
q.submit()
206+
```
207+
176208
## Updating Metric Attributes
177209

178210
You can update the information for a metric by using the `update` method,

librato/__init__.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -284,19 +284,26 @@ def submit(self, name, value, type="gauge", **query_props):
284284

285285
def submit_tagged(self, name, value, **query_props):
286286
payload = {'measurements': []}
287+
payload['measurements'].append(self.create_tagged_payload(name, value, **query_props))
288+
self._mexe("measurements", method="POST", query_props=payload)
287289

288-
if self.tags:
289-
payload['tags'] = self.tags
290-
290+
def create_tagged_payload(self, name, value, **query_props):
291+
"""Create the measurement for forwarding to Librato"""
291292
measurement = {
292293
'name': self.sanitize(name),
293294
'value': value
294295
}
296+
if 'tags' in query_props:
297+
inherit_tags = query_props.pop('inherit_tags', False)
298+
if inherit_tags:
299+
tags = query_props.pop('tags', {})
300+
measurement['tags'] = dict(self.get_tags(), **tags)
301+
elif self.tags:
302+
measurement['tags'] = self.tags
303+
295304
for k, v in query_props.items():
296305
measurement[k] = v
297-
298-
payload['measurements'].append(measurement)
299-
self._mexe("measurements", method="POST", query_props=payload)
306+
return measurement
300307

301308
def get(self, name, **query_props):
302309
resp = self._mexe("metrics/%s" % self.sanitize(name), method="GET", query_props=query_props)
@@ -561,13 +568,7 @@ def delete_chart(self, chart_id, space_id, **query_props):
561568
# Queue
562569
#
563570
def new_queue(self, **kwargs):
564-
tags = self.tags
565-
if 'tags' in kwargs:
566-
# Supplied tag set takes precedence
567-
tags.update(kwargs.pop('tags'))
568-
569-
q = Queue(self, tags=tags, **kwargs)
570-
return q
571+
return Queue(self, **kwargs)
571572

572573
#
573574
# misc

librato/queue.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,8 @@ def add_tags(self, d):
6363
self.tags.update(d)
6464

6565
def add(self, name, value, type='gauge', **query_props):
66-
if len(self.tags) > 0:
67-
query_props['tags'] = self.tags
68-
69-
if 'tags' in query_props:
66+
"""add measurements to the Q"""
67+
if 'tags' in query_props or len(self.tags) > 0:
7068
self.add_tagged(name, value, **query_props)
7169
else:
7270
nm = {} # new measurement
@@ -85,6 +83,13 @@ def add_tagged(self, name, value, **query_props):
8583
nm['sum'] = value
8684
nm['count'] = 1
8785

86+
# must remove the inherit_tags key for compliance with json
87+
inherit_tags = query_props.pop('inherit_tags', False)
88+
tags = query_props.get('tags', {})
89+
if inherit_tags or tags == {}:
90+
inheritted_tags = dict(self.connection.get_tags(), **self.get_tags())
91+
query_props['tags'] = dict(inheritted_tags, **tags)
92+
8893
for pn, v in query_props.items():
8994
nm[pn] = v
9095

@@ -134,12 +139,8 @@ def submit(self):
134139
self.connection._mexe("metrics", method="POST", query_props=c)
135140
self.chunks = []
136141

137-
for c in self.tagged_chunks:
138-
if 'tags' in c:
139-
c['tags'] = dict(self.tags).update(c['tags'])
140-
elif self.tags:
141-
c['tags'] = dict(self.tags)
142-
self.connection._mexe("measurements", method="POST", query_props=c)
142+
for chunk in self.tagged_chunks:
143+
self.connection._mexe("measurements", method="POST", query_props=chunk)
143144
self.tagged_chunks = []
144145

145146
def __enter__(self):

tests/test_metrics.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,20 @@ def test_add_in_gauge(self):
197197
assert len(gauge.measurements[src]) == 2
198198
assert gauge.measurements[src][-1]['value'] == 1
199199

200+
def test_md_inherit_tags(self):
201+
self.conn.set_tags({'company': 'Librato', 'hi': 'four'})
202+
203+
measurement = self.conn.create_tagged_payload('user_cpu', 20.2, tags={'hi': 'five'}, inherit_tags=True)
204+
205+
assert measurement['tags'] == {'hi': 'five', 'company': 'Librato'}
206+
207+
def test_md_donot_inherit_tags(self):
208+
self.conn.set_tags({'company': 'Librato', 'hi': 'four'})
209+
210+
measurement = self.conn.create_tagged_payload('user_cpu', 20.2, tags={'hi': 'five'})
211+
212+
assert measurement['tags'] == {'hi': 'five'}
213+
200214
def test_md_submit(self):
201215
mt1 = int(time.time()) - 5
202216

@@ -223,7 +237,7 @@ def test_merge_tags(self):
223237

224238
self.conn.set_tags({'company': 'Librato'})
225239
tags = {'hostname': 'web-1'}
226-
self.conn.submit_tagged('user_cpu', 20.2, time=mt1, tags=tags)
240+
self.conn.submit_tagged('user_cpu', 20.2, time=mt1, tags=tags, inherit_tags=True)
227241

228242
# Ensure 'company' and 'hostname' tags made it through
229243
for tags_search in ["hostname=web-1", "company=Librato"]:

tests/test_queue.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,56 @@ def test_inherited_tags(self):
4343
assert len(measurements) == 1
4444
assert measurements[0]['value'] == 10
4545

46+
def test_inherit_connection_level_tags(self):
47+
"""test if top level tags are ignored when passing measurement level tags"""
48+
conn = librato.connect('user_test', 'key_test', tags={'sky': 'blue'})
49+
50+
q = conn.new_queue()
51+
q.add_tagged('user_cpu', 10, tags={"hi": "five"}, inherit_tags=True)
52+
53+
measurements = q.tagged_chunks[0]['measurements']
54+
55+
assert len(measurements) == 1
56+
assert measurements[0].get('tags', {}) == {'sky': 'blue', 'hi': 'five'}
57+
58+
def test_ignore_connection_queue_level_tags(self):
59+
"""test if queue level tags are ignored when passing measurement level tags"""
60+
conn = librato.connect('user_test', 'key_test', tags={'sky': 'blue'})
61+
62+
q = conn.new_queue(tags={"service": "api"})
63+
q.add_tagged('user_cpu', 10, tags={"hi": "five"})
64+
measurements = q.tagged_chunks[0]['measurements']
65+
66+
assert len(measurements) == 1
67+
assert measurements[0].get('tags', {}) == {'hi': 'five'}
68+
69+
q.submit()
70+
71+
resp = self.conn.get_tagged('user_cpu', duration=60, tags_search="sky=blue")
72+
assert len(resp['series']) == 0
73+
74+
def test_inherit_queue_connection_level_tags(self):
75+
"""test if queue level tags are ignored when passing measurement level tags"""
76+
conn = librato.connect('user_test', 'key_test', tags={'sky': 'blue', 'company': 'Librato'})
77+
78+
q = conn.new_queue(tags={"service": "api", "hi": "four", "sky": "red"})
79+
q.add_tagged('user_cpu', 100, tags={"hi": "five"}, inherit_tags=True)
80+
measurements = q.tagged_chunks[0]['measurements']
81+
82+
assert len(measurements) == 1
83+
assert measurements[0].get('tags', {}) == {'sky': 'red', 'service': 'api', 'hi': 'five', 'company': 'Librato'}
84+
85+
def test_inherit_queue_level_tags(self):
86+
"""test if queue level tags are ignored when passing measurement level tags"""
87+
conn = librato.connect('user_test', 'key_test')
88+
89+
q = conn.new_queue(tags={"service": "api", "hi": "four"})
90+
q.add_tagged('user_cpu', 100, tags={"hi": "five"}, inherit_tags=True)
91+
measurements = q.tagged_chunks[0]['measurements']
92+
93+
assert len(measurements) == 1
94+
assert measurements[0].get('tags', {}) == {'service': 'api', 'hi': 'five'}
95+
4696
def test_constructor_tags(self):
4797
conn = librato.connect('user_test', 'key_test', tags={'sky': 'blue'})
4898
q = conn.new_queue(tags={'sky': 'red', 'coal': 'black'})
@@ -243,7 +293,7 @@ def test_md_measurement_level_tag(self):
243293
q.set_tags({'hostname': 'web-1'})
244294

245295
mt1 = int(time.time()) - 5
246-
q.add_tagged('system_cpu', 33.22, time=mt1, tags={"user": "james"})
296+
q.add_tagged('system_cpu', 33.22, time=mt1, tags={"user": "james"}, inherit_tags=True)
247297
q.submit()
248298

249299
# Ensure both tags get submitted

0 commit comments

Comments
 (0)