夜莺监控系统告警数据分析与报告生成脚本

夜莺监控系统告警数据分析与报告生成脚本

脚本概述

这是一个用于分析夜莺监控系统告警数据并生成可视化报告的Python脚本。它能够自动获取未恢复的告警信息,进行数据分析,生成统计图表,并将报告发送到指定的钉钉群组。

个人博客

个人博客直达地址
网站不断完善中里面拥有大量的脚本,并且源码完全开放 欢迎纯白嫖。

效果图

在这里插入图片描述

主要功能

  1. 数据获取:通过API从夜莺监控系统获取最近7天的未恢复告警数据。
  2. 数据分析:对获取的告警数据进行统计和分析。
  3. 可视化:使用matplotlib和seaborn生成多种统计图表。
  4. 报告生成:创建包含统计摘要和可视化图表的综合报告。
  5. OSS上传:将生成的报告图片上传到阿里云OSS存储。
  6. 消息推送:通过钉钉机器人将报告发送到指定群组。

详细功能说明

1. 数据获取

  • 使用get_access_token()函数获取夜莺API的访问令牌。
  • 通过query_unrecovered_alerts()函数获取未恢复的告警数据。

2. 数据分析

  • generate_summary()函数生成告警数据的文字摘要,包括总告警次数、时间范围、告警类型分布、严重级别分布和处理状态分布。

3. 可视化

generate_report()函数生成多个统计图表:

  • 每个集群的告警次数分布
  • 每个服务的告警次数分布
  • 每天的告警数量趋势
  • 告警严重级别分布
  • 告警处理状态分布
  • 每条规则的告警次数分布
  • 每个标签的告警次数分布

4. 报告生成

  • 将统计摘要和可视化图表整合为一个完整的报告。

5. OSS上传

  • 使用upload_to_oss()函数将生成的报告图片上传到阿里云OSS。

6. 消息推送

  • send_to_webhook()函数将报告通过钉钉机器人发送到指定群组。

使用说明

  1. 确保已安装所有必要的Python库:requests, pandas, matplotlib, seaborn, oss2。
  2. 配置环境变量:
    • OSS_ACCESS_KEY_ID:阿里云OSS的AccessKey ID
    • OSS_ACCESS_KEY_SECRET:阿里云OSS的AccessKey Secret
  3. 修改脚本中的以下参数:
    • 夜莺API的URL和登录凭证
    • 阿里云OSS的bucket名称和endpoint
    • 钉钉机器人的webhook URL
  4. 运行脚本:python script_name.py

注意事项

  1. API访问限制:注意夜莺API的访问频率限制,避免频繁调用导致被封禁。
  2. 数据安全:脚本处理敏感的监控数据,确保运行环境的安全性。
  3. 依赖管理:确保所有依赖库的版本兼容,建议使用虚拟环境。
  4. 错误处理:脚本包含基本的错误处理,但在生产环境中可能需要更robust的错误处理机制。
  5. 定制化:根据实际需求,可能需要调整图表样式、报告内容等。
  6. 资源消耗:生成大量图表可能消耗较多CPU和内存,注意监控资源使用。
  7. 字体配置:确保系统中安装了指定的中文字体(wqy-zenhei),否则可能导致中文显示问题。
  8. OSS配置:确保OSS配置正确,包括访问权限和存储策略。
  9. 钉钉限制:注意钉钉机器人的消息发送频率和大小限制。

代码结构

脚本主要包含以下几个关键函数:

  1. get_access_token(): 获取API访问令牌
  2. query_unrecovered_alerts(): 查询未恢复的告警
  3. convert_unix_to_datetime(): 转换Unix时间戳
  4. generate_summary(): 生成告警摘要
  5. generate_report(): 生成可视化报告
  6. upload_to_oss(): 上传图片到OSS
  7. send_to_webhook(): 发送报告到钉钉

未来改进方向

  1. 添加更多的数据分析维度和图表类型
  2. 实现定时自动运行功能
  3. 增加邮件发送选项
  4. 优化性能,减少API调用次数
  5. 增加交互式选项,允许用户自定义报告内容
#!/usr/bin/python3
import requests
import json
import pandas as pd
from io import BytesIO
from datetime import datetime
import oss2
import os
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib import font_manager as fm
from datetime import datetime, timedelta
# 获取当前时间
now = datetime.now()
now_days = now.strftime("%Y-%m-%d %H:%M:%S")

# 计算7天前的日期
seven_days_ago = now - timedelta(days=7)
seve_days = seven_days_ago.strftime("%Y-%m-%d %H:%M:%S")
# 设置字体路径
font_path = "/usr/share/fonts/wqy-zenhei/wqy-zenhei.ttc"
font_prop = fm.FontProperties(fname=font_path)
plt.rcParams['font.family'] = 'wqy-zenhei'
plt.rcParams.update({'font.size': 30})

# 登录并获取 access_token
def get_access_token():
    url = "http://自己夜莺的域名:18000/api/n9e/auth/login"
    data = {"username": "zhumengkang", "password": "hp12580"}
    response = requests.post(url, json=data)
    response.raise_for_status()
    return response.json()["dat"]["access_token"]

