暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

分享几个实用的Python运维封神脚本:告别Shell,新手即用(附完整代码)

热衷于分享各种干货知识,大家有想看或者想学的可以评论区留言,秉承着“开源知识来源于互联网,回归于互联网”的理念,分享一些日常工作中能用到或者比较重要的内容,希望大家能够喜欢,不足之处请大家多提宝贵地意见,我们一起提升,守住自己的饭碗。

关注公众号,技术道路不迷路

 


脚本1:实时系统监控仪表板

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import psutil
import time
from datetime import datetime
import os

def system_dashboard(interval=5, duration=60):
    """实时系统监控仪表板,动态刷新显示"""
    print("🖥️  Python系统监控仪表板 (Ctrl+C退出)")
    print("=" * 50)
    
    end_time = time.time() + duration
    try:
        while time.time() < end_time:
            os.system('clear' if os.name == 'posix' else 'cls')
            
            # CPU信息
            cpu_percent = psutil.cpu_percent(interval=0.5, percpu=True)
            cpu_avg = sum(cpu_percent)  len(cpu_percent)
            
            # 内存信息
            mem = psutil.virtual_memory()
            swap = psutil.swap_memory()
            
            # 磁盘信息
            disk = psutil.disk_usage('/')
            disk_io = psutil.disk_io_counters()
            
            # 网络信息
            net_io = psutil.net_io_counters()
            
            # 输出仪表板
            print(f"⏰ 时间: {datetime.now().strftime('%H:%M:%S')}")
            print("-" * 50)
            
            # CPU部分
            print(f"🔄 CPU使用率: {cpu_avg:.1f}%")
            print("  核心使用率: ", end="")
            for i, perc in enumerate(cpu_percent):
                bar = "█" * int(perc/5)
                print(f"C{i+1}:{perc:5.1f}% [{bar:<20}]", end="\n     " if (i+1)%4==0 else "  ")
            print()
            
            # 内存部分
            print(f"💾 内存: {mem.percent}% 已用")
            print(f"  总量: {mem.total/1024/1024/1024:.1f}GB | "
                  f"已用: {mem.used/1024/1024/1024:.1f}GB | "
                  f"可用: {mem.available/1024/1024/1024:.1f}GB")
            
            # 交换分区
            if swap.total > 0:
                print(f"🔄 交换分区: {swap.percent}%")
            
            # 磁盘部分
            print(f"💿 磁盘(/): {disk.percent}% 已用")
            print(f"  IO读: {disk_io.read_bytes/1024/1024:.1f}MB | "
                  f"IO写: {disk_io.write_bytes/1024/1024:.1f}MB")
            
            # 网络部分
            print(f"🌐 网络: 收 {net_io.bytes_recv/1024/1024:.1f}MB | "
                  f"发 {net_io.bytes_sent/1024/1024:.1f}MB")
            
            print("=" * 50)
            print("🔄 刷新中... (5秒间隔)")
            time.sleep(interval)
            
    except KeyboardInterrupt:
        print("\n\n👋 监控已停止")

if __name__ == "__main__":
    # 监控5秒刷新一次,持续10分钟
    system_dashboard(interval=5, duration=600)

在这里插入图片描述

脚本2:智能阈值告警系统

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import psutil
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import json
from datetime import datetime

class SmartAlertSystem:
    def __init__(self, config_file="alert_config.json"):
        self.config = self.load_config(config_file)
        self.alert_history = []
        
    def load_config(self, config_file):
        """加载告警配置"""
        default_config = {
            "thresholds": {
                "cpu_warning": 80,
                "cpu_critical": 95,
                "memory_warning": 85,
                "memory_critical": 95,
                "disk_warning": 80,
                "disk_critical": 90,
                "load_warning": 5,
                "load_critical": 10
            },
            "email": {
                "enabled": False,
                "smtp_server": "smtp.example.com",
                "smtp_port": 587,
                "sender": "alerts@example.com",
                "password": "your_password",
                "receivers": ["admin@example.com"]
            },
            "check_interval": 60  # 秒
        }
        
        try:
            with open(config_file, 'r') as f:
                user_config = json.load(f)
                # 合并配置
                for key in default_config:
                    if key in user_config:
                        if isinstance(default_config[key], dict):
                            default_config[key].update(user_config[key])
                        else:
                            default_config[key] = user_config[key]
        except FileNotFoundError:
            print(f"⚠️ 配置文件 {config_file} 不存在,使用默认配置")
            
        return default_config
    
    def check_resources(self):
        """检查所有资源"""
        alerts = []
        
        # CPU检查
        cpu_percent = psutil.cpu_percent(interval=1)
        if cpu_percent > self.config["thresholds"]["cpu_critical"]:
            alerts.append(("CRITICAL", f"CPU使用率过高: {cpu_percent}%"))
        elif cpu_percent > self.config["thresholds"]["cpu_warning"]:
            alerts.append(("WARNING", f"CPU使用率警告: {cpu_percent}%"))
        
        # 内存检查
        mem = psutil.virtual_memory()
        if mem.percent > self.config["thresholds"]["memory_critical"]:
            alerts.append(("CRITICAL", f"内存使用率过高: {mem.percent}%"))
        elif mem.percent > self.config["thresholds"]["memory_warning"]:
            alerts.append(("WARNING", f"内存使用率警告: {mem.percent}%"))
        
        # 磁盘检查
        for partition in psutil.disk_partitions():
            try:
                usage = psutil.disk_usage(partition.mountpoint)
                if usage.percent > self.config["thresholds"]["disk_critical"]:
                    alerts.append(("CRITICAL", 
                        f"磁盘 {partition.mountpoint} 使用率过高: {usage.percent}%"))
                elif usage.percent > self.config["thresholds"]["disk_warning"]:
                    alerts.append(("WARNING", 
                        f"磁盘 {partition.mountpoint} 使用率警告: {usage.percent}%"))
            except PermissionError:
                continue
        
        # 系统负载(仅Linux)
        if hasattr(psutil, "getloadavg"):
            load1, load5, load15 = psutil.getloadavg()
            cpu_count = psutil.cpu_count()
            if load5 cpu_count > self.config["thresholds"]["load_critical"]:
                alerts.append(("CRITICAL", f"系统负载过高: {load5:.2f}"))
            elif load5 cpu_count > self.config["thresholds"]["load_warning"]:
                alerts.append(("WARNING", f"系统负载警告: {load5:.2f}"))
        
        return alerts
    
    def send_email_alert(self, alerts):
        """发送邮件告警"""
        if not self.config["email"]["enabled"]:
            return
        
        msg = MIMEMultipart()
        msg['Subject'] = f"🚨 服务器告警 - {datetime.now().strftime('%Y-%m-%d %H:%M')}"
        msg['From'] = self.config["email"]["sender"]
        msg['To'] = ', '.join(self.config["email"]["receivers"])
        
        # 构建邮件内容
        content = "服务器资源告警报告\n\n"
        for level, message in alerts:
            content += f"{level}: {message}\n"
        
        content += f"\n检查时间: {datetime.now()}\n"
        content += f"主机名: {psutil.users()[0].name if psutil.users() else 'N/A'}\n"
        
        msg.attach(MIMEText(content, 'plain'))
        
        try:
            with smtplib.SMTP(self.config["email"]["smtp_server"], 
                            self.config["email"]["smtp_port"]) as server:
                server.starttls()
                server.login(self.config["email"]["sender"], 
                           self.config["email"]["password"])
                server.send_message(msg)
                print("📧 告警邮件已发送")
        except Exception as e:
            print(f"❌ 发送邮件失败: {e}")
    
    def run(self):
        """运行监控"""
        print("🔔 智能告警系统启动")
        print(f"检查间隔: {self.config['check_interval']}秒")
        print("=" * 50)
        
        import time
        try:
            while True:
                alerts = self.check_resources()
                if alerts:
                    print(f"\n⏰ {datetime.now().strftime('%H:%M:%S')} - 发现告警:")
                    for level, message in alerts:
                        icon = "🔴" if level == "CRITICAL" else "🟡"
                        print(f"  {icon} {level}: {message}")
                    
                    # 记录历史
                    self.alert_history.append({
                        "time": datetime.now(),
                        "alerts": alerts
                    })
                    
                    # 发送邮件
                    if self.config["email"]["enabled"]:
                        self.send_email_alert(alerts)
                else:
                    print(f"✅ {datetime.now().strftime('%H:%M:%S')} - 一切正常")
                
                time.sleep(self.config["check_interval"])
                
        except KeyboardInterrupt:
            print("\n\n📊 告警历史统计:")
            critical_count = sum(1 for h in self.alert_history 
                               if any(a[0]=="CRITICAL" for a in h["alerts"]))
            warning_count = sum(1 for h in self.alert_history 
                              if any(a[0]=="WARNING" for a in h["alerts"]))
            print(f"CRITICAL告警: {critical_count}次")
            print(f"WARNING告警: {warning_count}次")

