| 1 | """ |
|---|
| 2 | Ported to Python 3. |
|---|
| 3 | """ |
|---|
| 4 | |
|---|
| 5 | from collections import deque |
|---|
| 6 | from time import process_time |
|---|
| 7 | import time |
|---|
| 8 | from typing import Deque, Tuple |
|---|
| 9 | |
|---|
| 10 | from twisted.application import service |
|---|
| 11 | from twisted.application.internet import TimerService |
|---|
| 12 | from zope.interface import implementer |
|---|
| 13 | |
|---|
| 14 | from allmydata.util import log, dictutil |
|---|
| 15 | from allmydata.interfaces import IStatsProducer |
|---|
| 16 | |
|---|
| 17 | @implementer(IStatsProducer) |
|---|
| 18 | class CPUUsageMonitor(service.MultiService): |
|---|
| 19 | HISTORY_LENGTH: int = 15 |
|---|
| 20 | POLL_INTERVAL: float = 60 |
|---|
| 21 | initial_cpu: float = 0.0 |
|---|
| 22 | |
|---|
| 23 | def __init__(self): |
|---|
| 24 | service.MultiService.__init__(self) |
|---|
| 25 | self.samples: Deque[Tuple[float, float]] = deque([], self.HISTORY_LENGTH + 1) |
|---|
| 26 | # we provide 1min, 5min, and 15min moving averages |
|---|
| 27 | TimerService(self.POLL_INTERVAL, self.check).setServiceParent(self) |
|---|
| 28 | |
|---|
| 29 | def startService(self): |
|---|
| 30 | self.initial_cpu = process_time() |
|---|
| 31 | return super().startService() |
|---|
| 32 | |
|---|
| 33 | def check(self): |
|---|
| 34 | now_wall = time.time() |
|---|
| 35 | now_cpu = process_time() |
|---|
| 36 | self.samples.append( (now_wall, now_cpu) ) |
|---|
| 37 | |
|---|
| 38 | def _average_N_minutes(self, size): |
|---|
| 39 | if len(self.samples) < size+1: |
|---|
| 40 | return None |
|---|
| 41 | first = -size-1 |
|---|
| 42 | elapsed_wall = self.samples[-1][0] - self.samples[first][0] |
|---|
| 43 | elapsed_cpu = self.samples[-1][1] - self.samples[first][1] |
|---|
| 44 | fraction = elapsed_cpu / elapsed_wall |
|---|
| 45 | return fraction |
|---|
| 46 | |
|---|
| 47 | def get_stats(self): |
|---|
| 48 | s = {} |
|---|
| 49 | avg = self._average_N_minutes(1) |
|---|
| 50 | if avg is not None: |
|---|
| 51 | s["cpu_monitor.1min_avg"] = avg |
|---|
| 52 | avg = self._average_N_minutes(5) |
|---|
| 53 | if avg is not None: |
|---|
| 54 | s["cpu_monitor.5min_avg"] = avg |
|---|
| 55 | avg = self._average_N_minutes(15) |
|---|
| 56 | if avg is not None: |
|---|
| 57 | s["cpu_monitor.15min_avg"] = avg |
|---|
| 58 | now_cpu = process_time() |
|---|
| 59 | s["cpu_monitor.total"] = now_cpu - self.initial_cpu |
|---|
| 60 | return s |
|---|
| 61 | |
|---|
| 62 | |
|---|
| 63 | class StatsProvider(service.MultiService): |
|---|
| 64 | |
|---|
| 65 | def __init__(self, node): |
|---|
| 66 | service.MultiService.__init__(self) |
|---|
| 67 | self.node = node |
|---|
| 68 | |
|---|
| 69 | self.counters = dictutil.UnicodeKeyDict() |
|---|
| 70 | self.stats_producers = [] |
|---|
| 71 | self.cpu_monitor = CPUUsageMonitor() |
|---|
| 72 | self.cpu_monitor.setServiceParent(self) |
|---|
| 73 | self.register_producer(self.cpu_monitor) |
|---|
| 74 | |
|---|
| 75 | def count(self, name, delta=1): |
|---|
| 76 | val = self.counters.setdefault(name, 0) |
|---|
| 77 | self.counters[name] = val + delta |
|---|
| 78 | |
|---|
| 79 | def register_producer(self, stats_producer): |
|---|
| 80 | self.stats_producers.append(IStatsProducer(stats_producer)) |
|---|
| 81 | |
|---|
| 82 | def get_stats(self): |
|---|
| 83 | stats = {} |
|---|
| 84 | for sp in self.stats_producers: |
|---|
| 85 | stats.update(sp.get_stats()) |
|---|
| 86 | ret = { 'counters': self.counters, 'stats': stats } |
|---|
| 87 | log.msg(format='get_stats() -> %(stats)s', stats=ret, level=log.NOISY) |
|---|
| 88 | return ret |
|---|