python 实现服务器监控,cpu,内存,磁盘空间,网络等

Posted on 2026-03-25 17:53  打杂滴  阅读(12)  评论(0)    收藏  举报

 

import os
import shutil
import mimetypes
from pathlib import Path
from datetime import datetime
import hashlib

class FileOrganizer:
        def __init__(self, source_dir, target_dir):
            self.source_dir = Path(source_dir)
            self.target_dir = Path(target_dir)
            self.file_types = {
                'images': ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.svg', '.webp'],
                'documents': ['.pdf', '.doc', '.docx', '.txt', '.rtf', '.odt', '.md', '.tex'],
                'videos': ['.mp4', '.avi', '.mkv', '.mov', '.wmv', '.flv', '.webm'],
                'audio': ['.mp3', '.wav', '.flac', '.aac', '.ogg', '.wma'],
                'archives': ['.zip', '.rar', '.7z', '.tar', '.gz', '.bz2'],
                'executables': ['.exe', '.msi', '.bat', '.sh', '.apk'],
                'code': ['.py', '.js', '.html', '.css', '.java', '.cpp', '.c', '.php', '.rb', '.go'],
                'spreadsheets': ['.xlsx', '.xls', '.csv', '.ods'],
                'presentations': ['.pptx', '.ppt', '.odp']
            }
   
        def get_file_type(self, file_path):
            """根据文件扩展名确定文件类型"""
            ext = file_path.suffix.lower()
            for category, extensions in self.file_types.items():
                if ext in extensions:
                    return category
            return 'others'
   
        def organize_by_extension(self):
            """按文件扩展名分类整理"""
            organized_count = 0
            for file_path in self.source_dir.rglob('*'):
                if file_path.is_file():
                    file_type = self.get_file_type(file_path)
                    target_folder = self.target_dir / file_type
                    target_folder.mkdir(exist_ok=True)
               
                    target_path = target_folder / file_path.name
                    # 处理重名文件
                    if target_path.exists():
                        name_without_ext = file_path.stem
                        ext = file_path.suffix
                        counter = 1
                        while target_path.exists():
                            new_name = f"{name_without_ext}_{counter}{ext}"
                            target_path = target_folder / new_name
                            counter += 1
               
                    shutil.move(str(file_path), str(target_path))
                    organized_count += 1
                    print(f"已移动: {file_path.name} -> {file_type}/")
       
            print(f"按扩展名分类完成,共整理 {organized_count} 个文件")
   
        def organize_by_date(self):
            """按修改日期分类整理"""
            organized_count = 0
            for file_path in self.source_dir.rglob('*'):
                if file_path.is_file():
                    # 获取文件修改时间
                    mtime = file_path.stat().st_mtime
                    date_folder = datetime.fromtimestamp(mtime).strftime('%Y-%m')
               
                    target_folder = self.target_dir / date_folder
                    target_folder.mkdir(exist_ok=True)
               
                    target_path = target_folder / file_path.name
                    # 处理重名文件
                    if target_path.exists():
                        name_without_ext = file_path.stem
                        ext = file_path.suffix
                        counter = 1
                        while target_path.exists():
                            new_name = f"{name_without_ext}_{counter}{ext}"
                            target_path = target_folder / new_name
                            counter += 1
               
                    shutil.move(str(file_path), str(target_path))
                    organized_count += 1
                    print(f"已移动: {file_path.name} -> {date_folder}/")
       
            print(f"按日期分类完成,共整理 {organized_count} 个文件")
   
        def organize_by_size(self):
            """按文件大小分类整理"""
            organized_count = 0
            size_categories = {
                'small': (0, 1024*1024),      # < 1MB
                'medium': (1024*1024, 10*1024*1024),  # 1MB - 10MB
                'large': (10*1024*1024, 100*1024*1024), # 10MB - 100MB
                'huge': (100*1024*1024, float('inf'))   # > 100MB
            }
       
            for file_path in self.source_dir.rglob('*'):
                if file_path.is_file():
                    file_size = file_path.stat().st_size
               
                    # 确定文件大小类别
                    size_category = 'others'
                    for category, (min_size, max_size) in size_categories.items():
                        if min_size <= file_size < max_size:
                            size_category = category
                            break
               
                    target_folder = self.target_dir / size_category
                    target_folder.mkdir(exist_ok=True)
               
                    target_path = target_folder / file_path.name
                    # 处理重名文件
                    if target_path.exists():
                        name_without_ext = file_path.stem
                        ext = file_path.suffix
                        counter = 1
                        while target_path.exists():
                            new_name = f"{name_without_ext}_{counter}{ext}"
                            target_path = target_folder / new_name
                            counter += 1
               
                    shutil.move(str(file_path), str(target_path))
                    organized_count += 1
                    print(f"已移动: {file_path.name} -> {size_category}/")
       
            print(f"按大小分类完成,共整理 {organized_count} 个文件")
   
        def remove_duplicates(self):
            """查找并删除重复文件"""
            file_hashes = {}
            duplicates = []
       
            for file_path in self.source_dir.rglob('*'):
                if file_path.is_file():
                    # 计算文件哈希值
                    hash_value = self.calculate_file_hash(file_path)
               
                    if hash_value in file_hashes:
                        duplicates.append(file_path)
                        print(f"发现重复文件: {file_path} (与 {file_hashes[hash_value]} 相同)")
                    else:
                        file_hashes[hash_value] = file_path
       
            # 删除重复文件
            for duplicate in duplicates:
                duplicate.unlink()
                print(f"已删除重复文件: {duplicate}")
       
            print(f"共删除 {len(duplicates)} 个重复文件")
   
        def calculate_file_hash(self, file_path):
            """计算文件的MD5哈希值"""
            hash_md5 = hashlib.md5()
            with open(file_path, "rb") as f:
                for chunk in iter(lambda: f.read(4096), b""):
                    hash_md5.update(chunk)
            return hash_md5.hexdigest()
   
        def create_folder_structure(self):
            """创建目标文件夹结构"""
            for folder_name in self.file_types.keys():
                folder_path = self.target_dir / folder_name
                folder_path.mkdir(exist_ok=True)
            (self.target_dir / 'others').mkdir(exist_ok=True)