if __name__ == "__main__":
    # 创建配置文件(首次运行)
    config = {
        "thresholds": {
            "cpu_warning": 70,
            "cpu_critical": 90,
            "memory_warning": 75,
            "memory_critical": 90,
            "disk_warning": 80,
            "disk_critical": 95
        },
        "email": {
            "enabled": False,  # 改为True并配置邮箱信息
            "smtp_server": "smtp.163.com",
            "smtp_port": 25,
            "sender": "your_email@163.com",
            "password": "your_password",
            "receivers": ["admin@example.com"]
        }
    }
    
    with open("alert_config.json", "w") as f:
        json.dump(config, f, indent=2)
    
    alert_system = SmartAlertSystem()
    alert_system.run()

在这里插入图片描述

脚本3:网络拓扑自动发现

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import socket
import sys
import os
import subprocess
import re
from datetime import datetime

try:
    import nmap  # pip install python-nmap
    NMAP_AVAILABLE = True
except ImportError:
    print("⚠️  警告: python-nmap 模块未安装")
    NMAP_AVAILABLE = False
    print("   请运行: pip install python-nmap")

try:
    import networkx as nx  # pip install networkx
    NETWORKX_AVAILABLE = True
except ImportError:
    print("⚠️  警告: networkx 模块未安装")
    NETWORKX_AVAILABLE = False

try:
    import matplotlib.pyplot as plt  # pip install matplotlib
    MATPLOTLIB_AVAILABLE = True
except ImportError:
    print("⚠️  警告: matplotlib 模块未安装")
    MATPLOTLIB_AVAILABLE = False

