| # Copyright 2016 The ChromiumOS Authors | |
| # Use of this source code is governed by a BSD-style license that can be | |
| # found in the LICENSE file. | |
| """System metrics.""" | |
| from __future__ import absolute_import | |
| import errno | |
| import logging | |
| import os | |
| import time | |
| import psutil # pylint: disable=import-error | |
| from chromite.lib import metrics | |
| logger = logging.getLogger(__name__) | |
| _cpu_count_metric = metrics.GaugeMetric( | |
| "dev/cpu/count", description="Number of CPU cores." | |
| ) | |
| _cpu_time_metric = metrics.FloatMetric( | |
| "dev/cpu/time", | |
| description="percentage of time spent by the CPU " "in different states.", | |
| ) | |
| _disk_free_metric = metrics.GaugeMetric( | |
| "dev/disk/free", description="Available bytes on disk partition." | |
| ) | |
| _disk_total_metric = metrics.GaugeMetric( | |
| "dev/disk/total", description="Total bytes on disk partition." | |
| ) | |
| _inodes_free_metric = metrics.GaugeMetric( | |
| "dev/inodes/free", | |
| description="Number of available inodes on " "disk partition (unix only).", | |
| ) | |
| _inodes_total_metric = metrics.GaugeMetric( | |
| "dev/inodes/total", | |
| description="Number of possible inodes on " "disk partition (unix only)", | |
| ) | |
| _mem_free_metric = metrics.GaugeMetric( | |
| "dev/mem/free", | |
| description="Amount of memory available to a " | |
| "process (in Bytes). Buffers are considered " | |
| "free memory.", | |
| ) | |
| _mem_total_metric = metrics.GaugeMetric( | |
| "dev/mem/total", description="Total physical memory in Bytes." | |
| ) | |
| _BOOT_TIME = psutil.boot_time() | |
| _disk_read_metric = metrics.CounterMetric( | |
| "dev/disk/read", | |
| start_time=_BOOT_TIME, | |
| description="Number of Bytes read on disk.", | |
| ) | |
| _disk_write_metric = metrics.CounterMetric( | |
| "dev/disk/write", | |
| start_time=_BOOT_TIME, | |
| description="Number of Bytes written on disk.", | |
| ) | |
| _uptime_metric = metrics.GaugeMetric( | |
| "dev/uptime", description="Machine uptime, in seconds." | |
| ) | |
| _load_average_metric = metrics.FloatMetric( | |
| "dev/proc/load_average", | |
| description="Number of processes currently " "in the system run queue.", | |
| ) | |
| # ts_mon pipeline uses backend clocks when assigning timestamps to metric | |
| # points. By comparing point timestamp to the point value (i.e. time by | |
| # machine's local clock), we can potentially detect some anomalies (clock | |
| # drift, unusually high metrics pipeline delay, completely wrong clocks, etc). | |
| # | |
| # It is important to gather this metric right before the flush. | |
| _unix_time_metric = metrics.GaugeMetric( | |
| "dev/unix_time", | |
| description="Number of milliseconds since epoch" | |
| " based on local machine clock.", | |
| ) | |
| _os_name_metric = metrics.StringMetric( | |
| "proc/os/name", description="OS name on the machine" | |
| ) | |
| _os_version_metric = metrics.StringMetric( | |
| "proc/os/version", description="OS version on the machine" | |
| ) | |
| _os_arch_metric = metrics.StringMetric( | |
| "proc/os/arch", description="OS architecture on this machine" | |
| ) | |
| _python_arch_metric = metrics.StringMetric( | |
| "proc/python/arch", | |
| description="python userland " "architecture on this machine", | |
| ) | |
| def collect_uptime(): | |
| _uptime_metric.set(int(time.time() - _BOOT_TIME)) | |
| def collect_cpu_info(): | |
| _cpu_count_metric.set(psutil.cpu_count()) | |
| times = psutil.cpu_times_percent() | |
| for mode in ("user", "system", "idle"): | |
| _cpu_time_metric.set(getattr(times, mode), {"mode": mode}) | |
| def collect_disk_info(mountpoints=None): | |
| if mountpoints is None: | |
| mountpoints = [disk.mountpoint for disk in psutil.disk_partitions()] | |
| for mountpoint in mountpoints: | |
| _collect_disk_info_single(mountpoint) | |
| _collect_fs_inode_info(mountpoint) | |
| _collect_disk_io_info() | |
| def _collect_disk_info_single(mountpoint): | |
| fields = {"path": mountpoint} | |
| try: | |
| usage = psutil.disk_usage(mountpoint) | |
| except OSError as ex: | |
| if ex.errno == errno.ENOENT: | |
| # This happens on Windows when querying a removable drive that | |
| # doesn't have any media inserted right now. | |
| pass | |
| else: | |
| raise | |
| else: | |
| _disk_free_metric.set(usage.free, fields=fields) | |
| _disk_total_metric.set(usage.total, fields=fields) | |
| # inode counts are only available on Unix. | |
| if os.name == "posix": | |
| _collect_fs_inode_info(mountpoint) | |
| def _collect_fs_inode_info(mountpoint): | |
| fields = {"path": mountpoint} | |
| stats = os.statvfs(mountpoint) | |
| _inodes_free_metric.set(stats.f_favail, fields=fields) | |
| _inodes_total_metric.set(stats.f_files, fields=fields) | |
| def _collect_disk_io_info(): | |
| try: | |
| disk_counters = psutil.disk_io_counters(perdisk=True).items() | |
| except RuntimeError as ex: | |
| if "couldn't find any physical disk" in str(ex): | |
| # Disk performance counters aren't enabled on Windows. | |
| pass | |
| else: | |
| raise | |
| else: | |
| for disk, counters in disk_counters: | |
| fields = {"disk": disk} | |
| _disk_read_metric.set(counters.read_bytes, fields=fields) | |
| _disk_write_metric.set(counters.write_bytes, fields=fields) | |
| def collect_mem_info(): | |
| # We don't report mem.used because (due to virtual memory) it is not | |
| # useful. | |
| mem = psutil.virtual_memory() | |
| _mem_free_metric.set(mem.available) | |
| _mem_total_metric.set(mem.total) | |
| def collect_load_avg(): | |
| try: | |
| avg1, avg5, avg15 = os.getloadavg() | |
| except OSError: | |
| pass | |
| else: | |
| _load_average_metric.set(avg1, fields={"minutes": 1}) | |
| _load_average_metric.set(avg5, fields={"minutes": 5}) | |
| _load_average_metric.set(avg15, fields={"minutes": 15}) | |
| def collect_unix_time(): | |
| _unix_time_metric.set(int(time.time() * 1000)) |