def main():
        print("=== 文件整理分类工具 ===")
        source_dir = input("请输入源文件夹路径: ").strip()
        target_dir = input("请输入目标文件夹路径: ").strip()
   
        if not os.path.exists(source_dir):
            print("源文件夹不存在!")
            return
   
        # 创建目标文件夹
        Path(target_dir).mkdir(exist_ok=True)
   
        organizer = FileOrganizer(source_dir, target_dir)
   
        print("\n请选择整理方式:")
        print("1. 按文件类型整理")
        print("2. 按修改日期整理")
        print("3. 按文件大小整理")
        print("4. 删除重复文件")
   
        choice = input("请输入选择(1-4): ").strip()
   
        if choice == '1':
            organizer.create_folder_structure()
            organizer.organize_by_extension()
        elif choice == '2':
            organizer.organize_by_date()
        elif choice == '3':
            organizer.organize_by_size()
        elif choice == '4':
            organizer.remove_duplicates()
        else:
            print("无效选择!")

if __name__ == "__main__":
        main()

 

 

--------------------

 

PS F:\python> & C:/Users/Administrator/AppData/Local/Microsoft/WindowsApps/python3.13.exe f:/python/python_new/自动化/ServerMonitor.py
2026-03-25 17:52:02,637 - ERROR - 发送告警邮件时出错: Connection unexpectedly closed

============================================================
服务器状态检查报告 - 2026-03-25 17:52:02
============================================================

✓ 本地服务器 [正常]
cpu_usage: 8.5%
memory_usage: 83.7%
disk_usage: 17.7%

✗ 本地服务器 [异常]
host: 127.0.0.1
port: 80
⚠️ 服务不可达

✓ 百度 [正常]
host: www.baidu.com
port: 443

============================================================

 


import psutil
import smtplib
import schedule
import time
import logging
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from datetime import datetime
import json
import os

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('server_monitor.log'),
        logging.StreamHandler()
    ]
)