class NetworkDiscovery:
    def __init__(self, network_range="192.168.1.0/24"):
        self.network_range = network_range
        self.devices = []
        
        # 检查 nmap 是否可用
        self.nmap_available = self._check_nmap_installed()
        
        if self.nmap_available and NMAP_AVAILABLE:
            try:
                self.nm = nmap.PortScanner()
            except nmap.nmap.PortScannerError as e:
                print(f"❌ nmap 初始化失败: {e}")
                print("请确保已安装 nmap 命令行工具:")
                print("  - CentOS/RHEL: yum install nmap")
                print("  - Ubuntu/Debian: apt-get install nmap")
                self.nmap_available = False
        else:
            self.nmap_available = False
    
    def _check_nmap_installed(self):
        """检查系统是否安装了 nmap 命令行工具"""
        try:
            result = subprocess.run(
                ["which", "nmap"],
                capture_output=True,
                text=True
            )
            return result.returncode == 0
        except Exception:
            return False
    
    def _run_system_command(self, command):
        """运行系统命令"""
        try:
            result = subprocess.run(
                command,
                shell=True,
                capture_output=True,
                text=True,
                timeout=30
            )
            return result.returncode == 0, result.stdout, result.stderr
        except subprocess.TimeoutExpired:
            return False, "", "命令执行超时"
        except Exception as e:
            return False, "", str(e)
    
    def scan_network_simple(self):
        """使用简单的方法扫描网络(不依赖nmap)"""
        print(f"🔍 正在扫描网络 {self.network_range}...")
        
        # 使用 ping 扫描网络中的活跃主机
        base_ip = ".".join(self.network_range.split(".")[:3])
        
        devices_found = []
        
        # 扫描常见的 IP 地址范围
        for i in range(1, 255):
            ip = f"{base_ip}.{i}"
            print(f"  正在检查 {ip}...", end="\r")
            
            # 使用 ping 检查主机是否活跃
            success, stdout, stderr = self._run_system_command(f"ping -c 1 -W 1 {ip}")
            
            if success:
                # 尝试获取主机名
                hostname = "未知"
                try:
                    hostname_result = socket.gethostbyaddr(ip)
                    hostname = hostname_result[0]
                except:
                    pass
                
                # 尝试获取 MAC 地址(需要 arp 命令)
                mac = "未知"
                vendor = "未知"
                
                device_info = {
                    'ip': ip,
                    'hostname': hostname,
                    'mac': mac,
                    'vendor': vendor,
                    'os': '未知',
                    'ports': []
                }
                
                devices_found.append(device_info)
                print(f"✅ 发现设备: {ip} ({hostname})")
        
        print("\n" + "="*50)
        return devices_found
    
    def scan_network(self):
        """扫描网络设备"""
        if not self.nmap_available:
            print("⚠️  nmap 不可用,使用简单扫描模式...")
            self.devices = self.scan_network_simple()
            return self.devices
        
        print(f"🔍 正在扫描网络 {self.network_range}...")
        
        try:
            # 使用nmap进行主机发现
            self.nm.scan(hosts=self.network_range, arguments='-sn')
            
            for host in self.nm.all_hosts():
                if self.nm[host].state() == 'up':
                    device_info = {
                        'ip': host,
                        'hostname': self.nm[host].hostname() or '未知',
                        'mac': '未知',
                        'vendor': '未知',
                        'os': '未知',
                        'ports': []
                    }
                    
                    # 获取MAC地址和厂商
                    if 'addresses' in self.nm[host]:
                        if 'mac' in self.nm[host]['addresses']:
                            device_info['mac'] = self.nm[host]['addresses']['mac']
                    
                    # 获取厂商信息
                    if 'vendor' in self.nm[host] and device_info['mac'] in self.nm[host]['vendor']:
                        device_info['vendor'] = self.nm[host]['vendor'][device_info['mac']]
                    
                    self.devices.append(device_info)
                    print(f"✅ 发现设备: {host} ({device_info['hostname']})")
            
            return self.devices
            
        except Exception as e:
            print(f"❌ nmap 扫描失败: {e}")
            print("切换到简单扫描模式...")
            self.devices = self.scan_network_simple()
            return self.devices
    
    def scan_ports(self, host, ports="22,80,443,3306,3389"):
        """扫描设备端口"""
        if not self.nmap_available:
            print(f"⚠️  无法扫描端口(nmap不可用)")
            return []
        
        print(f"🔍 扫描 {host} 的端口 {ports}...")
        
        try:
            self.nm.scan(hosts=host, arguments=f'-p {ports}')
            
            open_ports = []
            if host in self.nm.all_hosts():
                if 'tcp' in self.nm[host]:
                    for port in self.nm[host]['tcp']:
                        if self.nm[host]['tcp'][port]['state'] == 'open':
                            open_ports.append({
                                'port': port,
                                'service': self.nm[host]['tcp'][port]['name'],
                                'version': self.nm[host]['tcp'][port]['version']
                            })
            
            return open_ports
            
        except Exception as e:
            print(f"❌ 端口扫描失败: {e}")
            return []
    
    def trace_route(self, target):
        """路由追踪"""
        print(f"📍 追踪到 {target} 的路由...")
        
        # Windows使用tracert,Linux使用traceroute
        command = "tracert" if os.name == "nt" else "traceroute"
        
        try:
            result = subprocess.run(
                [command, "-h", "15", target],
                capture_output=True,
                text=True,
                timeout=30
            )
            
            hops = []
            for line in result.stdout.split('\n'):
                match = re.search(r'\s+(\d+)\s+([\d\.ms\* ]+)', line)
                if match:
                    hop_num = match.group(1)
                    hop_info = match.group(2).strip()
                    hops.append(f"{hop_num}. {hop_info}")
            
            return hops[:10] if hops else ["无路由信息"]
            
        except FileNotFoundError:
            # 尝试使用 tracepath(Linux 替代方案)
            if os.name != "nt":
                try:
                    result = subprocess.run(
                        ["tracepath", target],
                        capture_output=True,
                        text=True,
                        timeout=30
                    )
                    
                    hops = []
                    for line in result.stdout.split('\n'):
                        if line.strip():
                            hops.append(line.strip())
                    
                    return hops[:10] if hops else ["无路由信息"]
                except:
                    return [f"路由追踪工具未找到,请安装 traceroute 或 tracepath"]
            return [f"命令 {command} 未找到,请安装相应工具"]
        except subprocess.TimeoutExpired:
            return ["追踪超时"]
        except Exception as e:
            return [f"追踪过程中出错: {str(e)}"]
    
    def visualize_network(self):
        """可视化网络拓扑"""
        if not self.devices:
            print("❌ 请先扫描网络设备")
            return
        
        if not NETWORKX_AVAILABLE or not MATPLOTLIB_AVAILABLE:
            print("❌ 无法可视化,需要安装 networkx 和 matplotlib")
            print("   请运行: pip install networkx matplotlib")
            return
        
        try:
            G = nx.Graph()
            
            # 添加节点
            for device in self.devices:
                label = f"{device['ip']}\n{device['hostname']}"
                G.add_node(device['ip'], label=label, type='device')
            
            # 添加网关节点
            G.add_node('Gateway', label='Gateway\n(路由器)', type='gateway')
            
            # 添加连接(简化模型,所有设备连接到网关)
            for device in self.devices:
                G.add_edge(device['ip'], 'Gateway')
            
            # 绘制图形
            plt.figure(figsize=(12, 8))
            
            pos = nx.spring_layout(G)
            
            # 绘制节点
            node_colors = ['lightblue' if G.nodes[node]['type'] == 'device' 
                          else 'lightgreen' for node in G.nodes()]
            nx.draw_networkx_nodes(G, pos, node_color=node_colors, node_size=3000)
            
            # 绘制标签
            labels = nx.get_node_attributes(G, 'label')
            nx.draw_networkx_labels(G, pos, labels, font_size=8)
            
            # 绘制边
            nx.draw_networkx_edges(G, pos, alpha=0.5)
            
            plt.title("网络拓扑图")
            plt.axis('off')
            plt.tight_layout()
            plt.show()
            
        except Exception as e:
            print(f"❌ 可视化失败: {e}")
    
    def generate_report(self, filename="network_report.html"):
        """生成HTML报告"""
        if not self.devices:
            print("❌ 没有发现设备,无法生成报告")
            return None
        
        html_content = f"""
        <!DOCTYPE html>
        <html>
        <head>
            <title>网络扫描报告</title>
            <meta charset="utf-8">
            <style>
                body {{ font-family: Arial, sans-serif; margin: 20px; background-color: #f5f5f5; }}
                .container {{ max-width: 1200px; margin: 0 auto; background: white; padding: 20px; border-radius: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }}
                .device {{ border: 1px solid #ddd; padding: 15px; margin: 10px 0; border-radius: 5px; }}
                .online {{ background-color: #d4edda; }}
                .port {{ display: inline-block; padding: 3px 8px; margin: 2px; 
                       background-color: #f8d7da; border-radius: 3px; font-size: 12px; }}
                h1, h2 {{ color: #333; }}
                .stats {{ background-color: #e9ecef; padding: 10px; border-radius: 5px; margin-bottom: 20px; }}
                .header {{ display: flex; justify-content: space-between; align-items: center; }}
                .timestamp {{ color: #666; font-size: 14px; }}
                .warning {{ background-color: #fff3cd; border: 1px solid #ffeaa7; padding: 10px; border-radius: 5px; margin: 10px 0; }}
            </style>
        </head>
        <body>
            <div class="container">
                <div class="header">
                    <h1>📡 网络扫描报告</h1>
                    <div class="timestamp">扫描时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</div>
                </div>
        """

        
        if not self.nmap_available:
            html_content += """
                <div class="warning">
                    <strong>⚠️ 注意:</strong> nmap 未安装,报告基于简单扫描。建议安装 nmap 以获得更详细的信息。<br>
                    <strong>安装命令:</strong> yum install nmap (CentOS/RHEL) 或 apt-get install nmap (Ubuntu/Debian)
                </div>
            """

        
        html_content += f"""
                <div class="stats">
                    <p><strong>扫描范围:</strong> {self.network_range}</p>
                    <p><strong>发现设备:</strong> {len(self.devices)} 台</p>
                    <p><strong>扫描模式:</strong> {'完整扫描 (nmap)' if self.nmap_available else '简单扫描 (ping)'}</p>
                </div>
                <h2>设备列表</h2>
        """

        
        for device in self.devices:
            # 扫描端口(如果nmap可用)
            open_ports = []
            if self.nmap_available:
                open_ports = self.scan_ports(device['ip'])
            
            html_content += f"""
            <div class="device online">
                <h3>🖥️ {device['hostname']} ({device['ip']})</h3>
                <p><strong>MAC地址:</strong> {device['mac']}</p>
                <p><strong>厂商:</strong> {device['vendor']}</p>
        """

            
            if self.nmap_available and open_ports:
                html_content += f"""
                <p><strong>开放端口 ({len(open_ports)}):</strong></p>
                """

                for port_info in open_ports:
                    html_content += f"""
                    <span class="port">🔒 {port_info['port']}/{port_info['service']} ({port_info['version']})</span>
                    """

            elif self.nmap_available:
                html_content += "<p><strong>开放端口:</strong> 无</p>"
            else:
                html_content += "<p><strong>端口信息:</strong> nmap未安装,无法扫描端口</p>"
            
            html_content += "</div>"
        
        # 路由追踪
        trace_result = self.trace_route("8.8.8.8")
        html_content += f"""
                <h2>路由追踪示例 (到 8.8.8.8)</h2>
                <div style="background-color: #f8f9fa; padding: 15px; border-radius: 5px; font-family: monospace; font-size: 12px;">
        """

        
        for hop in trace_result:
            html_content += f"{hop}<br>"
        
        html_content += """
                </div>
            </div>
        </body>
        </html>
        """

        
        try:
            with open(filename, 'w', encoding='utf-8') as f:
                f.write(html_content)
            
            print(f"📄 报告已生成: {filename}")
            print(f"   文件路径: {os.path.abspath(filename)}")
            return filename
        except Exception as e:
            print(f"❌ 生成报告失败: {e}")
            return None


