Mercurial > code > home > repos > rdfdb
view rdfdb/metrics.py @ 121:8a0559bcbe7e
shorthand import
author | drewp@bigasterisk.com |
---|---|
date | Wed, 24 May 2023 12:49:01 -0700 |
parents | 32e2c91eeaf3 |
children |
line wrap: on
line source
# copy from light9 version- lib me """for easier porting, and less boilerplate, allow these styles using the form of the call to set up the right type of metric automatically: from metrics import metrics metrics.setProcess('pretty_name') @metrics('loop').time() # a common one to get the fps of each service. Gets us qty and time def frame(): if err: metrics('foo_errors').incr() # if you incr it, it's a counter @metrics('foo_calls').time() # qty & time because it's a decorator def foo(): metrics('goal_fps').set(f) # a gauge because we called set() with metrics('recompute'): ... # ctxmgr also makes a timer time_this_part() I don't see a need for labels yet, but maybe some code will want like metrics('foo', label1=one). Need histogram? Info? """ from typing import Dict, Tuple, Callable, Type, TypeVar, cast import cyclone.web from prometheus_client import Counter, Gauge, Metric, Summary from prometheus_client.exposition import generate_latest from prometheus_client.registry import REGISTRY _created: Dict[str, Metric] = {} # _process=sys.argv[0] # def setProcess(name: str): # global _process # _process = name MT = TypeVar("MT") class _MetricsRequest: def __init__(self, name: str, **labels): self.name = name self.labels = labels def _ensure(self, cls: Type[MT]) -> MT: if self.name not in _created: _created[self.name] = cls(name=self.name, documentation=self.name, labelnames=self.labels.keys()) m = _created[self.name] if self.labels: m = m.labels(**self.labels) return m def __call__(self, fn) -> Callable: return timed_fn def set(self, v: float): self._ensure(Gauge).set(v) def inc(self): self._ensure(Counter).inc() def offset(self, amount: float): self._ensure(Gauge).inc(amount) def time(self): return self._ensure(Summary).time() def observe(self, x: float): return self._ensure(Summary).observe(x) def __enter__(self): return self._ensure(Summary).__enter__() def metrics(name: str, **labels): return _MetricsRequest(name, **labels) class _CycloneMetrics(cyclone.web.RequestHandler): def get(self): self.add_header('content-type', 'text/plain') self.write(generate_latest(REGISTRY)) def metricsRoute() -> Tuple[str, Type[cyclone.web.RequestHandler]]: return ('/metrics', _CycloneMetrics) """ stuff we used to have in greplin. Might be nice to get (client-side-computed) min/max/stddev back. class PmfStat(Stat): A stat that stores min, max, mean, standard deviation, and some percentiles for arbitrary floating-point data. This is potentially a bit expensive, so its child values are only updated once every twenty seconds. metrics consumer side can do this with the changing counts: class RecentFps(object): def __init__(self, window=20): self.window = window self.recentTimes = [] def mark(self): now = time.time() self.recentTimes.append(now) self.recentTimes = self.recentTimes[-self.window:] def rate(self): def dec(innerFunc): def f(*a, **kw): self.mark() return innerFunc(*a, **kw) return f return dec def __call__(self): if len(self.recentTimes) < 2: return {} recents = sorted(round(1 / (b - a), 3) for a, b in zip(self.recentTimes[:-1], self.recentTimes[1:])) avg = (len(self.recentTimes) - 1) / ( self.recentTimes[-1] - self.recentTimes[0]) return {'average': round(avg, 5), 'recents': recents} i think prometheus covers this one: import psutil def gatherProcessStats(): procStats = scales.collection('/process', scales.DoubleStat('time'), scales.DoubleStat('cpuPercent'), scales.DoubleStat('memMb'), ) proc = psutil.Process() lastCpu = [0.] def updateTimeStat(): now = time.time() procStats.time = round(now, 3) if now - lastCpu[0] > 3: procStats.cpuPercent = round(proc.cpu_percent(), 6) # (since last call) lastCpu[0] = now procStats.memMb = round(proc.memory_info().rss / 1024 / 1024, 6) task.LoopingCall(updateTimeStat).start(.1) """ class M: def __call__(self, name): return