Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A new function, many typos fixing, and code re-formatting #1

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,5 @@ dmypy.json

# 自定义
result
output
output
.idea/*
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
2. 哪些进程在你不用的时候突然占用大量资源
3. 监听系统进程资源占用情况
4. 关注指定程序的资源变动情况
5. 通过邮件将监控结果发送给用户

理论上该程序跨平台,我在编码的时候也尽力使用兼容性更强的代码,所以即便出现了兼容性问题也应该很容易定位和解决。

Expand Down Expand Up @@ -39,7 +40,7 @@ python start.py -h

```text
$ python start.py -h
usage: start.py [-h] [-o OUTPUT] [-i INTERVAL] [-f [FILTER ...]]
usage: start.py [-h] [-o OUTPUT] [-i INTERVAL] [-f [FILTER ...]] [-m]

optional arguments:
-h, --help show this help message and exit
Expand All @@ -49,6 +50,17 @@ optional arguments:
监听时间间隔(毫秒)
-f [FILTER ...], --filter [FILTER ...]
限制监听范围,为进程名(如explorer.exe)
-m False/True, --mail False/True
是否使用邮件通知
-a ADDRESS, --address ADDRESS
邮件发送方地址
-n NAME, --name NAME
邮件发送方地址
-p PASSWORD, --password PASSWORD
邮件发送方密钥
-r RECEIVER, --receiver RECEIVER
邮件接收方地址

```

#### 监听所有的进程
Expand Down Expand Up @@ -89,6 +101,14 @@ python start.py -f notepad.exe Taskmgr.exe -i 1000

默认情况下会在当前目录的`output`目录下生成结果,里面主要有两项:`summury.txt`文件和`process`文件夹。前者主要记录了一些宏观统计信息。后者则包含了众多被监听的进程数据。数据以`html`的形式提供,每个文件名均为`进程名.html`格式,每个网页内包含了三张图表,可自行查看。

#### 邮件通知

该功能基于[iMail](https://github.com/mtics/iMail)

- 首先需要用户通过指令`-m True`或者`--mail True`来允许使用邮件通知功能
- 然后用户需要通过指令进行相关配置,详情请见代码。
- 目前通知会在用户停止监听后,将相关输出发送至指定邮箱。

## 如何贡献

你可以提交`Issue`或者`Fork+PR`的方式提出你的想法和修改,我会不定期的查看并作出修改。当然,你也可以完全根据项目所属协议进行合法操作。
5 changes: 2 additions & 3 deletions common.py → SRM/common.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@

# Bytes -> KB MB GB
def trans_B2KB(val):
return float(val) / 1024


def trans_B2MB(val):
return trans_B2KB(val)/1024
return trans_B2KB(val) / 1024


def trans_B2GB(val):
return trans_B2MB(val)/1024
return trans_B2MB(val) / 1024
63 changes: 37 additions & 26 deletions exporter.py → SRM/exporter.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,53 @@
import datetime
import os

from pyecharts import charts
from pyecharts import options as opts
import os
import datetime


def resource_chart(page_title, title, subtitle, x_data, y_data, y_label=''):
line_chart = charts.Line(
opts.InitOpts(page_title=page_title)
).set_global_opts(
title_opts=opts.TitleOpts(title=title, subtitle=subtitle)
)

line_chart.add_xaxis(xaxis_data=x_data)
line_chart.add_yaxis(y_label,
y_axis=y_data,
markline_opts=opts.MarkLineOpts(
data=[opts.MarkLineItem(type_="max"), opts.MarkLineItem(type_="average")]),
label_opts=opts.LabelOpts(is_show=False)
)

return line_chart


def exportCharts(process_name, data, output_path):
page = charts.Page(
layout=charts.Page.DraggablePageLayout)

page.page_title = "{} 统计信息".format(process_name)
line_cpu = charts.Line(
opts.InitOpts(page_title="{} CPU占用(%)".format(process_name)))\
.set_global_opts(title_opts=opts.TitleOpts(title="CPU占用信息", subtitle="单位: %"))
line_cpu.add_xaxis(xaxis_data=data["Time"])
line_cpu.add_yaxis("CPU占用(%)", y_axis=data["CPU"], markline_opts=opts.MarkLineOpts(
data=[opts.MarkLineItem(type_="max"), opts.MarkLineItem(type_="average")]), label_opts=opts.LabelOpts(is_show=False))

line_mem = charts.Line(
opts.InitOpts(page_title="{} 内存占用(MB)".format(process_name)))\
.set_global_opts(title_opts=opts.TitleOpts(title="内存占用信息", subtitle="单位: MB"))
line_mem.add_xaxis(xaxis_data=data["Time"])
line_mem.add_yaxis("内存(MB)", y_axis=data["MEM"], markline_opts=opts.MarkLineOpts(
data=[opts.MarkLineItem(type_="max"), opts.MarkLineItem(type_="average")]), label_opts=opts.LabelOpts(is_show=False))

line_io = charts.Line(
opts.InitOpts(page_title="{} IO写(MB)".format(process_name)))\
.set_global_opts(title_opts=opts.TitleOpts(title="IO信息", subtitle="单位: MB"))
line_io.add_xaxis(xaxis_data=data["Time"])
line_io.add_yaxis("IO写(MB)", y_axis=data["IO"], markline_opts=opts.MarkLineOpts(
data=[opts.MarkLineItem(type_="max"), opts.MarkLineItem(type_="average")]), label_opts=opts.LabelOpts(is_show=False))

line_cpu = resource_chart(page_title="{} CPU占用(%)".format(process_name),
title="CPU占用信息", subtitle="单位: %",
x_data=data["Time"], y_data=data["CPU"], y_label="CPU占用(%)")

line_mem = resource_chart(page_title="{} 内存占用(MB)".format(process_name),
title="内存占用信息", subtitle="单位: MB",
x_data=data["Time"], y_data=data["MEM"], y_label="内存(MB)")

line_io = resource_chart(page_title="{} IO写(MB)".format(process_name),
title="IO信息", subtitle="单位: MB",
x_data=data["Time"], y_data=data["IO"], y_label="IO写(MB)")

page.add(line_cpu, line_mem, line_io)
page.render(os.path.join(output_path, "{}.html".format(process_name)))


def export(data_dict: dict, output_path: str, interval):
process_dat_path = os.path.join(output_path, "process")
summury_txt_path = os.path.join(output_path, "summury.txt")
summary_txt_path = os.path.join(output_path, "summary.txt")

os.makedirs(process_dat_path, exist_ok=True)

Expand All @@ -50,8 +61,8 @@ def export(data_dict: dict, output_path: str, interval):
stat = pm.getStatistic()
exportCharts(process_name, stat, process_dat_path)

# 统计summury
if(stat["END_TIME"] != None):
# 统计summary
if stat["END_TIME"] != None:
close_process_set.add(process_name)

if stop_timestamp < stat["END_TIME"]:
Expand All @@ -67,7 +78,7 @@ def export(data_dict: dict, output_path: str, interval):
# 之后启动的
start_process_set.add(process_name)

with open(summury_txt_path, "w") as f:
with open(summary_txt_path, "w", encoding='utf-8') as f:
f.write("======== 汇总 ========\n")
f.write("报告时间:{}\n".format(datetime.datetime.now()))
f.write("统计时间段:{} - {}\n".format(start_timestamp, stop_timestamp))
Expand Down
65 changes: 65 additions & 0 deletions SRM/mail.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import glob
import os

import iMail


class MAIL(object):

def __init__(self, args):

# the information for enable mailing
self.__sender = {
'host': args.mail_server,
'address': args.sender_addr,
'name': args.sender_name,
'pwd': args.pwd,
'receivers': args.receivers,
}

self.__output_path = args.output # the path of outputs
self.__enable = args.mail # whether the mailing system is enabled

def send_mail(self, subject='监控统计', content='', attachments=None):
"""
Package process monitoring results and send them to users via email
"""

# Create an email object for iMail
mail_system = iMail.EMAIL(host=self.__sender['host'], sender_addr=self.__sender['address'],
pwd=self.__sender['pwd'], sender_name=self.__sender['name'])

# Set the receiver list
mail_system.set_receiver(self.__sender['receivers'])

# New an email
mail_system.new_mail(subject=subject, encoding='utf-8')

# If user does not set the content,
# use the content of 'summary.txt';
# Else, attach the file and sent it through email
if content == '':
with open(os.path.join(self.__output_path, 'summary.txt'), 'r', encoding='utf-8') as file:
mail_system.add_text(content=file.read())
else:
mail_system.add_text(content=content)
mail_system.attach_files(os.path.join(self.__output_path, 'summary.txt'))

# Attach all output files or the specified files
if attachments == None:
files = glob.glob(os.path.join(self, 'process/*'))
mail_system.attach_files(files)
else:
mail_system.attach_files(attachments)

mail_system.send_mail()

def mail(self, subject='监控统计', content='', attachments=None):

if self.__enable:
print("邮件发送中,请等待...")
try:
self.send_mail(subject, content, attachments)
print("邮件发送成功")
except Exception as e:
print("邮件发送失败: {},请自行查看".format(e))
39 changes: 20 additions & 19 deletions monitor.py → SRM/monitor.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import psutil
import common
import datetime
import platform
import threading
import time
from collections import defaultdict
import datetime
import exporter
import platform

import psutil

from SRM import common, exporter


def showSystemInfo():
Expand All @@ -30,9 +31,9 @@ def showSystemInfo():


class ProcessMonitor:
'''
"""
进程监视器,根据进程名归并,即将子进程并入父进程计算
'''
"""

def __init__(self, process_name: str) -> None:
self.__process_name = process_name
Expand Down Expand Up @@ -67,7 +68,7 @@ def getStatistic(self):
def __monitorThread(self):
process_map = {}

while(self.__is_running):
while self.__is_running:
pid_num = 0
cpu_loaded = 0.0
mem = 0.0
Expand All @@ -79,7 +80,7 @@ def __monitorThread(self):
try:
process_map[pid] = psutil.Process(pid=pid)
process = process_map[pid]
if(process.name() != self.__process_name):
if process.name() != self.__process_name:
# 认为pid被复用了
continue
except psutil.NoSuchProcess as e:
Expand All @@ -103,7 +104,7 @@ def __monitorThread(self):
process_map.clear()

self.__recordStat(cpu_loaded, mem, io)
time.sleep(self.__interval/1000)
time.sleep(self.__interval / 1000)

def __clearStatistic(self):
self.__statistic.clear()
Expand All @@ -128,11 +129,11 @@ def __init__(self) -> None:
self.__thread = None
self.__process_map = defaultdict(ProcessMonitor) # 存放各种进程管理器

def start(self, interval, filter=set()):
def start(self, interval, thread_filter=set()):
self.__is_running = True
self.__interval = interval
self.__thread = threading.Thread(
target=self.__threadFunc, args=(set(filter),))
target=self.__threadFunc, args=(set(thread_filter),))
self.__thread.start()

def export(self, data_path):
Expand All @@ -144,18 +145,18 @@ def stop(self):
self.__is_running = False
self.__thread.join()

def __threadFunc(self, filter):
'''
def __threadFunc(self, thread_filter):
"""
子线程函数,用于扫描系统所有的进程,并且归类。再将进程分发给子进程进行监听
'''
"""
# 开启子进程
while(self.__is_running):
while self.__is_running:
for process_name, ids in self.__getProcessesInfo().items():
if(len(filter) > 0 and process_name not in filter):
if len(thread_filter) > 0 and process_name not in thread_filter:
continue
self.__distributeProcessName(process_name, ids)

time.sleep(self.__interval/1000)
time.sleep(self.__interval / 1000)

# 取消所有线程和子进程
for _, pm in self.__process_map.items():
Expand Down Expand Up @@ -184,6 +185,6 @@ def __distributeProcessName(self, process_name: str, ids: set):
self.__process_map.get(process_name).setPidSet(ids)

def __processMonitorFunc(self):
while(self.__is_running):
while self.__is_running:
pass
# 取消所有监听
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
psutil
pyecharts
pyecharts
iMail
Loading