# 查询未恢复的报警
def query_unrecovered_alerts(access_token, hours=168, limit=500, page=1):
    headers = {"Authorization": f"Bearer {access_token}"}
    url = "http://自己夜莺的域名/api/n9e/alert-his-events/list"
    params = {"bgid": -1, "hours": hours, "limit": limit, "p": page}
    print(str(url)+str(params))
    response = requests.get(url, headers=headers, params=params)
    response.raise_for_status()
    return response.json()["dat"]["list"]

# 将Unix时间戳转换为日期格式
def convert_unix_to_datetime(df, column_name):
    df[column_name] = pd.to_datetime(df[column_name], unit='s', errors='coerce')
    return df

# 生成汇总报告
def generate_summary(alerts):
    df = pd.DataFrame(alerts)
    if df.empty:
        return "没有未恢复的报警记录。"

    # 转换时间戳
    df = convert_unix_to_datetime(df, 'trigger_time')
    df = convert_unix_to_datetime(df, 'recover_time')

    summary = "## 未恢复报警汇总报告\n\n"
    summary += f"- 总报警次数: {len(df)}\n"
    summary += f"- 时间范围: {seve_days}{now_days}\n\n"

    # 报警类型
    if 'cate' in df.columns:
        type_dist = df['cate'].value_counts()
        summary += "### 报警类型分布\n"
        for alert_type, count in type_dist.items():
            summary += f"- 类型 {alert_type}: {count} 次\n"

    # 严重级别
    if 'severity' in df.columns:
        severity_dist = df['severity'].value_counts()
        summary += "### 报警严重级别分布\n"
        for level, count in severity_dist.items():
            summary += f"- 级别 {level}: {count} 次\n"

    # 处理状态
    if 'is_recovered' in df.columns:
        status_dist = df['is_recovered'].value_counts()
        summary += "### 处理状态分布\n"
        for status, count in status_dist.items():
            summary += f"- 状态 {status}: {count} 次\n"

    return summary


# 生成并保存报表
def generate_report(df):
    # 检查并转换时间列
    df = convert_unix_to_datetime(df, 'trigger_time')

    # 处理数据
    df['date'] = df['trigger_time'].dt.date

    image_buffer = BytesIO()
    plt.figure(figsize=(50, 36), dpi=100)  # 增加图像大小和分辨率
    sns.set(style="whitegrid")

    # 1. 每个集群的报警次数分布
    plt.subplot(4, 2, 1)
    cluster_dist = df['cluster'].value_counts()
    sns.barplot(x=cluster_dist.index, y=cluster_dist.values, palette='viridis')
    plt.title("每个集群的报警次数", fontproperties=font_prop, fontsize=45)
    plt.xlabel("集群", fontproperties=font_prop, fontsize=35)
    plt.ylabel("报警次数", fontproperties=font_prop, fontsize=35)
    plt.xticks(fontproperties=font_prop, fontsize=30)
    plt.yticks(fontproperties=font_prop, fontsize=30)
    plt.grid(axis='y', linestyle='--', alpha=0.7)

    # 2. 每个服务的报警次数分布
    plt.subplot(4, 2, 2)
    project_dist = df['tags'].apply(
        lambda x: next((tag.split('=')[1] for tag in x if tag.startswith('project=')), '未知')).value_counts()
    sns.barplot(x=project_dist.index, y=project_dist.values, palette='magma')
    plt.title("每个服务的报警次数", fontproperties=font_prop, fontsize=45)
    plt.xlabel("服务", fontproperties=font_prop, fontsize=35)
    plt.ylabel("报警次数", fontproperties=font_prop, fontsize=35)
    plt.xticks(fontproperties=font_prop, fontsize=30)
    plt.yticks(fontproperties=font_prop, fontsize=30)
    plt.grid(axis='y', linestyle='--', alpha=0.7)

    # 3. 每天的报警数量
    plt.subplot(4, 2, 3)
    daily_dist = df['date'].value_counts().sort_index()
    sns.lineplot(x=daily_dist.index, y=daily_dist.values, marker='o', color='teal')
    plt.title("每天的报警数量", fontproperties=font_prop, fontsize=45)
    plt.xlabel("日期", fontproperties=font_prop, fontsize=35)
    plt.ylabel("报警次数", fontproperties=font_prop, fontsize=35)
    plt.xticks(rotation=90, fontproperties=font_prop, fontsize=30)
    plt.yticks(fontproperties=font_prop, fontsize=30)
    plt.grid(axis='y', linestyle='--', alpha=0.7)

    # 4. 严重级别分布
    plt.subplot(4, 2, 4)
    severity_dist = df['severity'].value_counts()
    sns.barplot(x=severity_dist.index, y=severity_dist.values, palette='Set1')
    plt.title("报警严重级别分布", fontproperties=font_prop, fontsize=45)
    plt.xlabel("严重级别", fontproperties=font_prop, fontsize=35)
    plt.ylabel("报警次数", fontproperties=font_prop, fontsize=35)
    plt.xticks(fontproperties=font_prop, fontsize=30)
    plt.yticks(fontproperties=font_prop, fontsize=30)
    plt.grid(axis='y', linestyle='--', alpha=0.7)

    # 5. 处理状态分布
    plt.subplot(4, 2, 5)
    status_dist = df['is_recovered'].value_counts()
    sns.barplot(x=status_dist.index, y=status_dist.values, palette='Set2')
    plt.title("处理状态分布", fontproperties=font_prop, fontsize=45)
    plt.xlabel("处理状态", fontproperties=font_prop, fontsize=35)
    plt.ylabel("报警次数", fontproperties=font_prop, fontsize=35)
    plt.xticks(fontproperties=font_prop, fontsize=30)
    plt.yticks(fontproperties=font_prop, fontsize=30)
    plt.grid(axis='y', linestyle='--', alpha=0.7)

    # 6. 每条规则的报警次数分布
    plt.subplot(4, 2, 6)
    rule_dist = df['rule_name'].value_counts()
    sns.barplot(x=rule_dist.index, y=rule_dist.values, palette='cubehelix')
    plt.title("每条规则的报警次数", fontproperties=font_prop, fontsize=45)
    plt.xlabel("规则名称", fontproperties=font_prop, fontsize=35)
    plt.ylabel("报警次数", fontproperties=font_prop, fontsize=35)
    plt.xticks(rotation=90, fontproperties=font_prop, fontsize=30)
    plt.yticks(fontproperties=font_prop, fontsize=30)
    plt.grid(axis='y', linestyle='--', alpha=0.7)

    # 7. 每个标签的报警次数分布
    plt.subplot(4, 2, 7)
    tag_dist = df['tags'].explode().value_counts()
    sns.barplot(x=tag_dist.index, y=tag_dist.values, palette='Spectral')
    plt.title("每个标签的报警次数", fontproperties=font_prop, fontsize=45)
    plt.xlabel("标签", fontproperties=font_prop, fontsize=35)
    plt.ylabel("报警次数", fontproperties=font_prop, fontsize=35)
    plt.xticks(rotation=90, fontproperties=font_prop, fontsize=30)
    plt.yticks(fontproperties=font_prop, fontsize=30)
    plt.grid(axis='y', linestyle='--', alpha=0.7)

    plt.tight_layout()
    plt.savefig(image_buffer, format='png')
    image_buffer.seek(0)
    return image_buffer