def main():
    """主函数"""
    print("=" * 50)
    print("        网络发现工具 v1.0")
    print("=" * 50)
    
    # 显示系统信息
    print(f"操作系统: {os.name}")
    print(f"Python版本: {sys.version.split()[0]}")
    
    # 检查依赖
    if not NMAP_AVAILABLE:
        print("⚠️  缺少 python-nmap 模块,功能受限")
        print("   安装命令: pip install python-nmap")
    
    # 获取用户输入的网络范围
    default_range = "192.168.1.0/24"
    
    # 获取本机IP和网络信息
    try:
        hostname = socket.gethostname()
        local_ip = socket.gethostbyname(hostname)
        print(f"本机主机名: {hostname}")
        print(f"本机IP地址: {local_ip}")
        
        # 根据本机IP猜测网络范围
        ip_parts = local_ip.split('.')
        guessed_range = f"{ip_parts[0]}.{ip_parts[1]}.{ip_parts[2]}.0/24"
        default_range = guessed_range
        print(f"推测网络范围: {guessed_range}")
    except:
        pass
    
    user_input = input(f"\n请输入要扫描的网络范围 (默认: {default_range}): ").strip()
    network_range = user_input if user_input else default_range
    
    # 创建扫描器
    scanner = NetworkDiscovery(network_range)
    
    try:
        # 扫描网络
        print("\n" + "="*50)
        devices = scanner.scan_network()
        
        if not devices:
            print("❌ 未发现任何设备,请检查网络范围或网络连接")
            
            # 提供一些调试建议
            print("\n💡 调试建议:")
            print("  1. 检查网络连接")
            print("  2. 确认网络范围是否正确")
            print("  3. 尝试 ping 一个已知的主机")
            print("  4. 检查防火墙设置")
            
            test_ip = input("\n是否要测试网络连通性?(输入要ping的IP或按回车跳过): ").strip()
            if test_ip:
                success, stdout, stderr = scanner._run_system_command(f"ping -c 3 {test_ip}")
                if success:
                    print(f"✅ 可以连接到 {test_ip}")
                else:
                    print(f"❌ 无法连接到 {test_ip}")
            
            return
        
        print(f"\n✅ 发现 {len(devices)} 台设备")
        
        # 显示设备列表
        print("\n📋 设备列表:")
        for i, device in enumerate(devices, 1):
            print(f"  {i}. {device['ip']:15} - {device['hostname']}")
        
        # 生成报告
        print("\n" + "="*50)
        report_file = scanner.generate_report()
        
        if report_file:
            print(f"\n🎉 扫描完成!")
            print(f"   报告文件: {report_file}")
            
            # 询问是否可视化
            if NETWORKX_AVAILABLE and MATPLOTLIB_AVAILABLE:
                if input("\n是否显示网络拓扑图?(y/n): ").lower() == 'y':
                    try:
                        scanner.visualize_network()
                    except Exception as e:
                        print(f"❌ 显示拓扑图时出错: {e}")
            else:
                print("\nℹ️  要显示网络拓扑图,请安装:")
                print("   pip install networkx matplotlib")
                
    except KeyboardInterrupt:
        print("\n\n⚠️ 用户中断扫描")
    except Exception as e:
        print(f"❌ 扫描过程中出错: {e}")
        import traceback
        traceback.print_exc()


if __name__ == "__main__":
    main()

在这里插入图片描述

脚本4:SSL证书监控

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import ssl
import socket
from datetime import datetime
import pandas as pd
from cryptography import x509
from cryptography.hazmat.backends import default_backend
import idna
from urllib.parse import urlparse

