Overview
This blog post discusses collecting process level CPU and memory statistics. This one is going to be quick - here is a tiny Python script which collects process level CPU and memory stats and outputs it on console or to a specified file.
Using the script
Pre-requisites
- Python3
- Tested on both Windows and Linux
- Install psutil Python library
Install psutil
pip3 install psutil
Using the script
Syntax: To view detailed help: python3 collect --help To collect process metrics: python3 <list of process-names> <collection-interval-sec> <output-filepath>
Suppose I want to monitor the CPU and memory consumption of the firefox.exe process every 5 seconds at the console, I can invoke the script as:
python3 collect firefox,edge 5
I can also provide a file where the same information will be written:
python3 collect firefox,edge 5 metrics.logYou could of course redirect the console output to a file instead of specifiying a file as a parameter.
The code
# Author : Siddharth Barman # Date : 2-JULY-2024 # Description : Program to collect process statistics into a file or console. import psutil as ps import sys, time ARG_PROCESS_LIST = 1 ARG_INTERVAL_SEC = 2 ARG_OUTPUT_FILE = 3 def default(): print("Collect process metrics.") print("Syntax:") print("To view detailed help: run python3 collect --help") def help(): print("Collect process metrics.") print("Syntax:") print("To view detailed help:") print(" python3 collect --help") print("To collect process metrics: ") print(" python3 <list of process-names> <collection-interval-sec> <output-filepath>") print(" python3 collect firefox,edge 5 ./logs/metrics.log") def print_or_write(file_handle, string): if file_handle == None: print(string) else: file_handle.write(string) file_handle.write("\n") def is_process_in_list(process_name, process_name_list): process_name = process_name.lower() for pname in process_name_list: proc_name = pname.lower() if process_name == proc_name: return True return False def collect(process_names, interval_seconds, output_filepath): if output_filepath != None: f = open(output_filepath, 'w') else: f = None try: process_info = {} while True: for pid in ps.pids(): try: p = ps.Process(pid) if is_process_in_list(p.name(), process_names): line = "pid={0}, name={1}, CPU={2}, Mem={3}, args={4}".format(pid, p.name(), p.cpu_percent(interval=1) / ps.cpu_count(), p.memory_full_info().uss, p.cmdline()[0]) print_or_write(f, line) except: pass time.sleep(interval_seconds) except KeyboardInterrupt as k: if output_filepath != None: f.close() except: if output_filepath != None: f.close() raise def main(): if len(sys.argv) == 1: default() exit() if (sys.argv[1] == "--help"): help() exit() process_names = sys.argv[ARG_PROCESS_LIST].split(",") interval_seconds = int(sys.argv[ARG_INTERVAL_SEC]) output_filepath = None if len(sys.argv) > 3: output_filepath = sys.argv[ARG_OUTPUT_FILE] collect(process_names, interval_seconds, output_filepath) main()
There are a number of additional features I think this script could have like - monitoring a set of PIDs instead of having to provide a process name. If you have other ideas, I would be interested to know about them. That's all for now, have a great day!