Source code for tapes.meta
[docs]def metered_meta(metrics, base=type):
"""Creates a metaclass that will add the specified metrics at a path parametrized on the dynamic class name.
Prime use case is for base classes if all subclasses need separate metrics and / or the metrics need to be
used in base class methods, e.g., Tornado's ``RequestHandler`` like::
import tapes
import tornado
import abc
registry = tapes.Registry()
class MyCommonBaseHandler(tornado.web.RequestHandler):
__metaclass__ = metered_meta([
('latency', 'my.http.endpoints.{}.latency', registry.timer)
], base=abc.ABCMeta)
@tornado.gen.coroutine
def get(self, *args, **kwargs):
with self.latency.time():
yield self.get_impl(*args, **kwargs)
@abc.abstractmethod
def get_impl(self, *args, **kwargs):
pass
class MyImplHandler(MyCommonBaseHandler):
@tornado.gen.coroutine
def get_impl(self, *args, **kwargs):
self.finish({'stuff': 'something'})
class MyOtherImplHandler(MyCommonBaseHandler):
@tornado.gen.coroutine
def get_impl(self, *args, **kwargs):
self.finish({'other stuff': 'more of something'})
This would produce two different relevant metrics,
- ``my.http.endpoints.MyImplHandler.latency``
- ``my.http.endpoints.MyOtherImplHandler.latency``
and, as an unfortunate side effect of adding it in the base class,
a ``my.http.endpoints.MyCommonBaseHandler.latency`` too.
:param metrics: list of (attr_name, metrics_path_template, metrics_factory)
:param base: optional meta base if other than `type`
:return: a metaclass that populates the class with the needed metrics at paths based on the dynamic class name
"""
class _MeteredMeta(base):
def __new__(meta, name, bases, dict_):
new_dict = dict(**dict_)
for attr_name, template, factory in metrics:
new_dict[attr_name] = factory(template.format(name))
return super(_MeteredMeta, meta).__new__(meta, name, bases, new_dict)
return _MeteredMeta