class SSLCertMonitor:
    def __init__(self):
        self.results = []
        
    def get_cert_info(self, hostname, port=443):
        """获取SSL证书信息"""
        try:
            # 创建SSL上下文
            context = ssl.create_default_context()
            
            # 连接服务器获取证书
            with socket.create_connection((hostname, port), timeout=10) as sock:
                with context.wrap_socket(sock, server_hostname=hostname) as ssock:
                    cert_bin = ssock.getpeercert(True)
                    
            # 解析证书
            cert = x509.load_der_x509_certificate(cert_bin, default_backend())
            
            # 提取信息
            subject = cert.subject.rfc4514_string()
            issuer = cert.issuer.rfc4514_string()
            
            # 证书有效期
            not_before = cert.not_valid_before
            not_after = cert.not_valid_after
            
            # 计算剩余天数
            remaining_days = (not_after - datetime.now()).days
            
            # SANs(主题备用名称)
            sans = []
            try:
                ext = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName)
                sans = ext.value.get_values_for_type(x509.DNSName)
            except x509.ExtensionNotFound:
                sans = []
            
            # 证书指纹
            fingerprint = cert.fingerprint(ssl.PROTOCOL_TLS).hex()
            
            return {
                "hostname": hostname,
                "subject": subject,
                "issuer": issuer,
                "not_before": not_before.strftime("%Y-%m-%d"),
                "not_after": not_after.strftime("%Y-%m-%d"),
                "remaining_days": remaining_days,
                "sans": ", ".join(sans),
                "fingerprint_sha256": fingerprint,
                "status": "VALID" if remaining_days > 0 else "EXPIRED",
                "warning": self.get_warning_level(remaining_days)
            }
            
        except Exception as e:
            return {
                "hostname": hostname,
                "status": "ERROR",
                "error": str(e)
            }
    
    def get_warning_level(self, days):
        """根据剩余天数返回警告级别"""
        if days < 0:
            return "🔴 EXPIRED"
        elif days < 7:
            return "🔴 CRITICAL"
        elif days < 30:
            return "🟠 WARNING"
        else:
            return "🟢 OK"
    
    def check_multiple(self, urls_file="ssl_urls.txt"):
        """批量检查多个域名"""
        try:
            with open(urls_file, 'r') as f:
                urls = [line.strip() for line in f if line.strip()]
        except FileNotFoundError:
            # 创建示例文件
            sample_urls = [
                "https://www.baidu.com",
                "https://www.taobao.com",
                "https://www.jd.com",
                "https://github.com"
            ]
            with open(urls_file, 'w') as f:
                f.write("\n".join(sample_urls))
            urls = sample_urls
        
        print(f"🔍 开始检查 {len(urls)} 个域名的SSL证书...")
        print("=" * 80)
        
        for url in urls:
            try:
                # 解析URL获取主机名
                parsed = urlparse(url)
                hostname = parsed.netloc or url
                
                print(f"检查: {hostname}", end="", flush=True)
                result = self.get_cert_info(hostname)
                
                if result["status"] == "VALID":
                    print(f" ✅ 剩余 {result['remaining_days']}天 ({result['warning']})")
                else:
                    print(f" ❌ {result.get('error', '未知错误')}")
                
                self.results.append(result)
                
            except Exception as e:
                print(f" ❌ 检查失败: {str(e)}")
                self.results.append({
                    "hostname": url,
                    "status": "ERROR",
                    "error": str(e)
                })
        
        return self.results
    
    def generate_report(self, output_format="console"):
        """生成报告"""
        if not self.results:
            print("❌ 没有检查结果")
            return
        
        df = pd.DataFrame(self.results)
        
        if output_format == "excel":
            # 生成Excel报告
            filename = f"ssl_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
            
            # 创建Excel写入器
            with pd.ExcelWriter(filename, engine='openpyxl') as writer:
                # 所有证书
                df.to_excel(writer, sheet_name='All Certificates', index=False)
                
                # 即将过期的证书(30天内)
                expiring = df[df['remaining_days'] < 30] if 'remaining_days' in df.columns else pd.DataFrame()
                if not expiring.empty:
                    expiring.to_excel(writer, sheet_name='即将过期', index=False)
                
                # 有错误的证书
                errors = df[df['status'] == 'ERROR']
                if not errors.empty:
                    errors.to_excel(writer, sheet_name='检查失败', index=False)
                
                # 添加统计信息
                stats = {
                    '统计项': ['总域名数', '有效证书', '即将过期(<30天)', '已过期', '检查失败'],
                    '数量': [
                        len(df),
                        len(df[df['status'] == 'VALID']) if 'status' in df.columns else 0,
                        len(expiring),
                        len(df[(df['status'] == 'VALID') & (df['remaining_days'] < 0)]) if 'remaining_days' in df.columns else 0,
                        len(errors)
                    ]
                }
                pd.DataFrame(stats).to_excel(writer, sheet_name='统计', index=False)
            
            print(f"📊 Excel报告已生成: {filename}")
            
        elif output_format == "html":
            # 生成HTML报告
            html_content = """
            <!DOCTYPE html>
            <html>
            <head>
                <title>SSL证书监控报告</title>
                <style>
                    body { font-family: Arial, sans-serif; margin: 20px; }
                    table { border-collapse: collapse; width: 100%; margin: 20px 0; }
                    th, td { border: 1px solid #ddd; padding: 12px; text-align: left; }
                    th { background-color: #f2f2f2; }
                    .critical { background-color: #f8d7da; }
                    .warning { background-color: #fff3cd; }
                    .ok { background-color: #d4edda; }
                    .error { background-color: #f1f1f1; color: #721c24; }
                    h1 { color: #333; }
                </style>
            </head>
            <body>
                <h1>🔐 SSL证书监控报告</h1>
                <p>生成时间: """
 + datetime.now().strftime('%Y-%m-%d %H:%M:%S') + """</p>
                <table>
                    <tr>
                        <th>域名</th>
                        <th>状态</th>
                        <th>剩余天数</th>
                        <th>过期时间</th>
                        <th>颁发者</th>
                        <th>警告级别</th>
                    </tr>
            """

            
            for result in self.results:
                if result['status'] == 'VALID':
                    row_class = 'critical' if result['remaining_days'] < 7 else \
                               'warning' if result['remaining_days'] < 30 else 'ok'
                    
                    html_content += f"""
                    <tr class="{row_class}">
                        <td>{result['hostname']}</td>
                        <td>{result['status']}</td>
                        <td>{result['remaining_days']}</td>
                        <td>{result['not_after']}</td>
                        <td>{result['issuer'][:50]}...</td>
                        <td>{result['warning']}</td>
                    </tr>
                    """

                else:
                    html_content += f"""
                    <tr class="error">
                        <td>{result['hostname']}</td>
                        <td colspan="5">❌ {result.get('error', '检查失败')}</td>
                    </tr>
                    """

            
            html_content += """
                </table>
            </body>
            </html>
            """

            
            filename = f"ssl_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.html"
            with open(filename, 'w', encoding='utf-8') as f:
                f.write(html_content)
            
            print(f"📄 HTML报告已生成: {filename}")
            
        else:
            # 控制台输出
            print("\n" + "=" * 100)
            print("SSL证书检查报告")
            print("=" * 100)
            
            for result in self.results:
                if result['status'] == 'VALID':
                    warning_icon = result['warning'][0]  # 获取表情符号
                    print(f"{warning_icon} {result['hostname']:30} | "
                          f"剩余{result['remaining_days']:4}天 | "
                          f"过期: {result['not_after']} | "
                          f"颁发: {result['issuer'][:40]}...")
                else:
                    print(f"❌ {result['hostname']:30} | ERROR: {result.get('error', '未知')}")
    
    def monitor_schedule(self, interval_hours=24, urls_file="ssl_urls.txt"):
        """定时监控"""
        import schedule
        import time
        
        print(f"⏰ SSL证书定时监控启动,每{interval_hours}小时检查一次")
        print("按Ctrl+C停止")
        
        def check_job():
            print(f"\n🔄 {datetime.now().strftime('%Y-%m-%d %H:%M')} 开始检查...")
            self.results = []  # 清空之前的结果
            self.check_multiple(urls_file)
            self.generate_report("html")
        
        # 安排任务
        schedule.every(interval_hours).hours.do(check_job)
        
        # 立即执行一次
        check_job()
        
        try:
            while True:
                schedule.run_pending()
                time.sleep(60)  # 每分钟检查一次是否有任务
        except KeyboardInterrupt:
            print("\n👋 监控已停止")

if __name__ == "__main__":
    monitor = SSLCertMonitor()
    
    # 使用方式1: 批量检查
    monitor.check_multiple()
    monitor.generate_report("console")
    
    # 使用方式2: 生成详细报告
    # monitor.generate_report("excel")  # 需要openpyxl: pip install openpyxl
    
    # 使用方式3: 定时监控
    # monitor.monitor_schedule(interval_hours=12)

在这里插入图片描述

脚本5:智能日志分析器

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import re
import pandas as pd
from collections import Counter
from datetime import datetime
import gzip
import bz2
from pathlib import Path

