| # -*- coding: utf-8 -*- |
| # Copyright 2016 The Chromium OS Authors. All rights reserved. |
| # 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 |
| from __future__ import print_function |
| |
| import errno |
| import os |
| import time |
| |
| import psutil # pylint: disable=import-error |
| |
| from chromite.lib import cros_logging as logging |
| 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)) |