Skip to content

Commit

Permalink
Added config for CircleCi (#1)
Browse files Browse the repository at this point in the history
* Added CicleCI config and coverage
  • Loading branch information
Krolken authored Aug 20, 2019
1 parent aaab2c7 commit d2411ce
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 16 deletions.
59 changes: 59 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Python CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-python/ for more details
#
version: 2
jobs:
build:
docker:
# specify the version you desire here
# use `-browsers` prefix for selenium tests, e.g. `3.6.1-browsers`
- image: circleci/python:3.6.8

# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
# - image: circleci/postgres:9.4

working_directory: ~/repo

steps:
- checkout

# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "requirements-dev.txt" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-

- run:
name: install dependencies
command: |
python3 -m venv venv
. venv/bin/activate
pip install -r requirements-dev.txt
- save_cache:
paths:
- ./venv
key: v1-dependencies-{{ checksum "requirements-dev.txt" }}

- run:
name: install library
command: |
. venv/bin/activate
pip install -e .
# run tests!
- run:
name: run tests
command: |
. venv/bin/activate
pytest -v --cov=instrumentor
coveralls
- store_artifacts:
path: test-reports
destination: test-reports
16 changes: 16 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[run]
branch = True

[report]
exclude_lines =
if self.debug:
pragma: no cover
raise NotImplementedError
raise NotImplemented
if __name__ == .__main__.:
def __str__
pass

ignore_errors = True
omit =
*migrations*, *tests*
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

Instrumentation library for python apps. Backed by Redis and exposes to Prometheus

-- Work in progress --
-- Docs as design document --
[![CircleCI](https://circleci.com/gh/u9n/instrumentor/tree/master.svg?style=svg)](https://circleci.com/gh/u9n/instrumentor/tree/master)
[![Coverage Status](https://coveralls.io/repos/github/u9n/instrumentor/badge.svg?branch=circleci)](https://coveralls.io/github/u9n/instrumentor?branch=circleci)


# About Instrumentor

Expand Down Expand Up @@ -412,6 +413,15 @@ a separate webapp just for exposition could be set up, that also could expose
several namespaces (applications). This way scraping is decoupled from you application
and can be scaled accordingly.

## Installation

Python 3.6 + required. Install via pip:

```bash
pip install instrumentor
```





Expand Down
3 changes: 0 additions & 3 deletions dev-requirements.txt

This file was deleted.

21 changes: 10 additions & 11 deletions instrumentor/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ def __init__(self, name, description, allowed_labels=None):

self.counts = {NO_LABELS_KEY: 0}

def inc(self, value: int = 1, labels: Dict[str, str] = None) -> None:
def inc(self, value=1, labels: Dict[str, str] = None) -> None:
"""
Will increase the counter for a given label combination. It does not allow
negative values
Expand Down Expand Up @@ -417,6 +417,7 @@ def __init__(self, name, description, buckets=None, allowed_labels=None):
self.buckets = buckets
self.counts = dict()
self.sum = 0
self.total_count = 0
self.set_command_issued = set()

def observe(self, value, labels=None):
Expand Down Expand Up @@ -486,6 +487,7 @@ def _increase_total_count(self):
:return:
"""
self._add_observed_value(bucket=INFINITY_FOR_HISTOGRAM)
self.total_count += 1
self.propagate(
[
UpdateAction(
Expand All @@ -507,9 +509,9 @@ def time(self, _func=None, *, labels=None, milliseconds=False):
:return:
"""

def count_decorator(func):
def time_decorator(func):
@functools.wraps(func)
def count_wrapper(*args, **kwargs):
def time_wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
duration = time.time() - start
Expand All @@ -522,15 +524,15 @@ def count_wrapper(*args, **kwargs):
self.observe(value=observed, labels=labels)
return result

return count_wrapper
return time_wrapper

if _func is None:
return count_decorator
return time_decorator
else:
return count_decorator(_func)
return time_decorator(_func)


def count(_func=None, *, metric, labels=None):
def count(*, metric, labels=None):
"""
Decoration that will count the number of times the decorator has been used,
ie. how many times the function is called.
Expand All @@ -548,10 +550,7 @@ def count_wrapper(*args, **kwargs):

return count_wrapper

if _func is None:
return count_decorator
else:
return count_decorator(_func)
return count_decorator

else:
raise ValueError("Count decorator can only be used with Counters or Gauges.")
Expand Down
8 changes: 8 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Development dependencies
-r requirements.txt

redislite==5.0.142319
pytest==5.1.0
pytest-sugar==0.9.2
coveralls==1.8.2
pytest-cov==2.7.1
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
redis==3.3.7
hiredis==1.0.0
attrs==19.1.0
37 changes: 37 additions & 0 deletions tests/test_counter.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ def test_inc_by(self, counter: instrumentor.Counter):

assert counter.counts["__"] == 3

def test_inc_by_float(self, counter: instrumentor.Counter):
counter.inc(3.5)

assert counter.counts["__"] == 3.5

def test_inc_with_labels(self, counter: instrumentor.Counter):
counter.inc(3, labels={"code": "200", "path": "/api"})

Expand All @@ -28,6 +33,16 @@ def test_inc_with_labels_in_different_order_increase_same_counter(

assert counter.counts['code="200",path="/api"'] == 6

def test_inc_counter_without_registering_raises_runtime_error(self):
counter = instrumentor.Counter(
name="http_total_requests",
description="Test",
allowed_labels=["code", "path"],
)

with pytest.raises(RuntimeError):
counter.inc()

def test_counter_type_value(self, counter: instrumentor.Counter):
assert counter.TYPE_KEY == "c"

Expand Down Expand Up @@ -65,6 +80,19 @@ def try_count():

assert counter.counts['code="200"'] == 3

def test_using_reserved_labels_raises_value_error(
self, counter: instrumentor.Counter
):

with pytest.raises(ValueError):
counter.inc(3, labels={"le": "200"})

def test_creating_counter_with_reserved_label_raises_value_error(self):
with pytest.raises(ValueError):
counter = instrumentor.Counter(
name="test", description="test", allowed_labels=["le"]
)


class TestCountDecorator:
def test_with_counter(self, counter: instrumentor.Counter):
Expand Down Expand Up @@ -110,3 +138,12 @@ def try_count():
try_count()

assert gauge.counts['location="main-office"'] == 3

def test_count_with_histogram_raises_value_error(
self, histogram: instrumentor.Histogram
):
with pytest.raises(ValueError):

@instrumentor.count(metric=histogram)
def try_count():
print("Counting")
8 changes: 8 additions & 0 deletions tests/test_gauge.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ def test_inc_by(self, gauge: instrumentor.Gauge):
gauge.inc(3)
assert gauge.counts["__"] == 3

def test_inc_by_negative_raises_value_error(self, gauge: instrumentor.Gauge):
with pytest.raises(ValueError):
gauge.inc(-3)

def test_dec_by_1(self, gauge: instrumentor.Gauge):
gauge.dec()
assert gauge.counts["__"] == -1
Expand All @@ -30,6 +34,10 @@ def test_dec_by(self, gauge: instrumentor.Gauge):
gauge.dec(3)
assert gauge.counts["__"] == -3

def test_dec_by_negative_raises_value_error(self, gauge: instrumentor.Gauge):
with pytest.raises(ValueError):
gauge.dec(-3)

def test_set(self, gauge: instrumentor.Gauge):
gauge.set(100)
assert gauge.counts["__"] == 100
Expand Down
62 changes: 62 additions & 0 deletions tests/test_histogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,50 @@


class TestHistogram:
def test_observe_1(self, histogram: instrumentor.Histogram):
histogram.observe(0.22)

assert histogram.sum == 0.22

assert histogram.counts['le="0.4"'] == 1
assert histogram.counts['le="0.8"'] == 1
assert histogram.counts['le="1.6"'] == 1
assert histogram.counts['le="+Inf"'] == 1
assert histogram.total_count == 1

def test_observe_2(self, histogram: instrumentor.Histogram):
histogram.observe(0.22)
histogram.observe(0.78)

assert histogram.sum == 1
assert histogram.counts['le="0.4"'] == 1
assert histogram.counts['le="0.8"'] == 2
assert histogram.counts['le="1.6"'] == 2
assert histogram.counts['le="+Inf"'] == 2
assert histogram.total_count == 2

def test_observe_1_with_labels(self, histogram: instrumentor.Histogram):
histogram.observe(0.22, labels={"code": "200"})

assert histogram.sum == 0.22

assert histogram.counts['code="200",le="0.4"'] == 1
assert histogram.counts['code="200",le="0.8"'] == 1
assert histogram.counts['code="200",le="1.6"'] == 1
assert histogram.counts['le="+Inf"'] == 1
assert histogram.total_count == 1

def test_observe_2(self, histogram: instrumentor.Histogram):
histogram.observe(0.22, labels={"code": "200"})
histogram.observe(0.78, labels={"code": "200"})

assert histogram.sum == 1
assert histogram.counts['code="200",le="0.4"'] == 1
assert histogram.counts['code="200",le="0.8"'] == 2
assert histogram.counts['code="200",le="1.6"'] == 2
assert histogram.counts['le="+Inf"'] == 2
assert histogram.total_count == 2

def test_time_decorator(self, histogram: instrumentor.Histogram):
@histogram.time
def time_it():
Expand All @@ -23,6 +67,14 @@ def time_it():
assert histogram.sum > 0
assert histogram.counts['code="200",le="1.6"'] == 1

def test_reset(self, histogram: instrumentor.Histogram):
histogram.reset()

assert histogram.counts == dict()
assert histogram.sum == 0

# TODO: test all counting works.


class TestTimerDecorator:
def test_with_histogram(self, histogram: instrumentor.Histogram):
Expand All @@ -35,6 +87,16 @@ def time_it():
assert histogram.sum > 0
assert histogram.counts['le="+Inf"'] == 1

def test_with_histogram_and_milliseconds(self, histogram: instrumentor.Histogram):
@instrumentor.timer(metric=histogram, milliseconds=True)
def time_it():
time.sleep(0.1)

time_it()

assert histogram.sum > 10
assert histogram.counts['le="+Inf"'] == 1

def test_with_histogram_context_manager(self, histogram: instrumentor.Histogram):

with instrumentor.timer(metric=histogram):
Expand Down

0 comments on commit d2411ce

Please sign in to comment.