class SmartLogAnalyzer:
    def __init__(self):
        self.patterns = {
            'nginx': {
                'error': r'(\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}) \[error\] (\d+)#\d+: .*?, client: ([\d\.]+), server: (\S+), request: "(\S+) (\S+) HTTP/\d\.\d", host: "(\S+)"',
                'access': r'([\d\.]+) - - \[(.*?)\] "(\S+) (\S+) HTTP/\d\.\d" (\d+) (\d+) "([^"]*)" "([^"]*)"'
            },
            'apache': {
                'access': r'([\d\.]+) - - \[(.*?)\] "(\S+) (\S+) HTTP/\d\.\d" (\d+) (\d+)',
                'error': r'\[(.*?)\] \[(\w+)\] \[client ([\d\.]+)\] (.*)'
            },
            'syslog': {
                'ssh': r'(\w+ \d+ \d{2}:\d{2}:\d{2}) (\S+) sshd\[\d+\]: (.*)',
                'auth': r'(\w+ \d+ \d{2}:\d{2}:\d{2}) (\S+) (\w+): (.*)'
            }
        }
        
    def detect_log_type(self, log_line):
        """自动检测日志类型"""
        log_line = log_line.strip()
        
        if not log_line:
            return None
        
        # 检查Nginx错误日志
        if re.search(r'\[error\]', log_line):
            return ('nginx', 'error')
        
        # 检查Nginx访问日志
        if re.match(r'[\d\.]+ - - \[', log_line):
            return ('nginx', 'access')
        
        # 检查Apache日志
        if re.match(r'[\d\.]+ - - \[', log_line) and '"' in log_line:
            return ('apache', 'access')
        
        # 检查系统日志
        if re.match(r'\w+ \d+ \d{2}:\d{2}:\d{2}', log_line):
            if 'sshd' in log_line:
                return ('syslog', 'ssh')
            elif 'auth' in log_line.lower():
                return ('syslog', 'auth')
        
        return ('unknown', 'unknown')
    
    def parse_log_line(self, log_line, log_type=None):
        """解析单行日志"""
        if not log_type:
            log_type = self.detect_log_type(log_line)
        
        app, subtype = log_type
        
        if app == 'nginx' and subtype == 'error':
            match = re.match(self.patterns['nginx']['error'], log_line)
            if match:
                return {
                    'timestamp': match.group(1),
                    'pid': match.group(2),
                    'client_ip': match.group(3),
                    'server': match.group(4),
                    'method': match.group(5),
                    'url': match.group(6),
                    'host': match.group(7),
                    'type': 'error',
                    'raw': log_line
                }
        
        elif app == 'nginx' and subtype == 'access':
            match = re.match(self.patterns['nginx']['access'], log_line)
            if match:
                return {
                    'client_ip': match.group(1),
                    'timestamp': match.group(2),
                    'method': match.group(3),
                    'url': match.group(4),
                    'status': int(match.group(5)),
                    'size': int(match.group(6)),
                    'referer': match.group(7),
                    'user_agent': match.group(8),
                    'type': 'access',
                    'raw': log_line
                }
        
        # 其他日志类型的解析...
        
        return {'raw': log_line, 'type': 'unparsed'}
    
    def read_log_file(self, filepath):
        """读取日志文件,支持压缩格式"""
        filepath = Path(filepath)
        
        if filepath.suffix == '.gz':
            with gzip.open(filepath, 'rt', encoding='utf-8', errors='ignore') as f:
                return f.readlines()
        elif filepath.suffix == '.bz2':
            with bz2.open(filepath, 'rt', encoding='utf-8', errors='ignore') as f:
                return f.readlines()
        else:
            with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
                return f.readlines()
    
    def analyze_logs(self, log_files, max_lines=10000):
        """分析多个日志文件"""
        all_logs = []
        
        for log_file in log_files:
            print(f"📁 分析文件: {log_file}")
            
            try:
                lines = self.read_log_file(log_file)
                lines = lines[-max_lines:]  # 只分析最近的行
                
                for line in lines:
                    parsed = self.parse_log_line(line)
                    if parsed:
                        parsed['source_file'] = str(log_file)
                        all_logs.append(parsed)
                        
            except Exception as e:
                print(f"❌ 读取文件失败 {log_file}: {e}")
        
        return pd.DataFrame(all_logs)
    
    def generate_statistics(self, df):
        """生成统计报告"""
        if df.empty:
            return "没有可分析的日志数据"
        
        stats = {}
        
        # 整体统计
        stats['总行数'] = len(df)
        stats['已解析行数'] = len(df[df['type'] != 'unparsed'])
        
        # 按类型统计
        if 'type' in df.columns:
            type_stats = df['type'].value_counts().to_dict()
            stats['日志类型分布'] = type_stats
        
        # 访问日志特定统计
        if 'nginx' in str(df.get('raw', '')):
            access_logs = df[df['type'] == 'access']
            if not access_logs.empty:
                # 状态码统计
                if 'status' in access_logs.columns:
                    status_stats = access_logs['status'].value_counts().head(10).to_dict()
                    stats['HTTP状态码统计'] = status_stats
                
                # 最频繁的URL
                if 'url' in access_logs.columns:
                    url_stats = access_logs['url'].value_counts().head(20).to_dict()
                    stats['最常访问URL'] = url_stats
                
                # 客户端IP统计
                if 'client_ip' in access_logs.columns:
                    ip_stats = access_logs['client_ip'].value_counts().head(10).to_dict()
                    stats['客户端IP统计'] = ip_stats
        
        # 错误日志统计
        error_logs = df[df['type'] == 'error']
        if not error_logs.empty:
            if 'client_ip' in error_logs.columns:
                error_ip_stats = error_logs['client_ip'].value_counts().head(10).to_dict()
                stats['错误最多的客户端IP'] = error_ip_stats
            
            if 'url' in error_logs.columns:
                error_url_stats = error_logs['url'].value_counts().head(10).to_dict()
                stats['错误最多的URL'] = error_url_stats
        
        return stats
    
    def detect_anomalies(self, df):
        """检测异常模式"""
        anomalies = []
        
        # 检测大量404错误(可能是扫描)
        if 'status' in df.columns and 'client_ip' in df.columns:
            error_404 = df[df['status'] == 404]
            if not error_404.empty:
                ip_404_counts = error_404['client_ip'].value_counts()
                suspicious_ips = ip_404_counts[ip_404_counts > 50]  # 一个IP大量404
                
                for ip, count in suspicious_ips.items():
                    anomalies.append({
                        'type': '扫描攻击',
                        '描述': f"IP {ip} 产生了 {count} 次404错误",
                        '风险等级': '中',
                        '建议': '检查是否为恶意扫描,考虑封禁IP'
                    })
        
        # 检测大量500错误(服务器错误)
        if 'status' in df.columns:
            error_500 = df[df['status'] == 500]
            if len(error_500) > 10:
                anomalies.append({
                    'type': '服务器错误',
                    '描述': f"发现 {len(error_500)} 次500服务器错误",
                    '风险等级': '高',
                    '建议': '立即检查应用服务器状态'
                })
        
        # 检测短时间内大量请求(DDoS迹象)
        if 'timestamp' in df.columns and 'client_ip' in df.columns:
            # 这里简化处理,实际应该按时间窗口分析
            ip_counts = df['client_ip'].value_counts()
            suspicious_ips = ip_counts[ip_counts > 1000]  # 单个IP大量请求
            
            for ip, count in suspicious_ips.items():
                anomalies.append({
                    'type': '洪水攻击',
                    '描述': f"IP {ip} 在短时间内有 {count} 次请求",
                    '风险等级': '高',
                    '建议': '分析请求模式,考虑限流或封禁'
                })
        
        return anomalies
    
    def export_report(self, df, stats, anomalies, output_format="html"):
        """导出分析报告"""
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        
        if output_format == "html":
            html_content = """
            <!DOCTYPE html>
            <html>
            <head>
                <title>日志分析报告</title>
                <style>
                    body { font-family: Arial, sans-serif; margin: 20px; }
                    .section { margin: 30px 0; }
                    .anomaly { border-left: 5px solid #dc3545; padding: 10px; margin: 10px 0; background: #f8d7da; }
                    .warning { border-left: 5px solid #ffc107; padding: 10px; margin: 10px 0; background: #fff3cd; }
                    table { border-collapse: collapse; width: 100%; margin: 10px 0; }
                    th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
                    th { background-color: #f2f2f2; }
                    h1, h2, h3 { color: #333; }
                </style>
            </head>
            <body>
                <h1>📊 智能日志分析报告</h1>
                <p>生成时间: """
 + datetime.now().strftime('%Y-%m-%d %H:%M:%S') + """</p>
                
                <div class="section">
                    <h2>📈 总体统计</h2>
                    <ul>
            """

            
            for key, value in stats.items():
                if isinstance(value, dict):
                    html_content += f"<li><strong>{key}:</strong><br>"
                    for sub_key, sub_value in value.items():
                        html_content += f"&nbsp;&nbsp;{sub_key}: {sub_value}<br>"
                    html_content += "</li>"
                else:
                    html_content += f"<li><strong>{key}:</strong> {value}</li>"
            
            html_content += """
                    </ul>
                </div>
                
                <div class="section">
                    <h2>🚨 异常检测</h2>
            """

            
            if anomalies:
                for anomaly in anomalies:
                    alert_class = "anomaly" if anomaly['风险等级'] == '高' else "warning"
                    html_content += f"""
                    <div class="{alert_class}">
                        <h3>🔍 {anomaly['类型']} ({anomaly['风险等级']}风险)</h3>
                        <p><strong>描述:</strong> {anomaly['描述']}</p>
                        <p><strong>建议:</strong> {anomaly['建议']}</p>
                    </div>
                    """

            else:
                html_content += "<p>✅ 未检测到明显异常</p>"
            
            html_content += """
                </div>
                
                <div class="section">
                    <h2>📋 原始数据样本</h2>
                    <table>
                        <tr>
                            <th>时间</th>
                            <th>客户端IP</th>
                            <th>URL</th>
                            <th>状态码</th>
                            <th>类型</th>
                        </tr>
            """

            
            # 显示前20条记录
            sample_data = df.head(20)
            for _, row in sample_data.iterrows():
                html_content += f"""
                <tr>
                    <td>{row.get('timestamp', 'N/A')}</td>
                    <td>{row.get('client_ip', 'N/A')}</td>
                    <td>{str(row.get('url', 'N/A'))[:50]}</td>
                    <td>{row.get('status', 'N/A')}</td>
                    <td>{row.get('type', 'N/A')}</td>
                </tr>
                """

            
            html_content += """
                    </table>
                </div>
            </body>
            </html>
            """

            
            filename = f"log_analysis_{timestamp}.html"
            with open(filename, 'w', encoding='utf-8') as f:
                f.write(html_content)
            
            print(f"📄 HTML报告已生成: {filename}")
            
        elif output_format == "console":
            print("\n" + "=" * 80)
            print("📊 日志分析报告")
            print("=" * 80)
            
            print("\n📈 总体统计:")
            for key, value in stats.items():
                print(f"  {key}: {value}")
            
            print("\n🚨 异常检测:")
            if anomalies:
                for anomaly in anomalies:
                    icon = "🔴" if anomaly['风险等级'] == '高' else "🟡"
                    print(f"  {icon} {anomaly['类型']}: {anomaly['描述']}")
            else:
                print("  ✅ 未检测到明显异常")
            
            print("\n📋 样本数据 (前10行):")
            print(df.head(10).to_string())