class ServerMonitorMailer:
    def __init__(self, config_file='monitor_config.json'):
        """初始化监控邮件系统"""
        self.config = self.load_config(config_file)
        self.smtp_config = self.config['smtp_settings']
        self.recipients = self.config['recipients']
        self.monitor_settings = self.config['monitor_settings']
       
    def load_config(self, config_file):
        """加载配置文件"""
        try:
            with open(config_file, 'r', encoding='utf-8') as f:
                return json.load(f)
        except FileNotFoundError:
            # 创建默认配置文件
            default_config = {
                "smtp_settings": {
                    "smtp_server": "smtp.gmail.com",
                    "smtp_port": 587,
                    "sender_email": "your_email@gmail.com",
                    "sender_password": "your_app_password"
                },
                "recipients": [
                    {
                        "name": "管理员",
                        "email": "admin@company.com",
                        "role": "admin"
                    }
                ],
                "monitor_settings": {
                    "cpu_threshold": 80,
                    "memory_threshold": 85,
                    "disk_threshold": 90,
                    "check_interval_minutes": 30
                },
                "report_schedule": {
                    "daily_report_time": "09:00",
                    "weekly_report_day": "monday",
                    "monthly_report_date": 1
                }
            }
            with open(config_file, 'w', encoding='utf-8') as f:
                json.dump(default_config, f, indent=4, ensure_ascii=False)
            logging.info(f"已创建默认配置文件: {config_file}")
            return default_config
   
    def get_system_info(self):
        """获取系统状态信息"""
        try:
            # CPU信息
            cpu_percent = psutil.cpu_percent(interval=1)
           
            # 内存信息
            memory = psutil.virtual_memory()
            memory_percent = memory.percent
           
            # 磁盘信息
            disk = psutil.disk_usage('/')
            disk_percent = (disk.used / disk.total) * 100
           
            # 网络信息
            net_io = psutil.net_io_counters()
           
            # 系统启动时间
            boot_time = datetime.fromtimestamp(psutil.boot_time())
           
            system_info = {
                'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
                'cpu_percent': cpu_percent,
                'memory_percent': memory_percent,
                'disk_percent': disk_percent,
                'boot_time': boot_time.strftime('%Y-%m-%d %H:%M:%S'),
                'bytes_sent': net_io.bytes_sent,
                'bytes_recv': net_io.bytes_recv
            }
           
            return system_info
        except Exception as e:
            logging.error(f"获取系统信息时出错: {e}")
            return None
   
    def create_html_report(self, system_info, report_type="status"):
        """创建HTML格式的报告"""
        if not system_info:
            return ""
           
        css_style = """
        <style>
            body { font-family: Arial, sans-serif; margin: 20px; }
            .container { max-width: 800px; margin: 0 auto; background: white; padding: 20px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
            .header { text-align: center; color: #333; border-bottom: 2px solid #007cba; padding-bottom: 15px; }
            .status-card { background: #f8f9fa; border-left: 4px solid #007cba; padding: 15px; margin: 15px 0; border-radius: 5px; }
            .warning { border-left-color: #ffc107; background: #fff3cd; }
            .danger { border-left-color: #dc3545; background: #f8d7da; }
            .metric { display: flex; justify-content: space-between; margin: 10px 0; }
            .metric-name { font-weight: bold; }
            .metric-value { color: #007cba; }
            .footer { text-align: center; margin-top: 20px; color: #666; font-size: 12px; }
        </style>
        """
       
        # 根据指标值确定状态级别
        cpu_level = "danger" if system_info['cpu_percent'] > self.monitor_settings['cpu_threshold'] else \
                   "warning" if system_info['cpu_percent'] > self.monitor_settings['cpu_threshold'] * 0.8 else ""
                   
        memory_level = "danger" if system_info['memory_percent'] > self.monitor_settings['memory_threshold'] else \
                      "warning" if system_info['memory_percent'] > self.monitor_settings['memory_threshold'] * 0.8 else ""
                     
        disk_level = "danger" if system_info['disk_percent'] > self.monitor_settings['disk_threshold'] else \
                    "warning" if system_info['disk_percent'] > self.monitor_settings['disk_threshold'] * 0.8 else ""
       
        html_content = f"""
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <title>服务器状态报告</title>
            {css_style}
        </head>
        <body>
            <div class="container">
                <div class="header">
                    <h1>服务器状态监控报告</h1>
                    <p>{system_info['timestamp']}</p>
                </div>
               
                <div class="status-card">
                    <h2>CPU使用情况</h2>
                    <div class="metric">
                        <span class="metric-name">CPU使用率:</span>
                        <span class="metric-value">{system_info['cpu_percent']:.1f}%</span>
                    </div>
                    {f'<p style="color:red;">⚠️ CPU使用率过高,请关注!</p>' if cpu_level == 'danger' else ''}
                    {f'<p style="color:orange;">⚠️ CPU使用率偏高</p>' if cpu_level == 'warning' else ''}
                </div>
               
                <div class="status-card">
                    <h2>内存使用情况</h2>
                    <div class="metric">
                        <span class="metric-name">内存使用率:</span>
                        <span class="metric-value">{system_info['memory_percent']:.1f}%</span>
                    </div>
                    {f'<p style="color:red;">⚠️ 内存使用率过高,请关注!</p>' if memory_level == 'danger' else ''}
                    {f'<p style="color:orange;">⚠️ 内存使用率偏高</p>' if memory_level == 'warning' else ''}
                </div>
               
                <div class="status-card">
                    <h2>存储使用情况</h2>
                    <div class="metric">
                        <span class="metric-name">磁盘使用率:</span>
                        <span class="metric-value">{system_info['disk_percent']:.1f}%</span>
                    </div>
                    {f'<p style="color:red;">⚠️ 磁盘空间不足,请及时清理!</p>' if disk_level == 'danger' else ''}
                    {f'<p style="color:orange;">⚠️ 磁盘使用率偏高</p>' if disk_level == 'warning' else ''}
                </div>
               
                <div class="status-card">
                    <h2>网络流量统计</h2>
                    <div class="metric">
                        <span class="metric-name">发送字节数:</span>
                        <span class="metric-value">{system_info['bytes_sent']:,} bytes</span>
                    </div>
                    <div class="metric">
                        <span class="metric-name">接收字节数:</span>
                        <span class="metric-value">{system_info['bytes_recv']:,} bytes</span>
                    </div>
                </div>
               
                <div class="status-card">
                    <h2>系统信息</h2>
                    <div class="metric">
                        <span class="metric-name">系统启动时间:</span>
                        <span class="metric-value">{system_info['boot_time']}</span>
                    </div>
                </div>
               
                <div class="footer">
                    <p>此邮件由服务器自动发送,请勿回复</p>
                    <p>如有疑问请联系系统管理员</p>
                </div>
            </div>
        </body>
        </html>
        """
       
        return html_content
   
    def send_email(self, subject, content, recipients=None, is_html=False):
        """发送邮件"""
        try:
            if recipients is None:
                recipients = self.recipients
               
            # 创建邮件对象
            msg = MIMEMultipart()
            msg['From'] = self.smtp_config['sender_email']
            msg['To'] = ', '.join([r['email'] for r in recipients])
            msg['Subject'] = subject
           
            # 添加邮件正文
            if is_html:
                msg.attach(MIMEText(content, 'html', 'utf-8'))
            else:
                msg.attach(MIMEText(content, 'plain', 'utf-8'))
           
            # 连接SMTP服务器并发送邮件
            server = smtplib.SMTP(self.smtp_config['smtp_server'], self.smtp_config['smtp_port'])
            server.starttls()
            server.login(self.smtp_config['sender_email'], self.smtp_config['sender_password'])
           
            text = msg.as_string()
            server.sendmail(self.smtp_config['sender_email'],
                          [r['email'] for r in recipients], text)
            server.quit()
           
            logging.info(f"邮件发送成功: {subject}")
            return True
           
        except Exception as e:
            logging.error(f"发送邮件时出错: {e}")
            return False
   
    def send_status_alert(self, system_info):
        """发送状态告警邮件"""
        # 检查是否有超出阈值的情况
        alerts = []
        if system_info['cpu_percent'] > self.monitor_settings['cpu_threshold']:
            alerts.append(f"CPU使用率过高: {system_info['cpu_percent']:.1f}%")
        if system_info['memory_percent'] > self.monitor_settings['memory_threshold']:
            alerts.append(f"内存使用率过高: {system_info['memory_percent']:.1f}%")
        if system_info['disk_percent'] > self.monitor_settings['disk_threshold']:
            alerts.append(f"磁盘使用率过高: {system_info['disk_percent']:.1f}%")
       
        if alerts:
            subject = f"【警告】服务器状态异常 - {system_info['timestamp']}"
            html_content = self.create_html_report(system_info)
            self.send_email(subject, html_content, is_html=True)
            return True
        return False
   
    def send_daily_report(self):
        """发送每日汇总报告"""
        system_info = self.get_system_info()
        if system_info:
            subject = f"【日报】服务器状态汇总 - {system_info['timestamp'][:10]}"
            html_content = self.create_html_report(system_info, "daily")
            self.send_email(subject, html_content, is_html=True)
            logging.info("每日报告已发送")
   
    def send_weekly_report(self):
        """发送每周汇总报告"""
        system_info = self.get_system_info()
        if system_info:
            subject = f"【周报】服务器状态汇总 - {system_info['timestamp'][:10]}"
            html_content = self.create_html_report(system_info, "weekly")
            self.send_email(subject, html_content, is_html=True)
            logging.info("每周报告已发送")
   
    def start_monitoring(self):
        """开始监控"""
        logging.info("开始服务器状态监控...")
       
        # 设置定时任务
        schedule.every(self.monitor_settings['check_interval_minutes']).minutes.do(
            self.check_and_alert
        )
       
        # 设置日常报告
        daily_time = self.config['report_schedule']['daily_report_time']
        schedule.every().day.at(daily_time).do(self.send_daily_report)
       
        # 设置周报
        weekly_day = self.config['report_schedule']['weekly_report_day']
        getattr(schedule.every(), weekly_day).at("09:00").do(self.send_weekly_report)
       
        # 立即执行一次检查
        self.check_and_alert()
       
        # 持续运行
        while True:
            schedule.run_pending()
            time.sleep(60)
   
    def check_and_alert(self):
        """检查系统状态并发送告警"""
        system_info = self.get_system_info()
        if system_info:
            logging.info(f"系统检查完成 - CPU: {system_info['cpu_percent']:.1f}%, "
                        f"内存: {system_info['memory_percent']:.1f}%, "
                        f"磁盘: {system_info['disk_percent']:.1f}%")
           
            # 发送告警邮件(如果有异常)
            self.send_status_alert(system_info)

def main():
    """主函数"""
    try:
        monitor = ServerMonitorMailer()
       
        # 根据命令行参数决定运行模式
        import sys
        if len(sys.argv) > 1:
            if sys.argv[1] == '--once':
                # 单次检查模式
                system_info = monitor.get_system_info()
                if system_info:
                    html_content = monitor.create_html_report(system_info)
                    print("系统状态检查完成,报告已生成")
            elif sys.argv[1] == '--test-email':
                # 测试邮件发送
                system_info = monitor.get_system_info()
                if system_info:
                    html_content = monitor.create_html_report(system_info)
                    success = monitor.send_email(
                        "服务器监控系统测试邮件",
                        html_content,
                        is_html=True
                    )
                    if success:
                        print("测试邮件发送成功")
                    else:
                        print("测试邮件发送失败")
            else:
                print("未知参数。可用参数: --once (单次检查), --test-email (测试邮件)")
        else:
            # 持续监控模式
            monitor.start_monitoring()
           
    except KeyboardInterrupt:
        logging.info("监控程序已停止")
    except Exception as e:
        logging.error(f"程序运行出错: {e}")

if __name__ == "__main__":
    main()

博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3