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 |
---|