# 上传图片到 OSS 并使用唯一文件名
def upload_to_oss(image_buffer, bucket_name):
    timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
    object_name = f'夜莺_{timestamp}.png'
    auth = oss2.Auth(os.getenv('OSS_ACCESS_KEY_ID'), os.getenv('OSS_ACCESS_KEY_SECRET'))
    bucket = oss2.Bucket(auth, 'https://oss-cn-hangzhou.aliyuncs.com', bucket_name)
    bucket.put_object(object_name, image_buffer)
    print(f"文件上传成功: {object_name}")
    return object_name
# 发送到 Webhook
def send_to_webhook(image_url, summary):
    webhook_url = "https://oapi.dingtalk.com/robot/send?access_token=3d3c3a0e7f45bd760b6d0dde83fe5b193783a50a9db5a5f92db1b27c54XXXXX"
    headers = {"Content-Type": "application/json"}
    data = {
        "msgtype": "markdown",
        "markdown": {
            "title": "报警汇总报告",
            "text": f"## TEST报警报告\n{summary}\n![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=%7Bimage_url%7D&pos_id=img-QWDlaO17-1724752842269)"
        },
        "at": {
            "isAtAll": True  # @所有人
        }
    }
    response = requests.post(webhook_url, headers=headers, data=json.dumps(data))
    print("Webhook 响应状态码:", response.status_code)
    print("Webhook 响应内容:", response.text)
    response.raise_for_status()
    print("消息发送成功")



if __name__ == "__main__":
    try:
        access_token = get_access_token()
        alerts = query_unrecovered_alerts(access_token, hours=168, limit=500, page=1)

        # 调试信息:打印获取到的告警数据
        print("获取到的告警数据:")
        print(alerts)

        summary = generate_summary(alerts)
        df = pd.DataFrame(alerts)

        # 调试信息:打印数据框信息
        print("数据框信息:")
        print(df.info())
        print("数据框前几行:")
        print(df.head())

        if not df.empty:
            image_buffer = generate_report(df)

            # 上传到 OSS
            object_name = upload_to_oss(image_buffer, '自己OSS的名称')

            # 生成图片 URL
            image_url = f"https://自己OSS的名称.oss-cn-hangzhou.aliyuncs.com/{object_name}"

            # 发送到 Webhook
            send_to_webhook(image_url, summary)
        else:
            print("没有数据生成报告")
    except Exception as e:
        print(f"发生错误: {e}")

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

脚本小能手

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值