if __name__ == "__main__":
    analyzer = SmartLogAnalyzer()
    
    # 示例:分析Nginx日志
    log_files = [
        "/var/log/nginx/access.log",
        "/var/log/nginx/error.log"
    ]
    
    # 在实际使用时,需要修改为你的日志文件路径
    # 这里使用示例文件
    example_logs = [
        '192.168.1.100 - - [01/Jan/2024:10:30:00 +0800] "GET index.html HTTP/1.1" 200 1234 "-" "Mozilla/5.0"',
        '192.168.1.101 - - [01/Jan/2024:10:30:01 +0800] "GET test.php HTTP/1.1" 404 500 "-" "Mozilla/5.0"',
        '2024/01/01 10:30:02 [error] 1234#0: *5678 open() "/var/www/test" failed (2: No such file or directory), client: 192.168.1.101, server: example.com, request: "GET /test.php HTTP/1.1", host: "example.com"'
    ]
    
    # 创建示例DataFrame
    df = pd.DataFrame([analyzer.parse_log_line(line) for line in example_logs])
    
    # 生成统计
    stats = analyzer.generate_statistics(df)
    
    # 检测异常
    anomalies = analyzer.detect_anomalies(df)
    
    # 输出报告
    analyzer.export_report(df, stats, anomalies, "console")

在这里插入图片描述

脚本6:实时日志追踪器

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import time
import os
import sys
import re
from pathlib import Path
from collections import defaultdict
import threading

