Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tags to metrics #70

Closed
wants to merge 3 commits into from
Closed
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
10 changes: 2 additions & 8 deletions pyformance/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@

from .registry import MetricsRegistry, global_registry, set_global_registry
from .registry import timer, counter, meter, histogram, gauge
from .registry import (
dump_metrics,
clear,
count_calls,
meter_calls,
hist_calls,
time_calls,
)
from .registry import dump_metrics, clear
from .decorators import count_calls, meter_calls, hist_calls, time_calls
from .meters.timer import call_too_long
152 changes: 152 additions & 0 deletions pyformance/decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import functools
import sys

import pyformance.registry as global_registry


def get_qualname(obj):
if sys.version_info[0] > 2:
return obj.__qualname__
return obj.__name__


def count_calls(original_func=None, registry=None, tags=None):
"""
Decorator to track the number of times a function is called.

:param original_func: the function to be decorated
:type original_func: C{func}

:param registry: the registry in which to create the meter

:param tags: tags attached to the timer (e.g. {'region': 'us-west-1'})
:type tags: C{dict}

:return: the decorated function
:rtype: C{func}
"""

def _decorate(fn):
@functools.wraps(fn)
def wrapper(*args, **kwargs):
function_name = get_qualname(fn)
metric_name = "%s_calls" % function_name

_registry = registry or global_registry
_histogram = _registry.counter(metric_name, tags).inc()

return fn(*args, **kwargs)

return wrapper

if original_func:
return _decorate(original_func)

return _decorate


def meter_calls(original_func=None, registry=None, tags=None):
"""
Decorator to the rate at which a function is called.

:param original_func: the function to be decorated
:type original_func: C{func}

:param registry: the registry in which to create the meter

:param tags: tags attached to the timer (e.g. {'region': 'us-west-1'})
:type tags: C{dict}

:return: the decorated function
:rtype: C{func}
"""

def _decorate(fn):
@functools.wraps(fn)
def wrapper(*args, **kwargs):
function_name = get_qualname(fn)
metric_name = "%s_calls" % function_name

_registry = registry or global_registry
_histogram = _registry.meter(metric_name, tags).mark()

return fn(*args, **kwargs)

return wrapper

if original_func:
return _decorate(original_func)

return _decorate


def hist_calls(original_func=None, registry=None, tags=None):
"""
Decorator to check the distribution of return values of a function.

:param original_func: the function to be decorated
:type original_func: C{func}

:param registry: the registry in which to create the histogram

:param tags: tags attached to the timer (e.g. {'region': 'us-west-1'})
:type tags: C{dict}

:return: the decorated function
:rtype: C{func}
"""
def _decorate(fn):
@functools.wraps(fn)
def wrapper(*args, **kwargs):
function_name = get_qualname(fn)
metric_name = "%s_calls" % function_name

_registry = registry or global_registry
_histogram = _registry.histogram(metric_name, tags)

rtn = fn(*args, **kwargs)
if type(rtn) in (int, float):
_histogram.update(rtn)
return rtn

return wrapper

if original_func:
return _decorate(original_func)

return _decorate


def time_calls(original_func=None, registry=None, tags=None):
"""
Decorator to time the execution of the function.

:param original_func: the function to be decorated
:type original_func: C{func}

:param registry: the registry in which to create the timer

:param tags: tags attached to the timer (e.g. {'region': 'us-west-1'})
:type tags: C{dict}

:return: the decorated function
:rtype: C{func}
"""
def _decorate(fn):
@functools.wraps(fn)
def wrapper(*args, **kwargs):
function_name = get_qualname(fn)
metric_name = "%s_calls" % function_name

_registry = registry or global_registry
_timer = _registry.timer(metric_name, tags)

with _timer.time(fn=function_name):
return fn(*args, **kwargs)

return wrapper

if original_func:
return _decorate(original_func)

return _decorate
1 change: 1 addition & 0 deletions pyformance/meters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
from .histogram import Histogram
from .timer import Timer
from .gauge import Gauge, CallbackGauge, SimpleGauge
from .base_metric import BaseMetric
27 changes: 27 additions & 0 deletions pyformance/meters/base_metric.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class BaseMetric(object):

"""
Abstract class for grouping common properties of metrics, such as tags
"""

def __init__(self, key, tags=None):
self.key = key
self.tags = tags or {}

