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

All in with tags (v4.0.0) #158

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
286 changes: 53 additions & 233 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ python-librato

A Python wrapper for the Librato Metrics API.

NOTE: Starting in version 3, we have deprecated Dashboards and Instruments in favor of Spaces and Charts.
## Sections

## Installation

Expand Down Expand Up @@ -58,81 +58,56 @@ transparent pagination.
Let's now create a Metric:

```python
api.submit("temperature", 10, description="temperature at home")
api.submit("temperature", 10, tags={'city': 'Barcelona'})
```

By default ```submit()``` will create a gauge metric. The metric will be
created automatically by the server if it does not exist

To create a counter metric (note: counters are expected to be *absolute* counters and take a monotonically increasing value such as network throughput):

```python
api.submit("connections", 20, type="counter", description="server connections")
```
The metric will be created automatically by the server if it does not exist.

To iterate over your metric names:

```python
for m in api.list_metrics():
print "%s: %s" % (m.name, m.description)
for m in api.list_metrics():
print "%s: %s" % (m.name, m.description)
```

To retrieve a specific metric:

```python
# Retrieve metric metadata ONLY
gauge = api.get("temperature")
gauge.name # "temperature"
gauge.description # "temperature at home"
gauge.measurements # {}
# Retrive metric with the last measurement seen
gauge = api.get("temperature", count=1, resolution=1)
gauge.measurements
# {u'unassigned': [{u'count': 1, u'sum_squares': 100.0, u'min': 10.0, u'measure_time': 1474988647, u'max': 10.0, u'sum': 10.0, u'value': 10.0}]}
tags = {"city": "Barcelona}
metric = api.get_metric("temperature")
metric.name # "temperature"

# Retrieve the last measurements seen on that metric
data = api.get("temperature", duration=60, tags=tags)
data = self.api.get(name, duration=60, tags=tags)
data['series'][0]['measurements']
```

Iterate over measurements:

```python
metric = api.get("temperature", count=100, resolution=1)
source = 'unassigned'
for m in metric.measurements[source]:
print "%s: %s" % (m['value'], m['measure_time'])
data = api.get("temperature", duration=60, tags=tags)
data = self.api.get(name, duration=60, tags=tags)
data['series'][0]['measurements']
for m in data['series'][0]['measurements']:
print m
```

