需求
因近期有开发人员在跑脚本时占用系统内存太多导致系统其它进程宕掉,所以需要对系统进程进行扫描监控,如果检测到占用系统内存大于5G的进程就直接kill掉,但是担心误杀,所以暂时只做扫描并记录日志,进行观察,脚本如下:
#!/usr/bin/env python2
# -*- coding:utf-8 -*-
# 扫描所有进程内存占用量
import os
import sys
import psutil
import subprocess
import time
import logging
import logging.config
import signal
# kill内存大于5G(5242880kb)的非root用户启动/cron进程
#获取pid和进程占用内存;注意:该命令需要对特殊字符进行脱意,如:引号——\"\"
cmd = "ps aux |egrep -iv \"root|USER|CROND|redis|mysql|rabbitmq|celery\" |awk '$6>5242880{print $0}'|awk '{print $2, $6}'"
# python之subprocess模块:https://docs.python.org/2/library/subprocess.html
# https://www.cnblogs.com/yyds/p/7288916.html
s = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=True)
lines, _ = s.communicate()
# python脚本中的日志输出
## 定义名为CONF的日志格式
CONF = {
"version": 1,
"formatters": {
"simple": {
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
}, #定义日志的前缀
},
## 定义日志文件的属性
"handlers": {
"log_file": {
"class": "logging.handlers.RotatingFileHandler", #保持默认
"level": "INFO", #定义日志级别,INFO级别的日志由此输出
"formatter": "simple", #调用日志格式前缀
"filename": "/tmp/process_mem_monitor.log", #将日志输出到指定文件,不加该变量,日志将输出到终端
"maxBytes": "1024000", #定义日志文件大小
"backupCount": 20, #只保留最新的20个日志文件--类似于logrotate
"encoding": "utf8",
},
## 使用console可以将日志输出到当前终端
"console": {
"class": "logging.StreamHandler", #默认
"level": "DEBUG", #定义日志级别,DEBUG级别日志将在此输出
"formatter": "simple",
"stream": "ext://sys.stdout" #默认
},
},
## 日志过滤器
"loggers": {
"ps_logger": {
"level": "INFO",
#指定多个handlers,通过日志级别进行匹配,匹配到哪个handler就由哪种handler对应的格式输出日志
"handlers": ["log_file", "console"],
},
},
## 指定默认日志格式,即在未指定logger的时候使用该配置
"root": {
"level": "INFO",
"handler": ["console"],
},
}
# 调用日志配置
logging.config.dictConfig(CONF)
#指定日志过滤器
logger = logging.getLogger("ps_logger")
'''
#用于杀死进程
def kill(pid):
try:
a = os.kill(pid, signal.SIGKILL)
# a = os.kill(pid, signal.9) # 与上等效
print '已杀死pid为%s的进程!' % pid
except OSError, e:
print '没有如此进程!!!'
'''
# 通过pid获取进程信息
def process_stats():
for line in lines.split('\n'):
#时间戳
t = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
#过滤line为空的情况
if not line:
continue
#分别获取pid和内存使用量
pid, mem = line.split(' ')
pid = int(pid) #格式转换
try:
#使用psutil模块,通过pid获取进程信息
proc = psutil.Process(pid)
mem = int(mem) / 1024 / 1024
logger.info(u"pid: %d, process_name: %s, mem_ratio: %d, mem: %d, order: %s", pid, proc.name(), (proc.memory_percent()), mem"M", proc.exe())
#如果进程已不存在,执行该命令
except psutil.AccessDenied:
print "psutil.AccessDenied"
# kill(pid)
if __name__ == '__main__':
process_stats()
注意:
- 日志配置:
- 如果只是做临时的数据收集,不需要配置logger,直接将日志print到终端,然后使用nohup或者“>>”重定向到指定文件即可
- 日志文件的作用是做信息收集,配置好日志后需要配置logrotate对日志进行整理
- 在python脚本中尽量不要使用shell,python本身有自己的包获取系统信息,如psutil;本次之所以使用shell是因为对python不够熟悉,没找到可以获取内存使用量的python包,所以才曲线救国。。。