jbi/common/instrument.py (24 lines of code) (raw):
"""Contains code common to all services
ServiceHealth: Return type that service health checks should use
InstrumentedClient: wraps service clients so that we can track their usage
"""
import logging
from functools import wraps
from typing import Sequence, Type
import backoff
from statsd.defaults.env import statsd
from jbi import environment
settings = environment.get_settings()
logger = logging.getLogger(__name__)
ServiceHealth = dict[str, bool]
def instrument(prefix: str, exceptions: Sequence[Type[Exception]], **backoff_params):
"""This decorator wraps a function such that it increments a counter every
time it is called and times its execution. It retries the function if the
specified exceptions are raised.
"""
def decorator(func):
@backoff.on_exception(
backoff.expo,
exceptions,
max_tries=settings.max_retries + 1,
**backoff_params,
)
@wraps(func)
def wrapper(*args, **kwargs):
# Increment the call counter.
statsd.incr(f"jbi.{prefix}.methods.{func.__name__}.count")
# Time its execution.
with statsd.timer(f"jbi.{prefix}.methods.{func.__name__}.timer"):
return func(*args, **kwargs)
return wrapper
return decorator