more_executors/_impl/metrics/__init__.py
import os
import logging
from functools import partial
try:
from time import monotonic
except ImportError: # pragma: no cover
from monotonic import monotonic
from .null import NullMetrics
LOG = logging.getLogger("more-executors.metrics")
if os.environ.get("MORE_EXECUTORS_PROMETHEUS", "1") == "0":
metrics = NullMetrics() # type: ignore
else:
try: # pylint: disable=import-error
from .prometheus import PrometheusMetrics
metrics = PrometheusMetrics()
except Exception:
LOG.debug("disabling prometheus support", exc_info=True)
metrics = NullMetrics() # type: ignore
def record_done(f, started_when, time, inprogress, cancelled, failed):
inprogress.dec()
run_time = monotonic() - started_when
time.inc(run_time)
if f.cancelled():
cancelled.inc()
elif f.exception():
failed.inc()
def track_future(f, **labels):
if "executor" not in labels:
# TODO: when using the future composition functions like
# f_or, f_map etc, we'll often get here. But it might be possible
# to do something better? e.g. if we have one future chained from
# another, can we copy the label from the other?
labels["executor"] = "default"
metrics.FUTURE_TOTAL.labels(**labels).inc()
start = monotonic()
inprogress = metrics.FUTURE_INPROGRESS.labels(**labels)
inprogress.inc()
time = metrics.FUTURE_TIME.labels(**labels)
cancelled = metrics.FUTURE_CANCEL.labels(**labels)
failed = metrics.FUTURE_ERROR.labels(**labels)
cb = partial(
record_done,
started_when=start,
time=time,
inprogress=inprogress,
cancelled=cancelled,
failed=failed,
)
f.add_done_callback(cb)
return f
def track_future_noop(f, **_labels):
return f
if isinstance(metrics, NullMetrics):
track_future = track_future_noop