class RealTimeLogTracker:
    def __init__(self, highlight_patterns=None):
        self.highlight_patterns = highlight_patterns or {
            'ERROR': '\033[91m',      # 红色
            'WARNING': '\033[93m',    # 黄色
            'INFO': '\033[94m',       # 蓝色
            'SUCCESS': '\033[92m',    # 绿色
            'DEBUG': '\033[90m',      # 灰色
        }
        self.reset_color = '\033[0m'
        self.filters = []
        self.excludes = []
        
    def add_filter(self, pattern):
        """添加过滤条件(只显示匹配的行)"""
        self.filters.append(re.compile(pattern, re.IGNORECASE))
    
    def add_exclude(self, pattern):
        """添加排除条件(不显示匹配的行)"""
        self.excludes.append(re.compile(pattern, re.IGNORECASE))
    
    def should_display(self, line):
        """判断是否应该显示该行"""
        # 检查排除条件
        for exclude_pattern in self.excludes:
            if exclude_pattern.search(line):
                return False
        
        # 检查过滤条件
        if self.filters:
            for filter_pattern in self.filters:
                if filter_pattern.search(line):
                    return True
            return False
        
        return True
    
    def highlight_line(self, line):
        """高亮显示日志行"""
        for pattern, color in self.highlight_patterns.items():
            if re.search(pattern, line, re.IGNORECASE):
                # 找到匹配的位置进行高亮
                match = re.search(pattern, line, re.IGNORECASE)
                if match:
                    start, end = match.span()
                    highlighted = (line[:start] + color + 
                                 line[start:end] + self.reset_color + 
                                 line[end:])
                    return highlighted
        return line
    
    def tail_file(self, filepath, n=10):
        """类似tail -f的功能"""
        filepath = Path(filepath)
        
        if not filepath.exists():
            print(f"❌ 文件不存在: {filepath}")
            return
        
        print(f"📁 开始追踪: {filepath}")
        print("-" * 80)
        
        # 先显示最后n行
        with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
            lines = f.readlines()
            for line in lines[-n:]:
                if self.should_display(line):
                    print(self.highlight_line(line.rstrip()))
        
        print("-" * 80)
        print("🔄 实时追踪中... (Ctrl+C停止)")
        print()
        
        # 实时追踪新内容
        try:
            with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
                # 移动到文件末尾
                f.seek(0, os.SEEK_END)
                
                while True:
                    line = f.readline()
                    if not line:
                        time.sleep(0.1)  # 等待新内容
                        continue
                    
                    if self.should_display(line):
                        highlighted = self.highlight_line(line.rstrip())
                        
                        # 添加时间戳
                        timestamp = time.strftime("[%H:%M:%S] ")
                        print(f"{timestamp}{highlighted}")
                        
        except KeyboardInterrupt:
            print("\n👋 停止追踪")
        except Exception as e:
            print(f"\n❌ 追踪错误: {e}")
    
    def tail_multiple(self, filepaths, n=5):
        """同时追踪多个文件"""
        print(f"📁 开始追踪 {len(filepaths)} 个文件")
        print("=" * 80)
        
        threads = []
        
        def tail_thread(filepath):
            try:
                self.tail_file(filepath, n)
            except Exception as e:
                print(f"❌ 追踪 {filepath} 失败: {e}")
        
        for filepath in filepaths:
            thread = threading.Thread(target=tail_thread, args=(filepath,))
            thread.daemon = True
            threads.append(thread)
            thread.start()
        
        try:
            # 主线程等待所有子线程
            for thread in threads:
                thread.join()
        except KeyboardInterrupt:
            print("\n👋 停止所有追踪")
    
    def search_logs(self, filepath, search_pattern, context_lines=3):
        """在日志中搜索并显示上下文"""
        filepath = Path(filepath)
        
        if not filepath.exists():
            print(f"❌ 文件不存在: {filepath}")
            return
        
        pattern = re.compile(search_pattern, re.IGNORECASE)
        
        print(f"🔍 在 {filepath} 中搜索: {search_pattern}")
        print("=" * 80)
        
        try:
            with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
                lines = f.readlines()
                
                for i, line in enumerate(lines):
                    if pattern.search(line):
                        # 显示上下文
                        start = max(0, i - context_lines)
                        end = min(len(lines), i + context_lines + 1)
                        
                        print(f"\n📍 第 {i+1} 行附近:")
                        print("-" * 40)
                        
                        for j in range(start, end):
                            if j == i:
                                # 高亮匹配行
                                print(f"\033[92m{j+1:6d}: {lines[j].rstrip()}\033[0m")
                            else:
                                print(f"{j+1:6d}: {lines[j].rstrip()}")
                        
                        print()
                        
        except Exception as e:
            print(f"❌ 搜索失败: {e}")
    
    def analyze_log_patterns(self, filepath, top_n=10):
        """分析日志模式(最常见的日志行)"""
        filepath = Path(filepath)
        
        if not filepath.exists():
            print(f"❌ 文件不存在: {filepath}")
            return
        
        print(f"📊 分析 {filepath} 中的日志模式")
        print("=" * 80)
        
        try:
            with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
                lines = f.readlines()
                
                # 统计日志行出现次数
                line_counter = defaultdict(int)
                
                for line in lines:
                    # 清理行(移除时间戳、IP等变量部分)
                    cleaned = self.clean_log_line(line)
                    line_counter[cleaned] += 1
                
                # 显示最常见的日志模式
                print(f"总行数: {len(lines)}")
                print(f"唯一模式数: {len(line_counter)}")
                print("\n最常见的日志模式:")
                print("-" * 80)
                
                for pattern, count in sorted(line_counter.items(), 
                                           key=lambda x: x[1], 
                                           reverse=True)[:top_n]:
                    percentage = (count / len(lines)) * 100
                    print(f"[{count:6d}次, {percentage:5.1f}%] {pattern[:100]}")
                    
        except Exception as e:
            print(f"❌ 分析失败: {e}")
    
    def clean_log_line(self, line):
        """清理日志行,提取模式"""
        # 移除时间戳
        line = re.sub(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}', '<TIMESTAMP>', line)
        line = re.sub(r'\d{2}/\w{3}/\d{4}:\d{2}:\d{2}:\d{2}', '<TIMESTAMP>', line)
        
        # 移除IP地址
        line = re.sub(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', '<IP>', line)
        
        # 移除数字ID
        line = re.sub(r'\b\d{5,}\b', '<NUMBER>', line)
        
        # 移除引号内容
        line = re.sub(r'"[^"]*"', '<QUOTED>', line)
        
        return line.strip()

if __name__ == "__main__":
    tracker = RealTimeLogTracker()
    
    # 使用示例
    print("Python实时日志追踪器")
    print("=" * 50)
    
    while True:
        print("\n请选择操作:")
        print("1. 实时追踪单个日志文件")
        print("2. 实时追踪多个日志文件")
        print("3. 搜索日志内容")
        print("4. 分析日志模式")
        print("5. 退出")
        
        choice = input("请输入选项 (1-5): ").strip()
        
        if choice == "1":
            filepath = input("请输入日志文件路径: ").strip()
            if filepath:
                # 添加一些常用过滤
                tracker.add_filter(r'ERROR|WARNING|Exception')
                tracker.tail_file(filepath)
        
        elif choice == "2":
            filepaths = input("请输入日志文件路径(用逗号分隔): ").strip()
            if filepaths:
                files = [f.strip() for f in filepaths.split(',')]
                tracker.tail_multiple(files)
        
        elif choice == "3":
            filepath = input("请输入日志文件路径: ").strip()
            pattern = input("请输入搜索关键词: ").strip()
            if filepath and pattern:
                tracker.search_logs(filepath, pattern)
        
        elif choice == "4":
            filepath = input("请输入日志文件路径: ").strip()
            if filepath:
                tracker.analyze_log_patterns(filepath)
        
        elif choice == "5":
            print("👋 再见!")
            break
        
        else:
            print("❌ 无效选项,请重新输入")

在这里插入图片描述

 



END
往期文章回顾

文中的概念来源于互联网,如有侵权,请联系我删除。

欢迎关注公众号:小周的数据库进阶之路,一起交流AI、数据库、中间件和云计算等技术。如果觉得读完本文有收获,可以转发给其他朋友,大家一起学习进步!感兴趣的朋友可以加我微信,拉您进群与业界的大佬们一起交流学习。



文章转载自小周的数据库进阶之路,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论