Snippets

saaj Simple CPU and RSS monitor that pushes to statsd

Created by saaj last modified
#!/usr/bin/env python

import re
import time
import argparse
import logging

import psutil
import statsd


def find_process(pid, regex):
    if pid:
        try:
            return psutil.Process(pid)
        except psutil.NoSuchProcess:
            pass
    elif regex:
        current_pid = psutil.Process().pid
        for p in psutil.process_iter():
            if regex.search(' '.join(p.cmdline())) and p.pid != current_pid:
                return p
    else:
        raise ValueError('No pid or regex')


def main(host, prefix, interval, pid, regex):
    client = statsd.StatsClient(host=host, prefix='.'.join(filter(bool, ['procmon', prefix])))
    while True:
        try:
            proc = find_process(pid, regex)
            if not proc:
                logging.warning('No target process found')
                time.sleep(interval)
                continue
            else:
                logging.debug('Found process %s, collecting metrics', proc)

            try:
                pct = proc.cpu_percent(interval) # the call is blocking
                mem = proc.memory_info().rss
            except psutil.NoSuchProcess:
                logging.warning('Target process was terminated during collection')
            else:
                client.gauge('cpu', pct)
                client.gauge('rss', mem)
                logging.debug('Sent CPU:%.0f%% RSS:%d', pct, mem)
        except KeyboardInterrupt:
            break


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Process monitor')
    parser.add_argument('-p', '--pid', help='Target process pid', default=0)
    parser.add_argument('-r', '--regex', help='Target process cmdline regex', default='')
    parser.add_argument('-f', '--prefix', help='Statsd prefix', default='')
    parser.add_argument('-s', '--host', help='Statsd host', default='localhost')
    parser.add_argument('-i', '--interval', help='Collection interval in seconds', default=1)
    parser.add_argument('-v', '--verbose', help='Be verbose', action='store_true', default=False)

    args = parser.parse_args()
    if not any([args.pid, args.regex]):
        parser.error('Either PID or cmdline regex must be provided')

    level = logging.DEBUG if args.verbose else logging.INFO
    logging.basicConfig(level=level, format='%(asctime)s %(levelname)s %(name)s %(message)s')

    main(args.host, args.prefix, float(args.interval), int(args.pid), re.compile(args.regex))

Comments (1)