def get_tags(self):
return self.tags

def get_key(self):
return self.key

def __hash__(self):
if not self.tags:
return hash(self.key)

return hash((self.key, frozenset(self.tags.items())))

def __eq__(self, other):
if not isinstance(other, BaseMetric):
return False

return self.key == other.key and set(self.tags) == set(other.tags)
7 changes: 4 additions & 3 deletions pyformance/meters/counter.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
from threading import Lock
from .base_metric import BaseMetric


class Counter(object):
class Counter(BaseMetric):

"""
An incrementing and decrementing metric
"""

def __init__(self):
super(Counter, self).__init__()
def __init__(self, key, tags=None):
super(Counter, self).__init__(key, tags)
self.lock = Lock()
self.counter = 0

Expand Down
13 changes: 8 additions & 5 deletions pyformance/meters/gauge.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
class Gauge(object):
from .base_metric import BaseMetric


class Gauge(BaseMetric):

"""
A base class for reading of a particular.
Expand Down Expand Up @@ -26,9 +29,9 @@ class CallbackGauge(Gauge):
A Gauge reading for a given callback
"""

def __init__(self, callback):
def __init__(self, callback, key, tags=None):
"constructor expects a callable"
super(CallbackGauge, self).__init__()
super(CallbackGauge, self).__init__(key, tags)
self.callback = callback

def get_value(self):
Expand All @@ -42,9 +45,9 @@ class SimpleGauge(Gauge):
A gauge which holds values with simple getter- and setter-interface
"""

def __init__(self, value=float("nan")):
def __init__(self, key, value=float("nan"), tags=None):
"constructor accepts initial value"
super(SimpleGauge, self).__init__()
super(SimpleGauge, self).__init__(key, tags)
self._value = value

def get_value(self):
Expand Down
15 changes: 12 additions & 3 deletions pyformance/meters/histogram.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
import time
import math
from threading import Lock
from .base_metric import BaseMetric
from ..stats.samples import ExpDecayingSample, DEFAULT_SIZE, DEFAULT_ALPHA


class Histogram(object):
class Histogram(BaseMetric):

"""
A metric which calculates the distribution of a value.
"""

def __init__(self, size=DEFAULT_SIZE, alpha=DEFAULT_ALPHA, clock=time, sample=None):
def __init__(
self,
key,
size=DEFAULT_SIZE,
alpha=DEFAULT_ALPHA,
clock=time,
sample=None,
tags=None
):
"""
Creates a new instance of a L{Histogram}.
"""
super(Histogram, self).__init__()
super(Histogram, self).__init__(key, tags)
self.lock = Lock()
self.clock = clock
if sample is None:
Expand Down
7 changes: 4 additions & 3 deletions pyformance/meters/meter.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import time
from threading import Lock
from .base_metric import BaseMetric
from ..stats.moving_average import ExpWeightedMovingAvg


class Meter(object):
class Meter(BaseMetric):

"""
A meter metric which measures mean throughput and one-, five-, and fifteen-minute
exponentially-weighted moving average throughputs.
"""

def __init__(self, clock=time):
super(Meter, self).__init__()
def __init__(self, key, clock=time, tags=None):
super(Meter, self).__init__(key, tags)
self.lock = Lock()
self.clock = clock
self.clear()
Expand Down
18 changes: 14 additions & 4 deletions pyformance/meters/timer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import time
from .base_metric import BaseMetric

try:
from blinker import Namespace
Expand All @@ -14,7 +15,7 @@
call_too_long = None


class Timer(object):
class Timer(BaseMetric):

"""
A timer metric which aggregates timing durations and provides duration statistics, plus
Expand All @@ -24,16 +25,25 @@ class Timer(object):

def __init__(
self,
key,
threshold=None,
size=DEFAULT_SIZE,
alpha=DEFAULT_ALPHA,
clock=time,
sink=None,
sample=None,
tags=None
):
super(Timer, self).__init__()
self.meter = Meter(clock=clock)
self.hist = Histogram(size=size, alpha=alpha, clock=clock, sample=sample)
super(Timer, self).__init__(key, tags)
self.meter = Meter(key=key, tags=tags, clock=clock)
self.hist = Histogram(
key=key,
tags=tags,
size=size,
alpha=alpha,
clock=clock,
sample=sample
)
self.sink = sink
self.threshold = threshold

Expand Down
Loading