|
6 | 6 | from notebook.utils import url_path_join |
7 | 7 | from notebook.base.handlers import IPythonHandler |
8 | 8 | from tornado import web |
| 9 | + |
9 | 10 | try: |
10 | 11 | # Traitlets >= 4.3.3 |
11 | 12 | from traitlets import Callable |
12 | 13 | except ImportError: |
13 | 14 | from .callable import Callable |
14 | 15 |
|
| 16 | +from threading import Thread |
| 17 | +from concurrent.futures import ThreadPoolExecutor, as_completed |
15 | 18 |
|
16 | 19 | class MetricsHandler(IPythonHandler): |
| 20 | + def initialize(self): |
| 21 | + super().initialize() |
| 22 | + self.cpu_percent = 0 |
| 23 | + # A better approach would use cpu_affinity to account for the |
| 24 | + # fact that the number of logical CPUs in the system is not |
| 25 | + # necessarily the same as the number of CPUs the process |
| 26 | + # can actually use. But cpu_affinity isn't available for OS X. |
| 27 | + self.cpu_count = psutil.cpu_count() |
| 28 | + |
| 29 | + def update_cpu_percent(): |
| 30 | + def get_cpu_percent(p): |
| 31 | + try: |
| 32 | + return p.cpu_percent(interval=0.1) |
| 33 | + # Avoid littering logs with stack traces complaining |
| 34 | + # about dead processes having no CPU usage |
| 35 | + except: |
| 36 | + return 0 |
| 37 | + # This loop should execute roughly every "interval" seconds |
| 38 | + # Slower if max_workers is much less than the number of processes |
| 39 | + while True: |
| 40 | + cur_process = psutil.Process() |
| 41 | + all_processes = [cur_process] + cur_process.children(recursive=True) |
| 42 | + # Could have a worker for every process |
| 43 | + with ThreadPoolExecutor(max_workers=10) as executor: |
| 44 | + cpu_percents = [executor.submit(get_cpu_percent, p) for p in all_processes] |
| 45 | + total_percent = 0 |
| 46 | + for future in as_completed(cpu_percents): |
| 47 | + try: |
| 48 | + total_percent += future.result() |
| 49 | + except: |
| 50 | + pass |
| 51 | + self.cpu_percent = total_percent |
| 52 | + |
| 53 | + t = Thread(target=update_cpu_percent) |
| 54 | + t.start() |
| 55 | + |
17 | 56 | @web.authenticated |
18 | 57 | def get(self): |
19 | 58 | """ |
@@ -61,8 +100,8 @@ def get_cpu_percent(p): |
61 | 100 |
|
62 | 101 | metrics = { |
63 | 102 | 'rss': rss, |
64 | | - 'cpu_percent': cpu_percent, |
65 | | - 'cpu_count': cpu_count, |
| 103 | + 'cpu_percent': self.cpu_percent, |
| 104 | + 'cpu_count': self.cpu_count, |
66 | 105 | 'limits': limits, |
67 | 106 | } |
68 | 107 | self.write(json.dumps(metrics)) |
|
0 commit comments