Notice a couple of things here. First, we are using the key `unassigned` since
we have not associated our measurements to any source. If we had specified a
source such as `sf` we could use it in the same fashion. Read more the
[API documentation](https://www.librato.com/docs/api/). In addition, notice how
we are passing the count and resolution parameters to make sure the API
returns measurements in its answer and not only the metric properties.
Read more about them [here](https://www.librato.com/docs/api/#retrieve-metric-by-name).

To retrieve a composite metric:

```python
# Get average temperature across all cities for last 8 hours
compose = 'mean(s("temperature", "*", {function: "mean", period: "3600"}))'
import time
start_time = int(time.time()) - 8 * 3600
resp = api.get_composite(compose, start_time=start_time)
resp['measurements'][0]['series']
# [
# {u'measure_time': 1421744400, u'value': 41.23944444444444},
# {u'measure_time': 1421748000, u'value': 40.07611111111111},
# {u'measure_time': 1421751600, u'value': 38.77444444444445},
# {u'measure_time': 1421755200, u'value': 38.05833333333333},
# {u'measure_time': 1421758800, u'value': 37.983333333333334},
# {u'measure_time': 1421762400, u'value': 38.93333333333333},
# {u'measure_time': 1421766000, u'value': 40.556666666666665}
# ]
compose = 'mean(s("' + name + '", "*", {function: "mean", period: "3600"}))'
import time
start_time = int(time.time()) - 3600
data = self.api.get_composite(compose, start_time=start_time)
for m in data['series'][0]['measurements']:
print m
```

To create a saved composite metric:

```python
api.create_composite('humidity', 'sum(s("all.*", "*"))',
description='a test composite')
api.create_composite('humidity', 'sum(s("all.*", "*"))', description='a test composite')
```

Delete a metric:
Expand All @@ -150,13 +125,11 @@ in batch mode. We push measurements that are stored and when we are
ready, they will be submitted in an efficient manner. Here is an example:

```python
api = librato.connect('email', 'token')
q = api.new_queue()
q.add('temperature', 22.1, source='upstairs')
q.add('temperature', 23.1, source='dowstairs')
q.add('num_requests', 100, type='counter', source='server1')
q.add('num_requests', 102, type='counter', source='server2')
q.submit()
q = api.new_queue()
q.add('temperature', 12, tags={'city': 'sf' , 'station': '12'})
q.add('temperature', 14, tags={'city': 'new york', 'station': '1'})
q.add('temperature', 22, tags={'city': 'austin' , 'station': '112'})
q.submit()
```

Queues can also be used as context managers. Once the context block is complete the queue
Expand All @@ -168,40 +141,18 @@ If the operation succeeds both measurements will be submitted.
```python
api = librato.connect('email', 'token')
with api.new_queue() as q:
q.add('temperature', 22.1, source='upstairs')
q.add('temperature', 22.1, tags={'city': 'sf', 'station': '12'})
potentially_dangerous_operation()
q.add('num_requests', 100, type='counter', source='server1')
q.add('num_requests', tags={'city': 'austin', 'station': '112'})
```

Queues by default will collect metrics until they are told to submit. You may create a queue
that autosubmits based on metric volume.

```python
api = librato.connect('email', 'token')
# Submit when the 400th metric is queued
q = api.new_queue(auto_submit_count=400)
```

## Submitting tagged measurements

NOTE: **Tagged measurements are only available in the Tags Beta. Please [contact Librato support](mailto:[email protected]) to join the beta.**

We can use tags in the submit method in order to associate key value pairs with our
measurements:

```python
api.submit("temperature", 22, tags={'city': 'austin', 'station': '27'})
```

Queues also support tags. When adding measurements to a queue, we can associate tags to them
in the same way we do with the submit method:

```python
q = api.new_queue()
q.add('temperature', 12, tags={'city': 'sf' , 'station': '12'})
q.add('temperature', 14, tags={'city': 'new york', 'station': '1'})
q.add('temperature', 22, tags={'city': 'austin' , 'station': '112'})
q.submit()
api = librato.connect('email', 'token')
# Submit when the 400th metric is queued
q = api.new_queue(auto_submit_count=400)
```

## Updating Metric Attributes
Expand All @@ -210,12 +161,10 @@ You can update the information for a metric by using the `update` method,
for example:

```python
api = librato.connect('email', 'token')
for metric in api.list_metrics(name=" "):
gauge = api.get(metric.name)
attrs = gauge.attributes
attrs['display_units_long'] = 'ms'
api.update(metric.name, attributes=attrs)
metric = self.api.get_metric("temperature")
attrs = metric.attributes
attrs['description'] = "A temperature metric"
self.api.update(name, attributes=attrs)
```

## Annotations
Expand Down Expand Up @@ -266,138 +215,6 @@ Delete a named annotation stream:
api.delete_annotation_stream("testing")
```

## Spaces API
### List Spaces
```python
# List spaces
spaces = api.list_spaces()
```

### Create a Space
```python
# Create a new Space directly via API
space = api.create_space("space_name")
print("Created '%s'" % space.name)

# Create a new Space via the model, passing the connection
space = Space(api, 'Production')
space.save()
```

### Find a Space
```python
space = api.find_space('Production')
```

### Delete a Space
```python
space = api.create_space('Test')
api.delete_space(space.id)
# or
space.delete()
```

### Create a Chart
```python
# Create a Chart directly via API (defaults to line chart)
space = api.find_space('Production')
chart = api.create_chart(
'cpu',
space,
streams=[{'metric': 'cpu.idle', 'source': '*'}]
)
```

```python
# Create line chart using the Space model
space = api.find_space('Production')

# You can actually create an empty chart (default to line)
chart = space.add_chart('cpu')

# Create a chart with all attributes
chart = space.add_chart(
'memory',
type='line',
streams=[
{'metric': 'memory.free', 'source': '*'},
{'metric': 'memory.used', 'source': '*',
'group_function': 'breakout', 'summary_function': 'average'}
],
min=0,
max=50,
label='the y axis label',
use_log_yaxis=True,
related_space=1234
)
```

```python
# Shortcut to create a line chart with a single metric on it
chart = space.add_single_line_chart('my chart', 'my.metric', '*')
chart = space.add_single_line_chart('my chart', metric='my.metric', source='*')
```

```python
# Shortcut to create a stacked chart with a single metric on it
chart = space.add_single_stacked_chart('my chart', 'my.metric', '*')
```

```python
# Create a big number chart
bn = space.add_chart(
'memory',
type='bignumber',
streams=[{'metric': 'my.metric', 'source': '*'}]
)
# Shortcut to add big number chart
bn = space.add_bignumber_chart('My Chart', 'my.metric', '*')
bn = space.add_bignumber_chart('My Chart', 'my.metric',
source='*',
group_function='sum',
summary_function='sum',
use_last_value=True
)
```

### Find a Chart
```python
# Takes either space_id or a space object
chart = api.get_chart(chart_id, space_id)
chart = api.get_chart(chart_id, space)
```

### Update a Chart
```python
chart = api.get_chart(chart_id, space_id)
chart.min = 0
chart.max = 50
chart.save()
```

### Rename a Chart
```python
chart = api.get_chart(chart_id, space_id)
# save() gets called automatically here
chart.rename('new chart name')
```

### Add new metrics to a Chart
```python
chart = space.charts()[-1]
chart.new_stream('foo', '*')
chart.new_stream(metric='foo', source='*')
chart.new_stream(composite='s("foo", "*")')
chart.save()
```

### Delete a Chart
```python
chart = api.get_chart(chart_id, space_id)
chart.delete()
```


## Alerts

List all alerts:
Expand Down Expand Up @@ -476,44 +293,47 @@ print(alert.services)

## Client-side Aggregation

You can aggregate measurements before submission using the `Aggregator` class. Optionally, specify a `measure_time` to submit that timestamp to the API. You may also optionally specify a `period` to floor the timestamp to a particular interval. If `period` is specified without a `measure_time`, the current timestamp will be used, and floored to `period`. Specifying an optional `source` allows the aggregated measurement to report a source name.
You can aggregate measurements before submission using the `Aggregator` class.
Optionally, specify a `measure_time` to submit that timestamp to the API.
You may also optionally specify a `period` to floor the timestamp to a particular interval.
If `period` is specified without a `measure_time`, the current timestamp will be used, and floored to `period`.

Aggregator instances can be sent immediately by calling `submit()` or added to a `Queue` by calling `queue.add_aggregator()`.
Aggregator instances can be sent immediately by calling `submit()` or added to
a `Queue` by calling `queue.add_aggregator()`.

```python
from librato.aggregator import Aggregator

api = librato.connect('email', 'token')

a = Aggregator(api)
a = Aggregator(api, tags={'host': 'machine1'})
a.add("foo", 42)
a.add("foo", 5)
# count=2, min=5, max=42, sum=47 (value calculated by API = mean = 23.5), source=unassigned
# count=2, min=5, max=42, sum=47 (value calculated by API = mean = 23.5)
# measure_time = <now>
a.submit()

a = Aggregator(api, source='my.source', period=60)
a = Aggregator(api, tags={'host': 'machine1'}, period=60)
a.add("foo", 42)
a.add("foo", 5)
# count=2, min=5, max=42, sum=47 (value calculated by API = mean = 23.5), source=my.source
# count=2, min=5, max=42, sum=47 (value calculated by API = mean = 23.5)
# measure_time = <now> - (<now> % 60)
a.submit()

a = Aggregator(api, period=60, measure_time=1419302671)
a = Aggregator(api, period=60, measure_time=1419302671, tags={'host': 'machine1'})
a.add("foo", 42)
a.add("foo", 5)
# count=2, min=5, max=42, sum=47 (value calculated by API = mean = 23.5), source=unassigned
# count=2, min=5, max=42, sum=47 (value calculated by API = mean = 23.5)
# measure_time = 1419302671 - (1419302671 % 60) = 1419302671 - 31 = 1419302640
a.submit()

a = Aggregator(api, measure_time=1419302671)
a = Aggregator(api, measure_time=1419302671, tags={'host': 'machine1'})
a.add("foo", 42)
a.add("foo", 5)
# count=2, min=5, max=42, sum=47 (value calculated by API = mean = 23.5), source=unassigned
# count=2, min=5, max=42, sum=47 (value calculated by API = mean = 23.5)
# measure_time = 1419302671
a.submit()


# You can also add an Aggregator instance to a queue
q = librato.queue.Queue(api)
q.add_aggregator(a)
Expand Down
Loading