界面如下,丑是丑了点,但主打一个实用。

主要就是通过ping,就是一直在ping,当不通的时候,就根据你设置的报警时间,主要是利用飞书机器人来给飞书指定群里发异常信息报警。
直接上代码
python
import subprocess
import time
import requests
import threading
import logging
import tkinter as tk
from tkinter import messagebox, simpledialog
import json
import os
import sys
# 获取程序所在目录
if getattr(sys, 'frozen', False): # 如果程序被打包成exe
PROGRAM_DIR = os.path.dirname(sys.executable) # 使用exe所在目录
else: # 如果是脚本运行
PROGRAM_DIR = os.path.dirname(os.path.abspath(__file__)) # 使用脚本所在目录
# 日志文件路径
LOG_FILE = os.path.join(PROGRAM_DIR, "ip_monitor.log")
CONFIG_FILE = os.path.join(PROGRAM_DIR, "config.json")
# 配置日志
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler(LOG_FILE, encoding="utf-8"), # 日志写入文件
logging.StreamHandler() # 日志输出到控制台
]
)
# 输出日志和配置文件的路径,方便调试
logging.info(f"程序所在目录: {PROGRAM_DIR}")
logging.info(f"日志文件路径: {LOG_FILE}")
logging.info(f"配置文件路径: {CONFIG_FILE}")
class IPMonitor:
def __init__(self):
self.running = False
self.thread = None
self.start_time = None # 记录监控启动时间
self.last_normal_notify_time = {} # 记录每个目标最后一次正常通知的时间
def ping_target(self, target, ping_count, ping_size):
"""Ping目标(IP或域名),返回是否连通,并在日志中显示详细过程"""
try:
# 执行Ping命令,捕获输出
result = subprocess.run(
['ping', '-n', str(ping_count), '-l', str(ping_size), '-w', '1000', target],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
creationflags=subprocess.CREATE_NO_WINDOW # 隐藏窗口(仅Windows有效)
)
# 将Ping命令的完整输出写入日志
logging.info(f"Ping {target} 过程:\n{result.stdout}")
if result.stderr:
logging.error(f"Ping {target} 错误:\n{result.stderr}")
# 检查输出中是否包含"回复"或"Reply"
if "回复" in result.stdout or "Reply" in result.stdout:
return True
else:
return False
except Exception as e:
logging.error(f"Ping命令执行错误 {target}: {e}")
return False
def send_feishu_message(self, webhook_url, message):
"""发送飞书群消息"""
try:
response = requests.post(webhook_url, json={"msg_type": "text", "content": {"text": message}})
if response.status_code == 200:
logging.info("消息发送成功")
else:
logging.error(f"消息发送失败: {response.status_code}, {response.text}")
except Exception as e:
logging.error(f"发送消息错误: {e}")
finally:
if 'response' in locals():
response.close()
def monitor_targets(self, target_info_list, webhook_url, monitor_interval, enable_normal_notify, normal_notify_interval, ping_count, ping_size):
"""监控目标列表(IP或域名),根据设置发送通知"""
while self.running:
for target_info in target_info_list:
target, comment = target_info
if not self.ping_target(target, ping_count, ping_size):
self.send_feishu_message(webhook_url, f"🛑 检测到目标无法访问:{target} ({comment}),请检查!")
else:
current_time = time.time()
# 检查是否启用了正常通知,并且满足时间间隔
if enable_normal_notify and (
target not in self.last_normal_notify_time or
(current_time - self.last_normal_notify_time.get(target, 0)) >= normal_notify_interval * 60
):
self.send_feishu_message(webhook_url, f"✅ 目标 {target} ({comment}) 正常")
self.last_normal_notify_time[target] = current_time
logging.info(f"✅ 目标 {target} ({comment}) 正常")
time.sleep(monitor_interval) # 根据设置的间隔检查
def start(self, target_info_list, webhook_url, monitor_interval, enable_normal_notify, normal_notify_interval, ping_count, ping_size):
"""启动监控"""
if self.running:
logging.warning("监控已在运行中")
return
self.running = True
self.start_time = time.time() # 记录启动时间
self.thread = threading.Thread(
target=self.monitor_targets,
args=(target_info_list, webhook_url, monitor_interval, enable_normal_notify, normal_notify_interval, ping_count, ping_size)
)
self.thread.daemon = True # 设置为守护线程,主线程退出时自动结束
self.thread.start()
logging.info("监控已启动")
def stop(self):
"""停止监控"""
self.running = False
if self.thread:
self.thread.join(timeout=1) # 最多等待1秒让线程退出
if self.thread.is_alive():
logging.warning("监控线程未能及时退出,强制终止")
self.start_time = None # 清空启动时间
logging.info("监控已停止")
class IPMonitorGUI:
def __init__(self, root):
self.root = root
self.root.title("IP和域名监控工具")
self.root.geometry("500x600")
self.monitor = IPMonitor()
# 加载配置文件
self.config = self.load_config()
# 飞书Webhook地址输入
self.label_webhook = tk.Label(root, text="飞书Webhook地址:")
self.label_webhook.pack(pady=5)
self.button_set_webhook = tk.Button(root, text="设置飞书Webhook地址", command=self.set_webhook)
self.button_set_webhook.pack(pady=5)
# 目标(IP或域名)输入框(带滚动条)
self.label_target = tk.Label(root, text="需要监控的目标(每行一个,格式:IP/域名 | 备注):")
self.label_target.pack(pady=5)
self.frame_target = tk.Frame(root)
self.frame_target.pack(pady=5)
self.text_target = tk.Text(self.frame_target, height=10, width=50)
self.text_target.pack(side=tk.LEFT, fill=tk.BOTH)
self.scroll_target = tk.Scrollbar(self.frame_target, command=self.text_target.yview)
self.scroll_target.pack(side=tk.RIGHT, fill=tk.Y)
self.text_target.config(yscrollcommand=self.scroll_target.set)
if "target_info_list" in self.config:
self.text_target.insert(tk.END, "\n".join([" | ".join(info) for info in self.config["target_info_list"]]))
# Ping包次数设置
self.label_ping_count = tk.Label(root, text="Ping包次数:")
self.label_ping_count.pack(pady=5)
self.entry_ping_count = tk.Entry(root)
self.entry_ping_count.pack(pady=5)
self.entry_ping_count.insert(0, str(self.config.get("ping_count", 1)))
# Ping包大小设置
self.label_ping_size = tk.Label(root, text="Ping包大小(字节):")
self.label_ping_size.pack(pady=5)
self.entry_ping_size = tk.Entry(root)
self.entry_ping_size.pack(pady=5)
self.entry_ping_size.insert(0, str(self.config.get("ping_size", 32)))
# 报警时间间隔设置
self.label_monitor_interval = tk.Label(root, text="报警时间间隔(秒):")
self.label_monitor_interval.pack(pady=5)
self.entry_monitor_interval = tk.Entry(root)
self.entry_monitor_interval.pack(pady=5)
self.entry_monitor_interval.insert(0, str(self.config.get("monitor_interval", 30)))
# 正常通知开关
self.enable_normal_notify = tk.BooleanVar(value=self.config.get("enable_normal_notify", False))
self.check_normal_notify = tk.Checkbutton(root, text="启用正常通知", variable=self.enable_normal_notify)
self.check_normal_notify.pack(pady=5)
# 正常通知时间间隔设置
self.label_normal_notify_interval = tk.Label(root, text="正常通知时间间隔(分钟):")
self.label_normal_notify_interval.pack(pady=5)
self.entry_normal_notify_interval = tk.Entry(root)
self.entry_normal_notify_interval.pack(pady=5)
self.entry_normal_notify_interval.insert(0, str(self.config.get("normal_notify_interval", 5)))
# 按钮区域
self.button_frame = tk.Frame(root)
self.button_frame.pack(pady=10)
self.button_start = tk.Button(self.button_frame, text="开始监控", command=self.start_monitor)
self.button_start.pack(side=tk.LEFT, padx=10)
self.button_stop = tk.Button(self.button_frame, text="停止监控", command=self.stop_monitor)
self.button_stop.pack(side=tk.LEFT, padx=10)
self.button_stop.config(state=tk.DISABLED) # 初始禁用停止按钮
# 日志显示区域
self.label_log = tk.Label(root, text="日志:")
self.label_log.pack(pady=5)
self.text_log = tk.Text(root, height=10, width=60)
self.text_log.pack(pady=5)
# 绑定窗口关闭事件
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
def set_webhook(self):
"""设置飞书Webhook地址"""
webhook_url = simpledialog.askstring("设置飞书Webhook地址", "请输入飞书Webhook地址:")
if webhook_url:
self.config["webhook_url"] = webhook_url
self.save_config()
def load_config(self):
"""加载配置文件"""
try:
if os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, "r", encoding="utf-8") as f:
return json.load(f)
except Exception as e:
logging.error(f"加载配置文件失败: {e}")
return {}
def save_config(self):
"""保存配置文件"""
try:
# 获取当前目标列表
target_info_list = []
for line in self.text_target.get("1.0", tk.END).strip().splitlines():
if "|" in line:
target, comment = map(str.strip, line.split("|", 1))
target_info_list.append((target, comment))
else:
target_info_list.append((line.strip(), "无备注"))
self.config["target_info_list"] = target_info_list
# 保存其他设置
self.config["ping_count"] = int(self.entry_ping_count.get())
self.config["ping_size"] = int(self.entry_ping_size.get())
self.config["monitor_interval"] = int(self.entry_monitor_interval.get())
self.config["enable_normal_notify"] = self.enable_normal_notify.get()
self.config["normal_notify_interval"] = int(self.entry_normal_notify_interval.get())
with open(CONFIG_FILE, "w", encoding="utf-8") as f:
json.dump(self.config, f, ensure_ascii=False, indent=4)
logging.info("配置文件已保存")
except Exception as e:
logging.error(f"保存配置文件失败: {e}")
def start_monitor(self):
"""启动监控"""
webhook_url = self.config.get("webhook_url", "").strip()
target_info_list = []
for line in self.text_target.get("1.0", tk.END).strip().splitlines():
if "|" in line:
target, comment = map(str.strip, line.split("|", 1))
target_info_list.append((target, comment))
else:
target_info_list.append((line.strip(), "无备注"))
if not webhook_url or not target_info_list:
messagebox.showerror("错误", "请设置飞书Webhook地址和填写需要监控的目标!")
return
try:
ping_count = int(self.entry_ping_count.get())
ping_size = int(self.entry_ping_size.get())
monitor_interval = int(self.entry_monitor_interval.get())
normal_notify_interval = int(self.entry_normal_notify_interval.get())
except ValueError:
messagebox.showerror("错误", "Ping包次数、Ping包大小和时间间隔必须为整数!")
return
self.monitor.start(
target_info_list,
webhook_url,
monitor_interval,
self.enable_normal_notify.get(),
normal_notify_interval,
ping_count,
ping_size
)
self.button_start.config(state=tk.DISABLED)
self.button_stop.config(state=tk.NORMAL)
self.log("监控已启动")
# 保存配置
self.save_config()
def stop_monitor(self):
"""停止监控"""
self.monitor.stop()
self.button_start.config(state=tk.NORMAL)
self.button_stop.config(state=tk.DISABLED)
self.log("监控已停止")
def log(self, message):
"""在日志区域显示消息"""
self.text_log.insert(tk.END, message + "\n")
self.text_log.see(tk.END)
def on_closing(self):
"""窗口关闭时确保监控停止"""
if self.monitor.running:
self.monitor.stop()
self.root.destroy()
# 启动GUI
if __name__ == "__main__":
root = tk.Tk()
app = IPMonitorGUI(root)
root.mainloop()
直接打包即可使用。打包命令
pyinstaller --onefile --windowed 12.py