SP-01 · 硬盘健康可视化:将 SMART 数据转化为体检报告
引言:直击痛点
你有没有遇到过这样的场景:Mac 突然卡顿,打开"活动监视器"发现硬盘读写爆表,但系统自带的"磁盘工具"只能告诉你"验证成功",却看不到任何历史趋势?更糟糕的是,当你想向老板汇报"这台设备还能再战几年"时,只能拿出一堆冰冷的数字:138 TB 读取、56.7 TB 写入、寿命 1%……这些数字对非技术人员来说,就像天书。
今天我们要解决的痛点是:如何用 Python 将硬盘的 SMART 数据(读写量、寿命、温度、异常关机次数)转化为一张"一眼就懂"的健康报告图表?就像体检报告一样,用饼图、柱状图和颜色编码(绿色=健康,橙色=警告,红色=危险)让任何人都能秒懂硬盘状态。
核心比喻:硬盘就像一辆汽车,SMART 数据是它的"行车记录仪",而我们要做的,是把这些记录翻译成"保养建议单"。
步步为营:核心逻辑拆解
Step 1:搭建 2×2 画布 —— 四象限布局的艺术
痛点解析:如果把 4 个指标(读写量、寿命、健康指标、使用统计)挤在一张图里,就像把 4 个人塞进一个电话亭,谁也看不清。我们需要一个"四宫格"布局,每个象限独立展示一个维度。
实战代码:
import matplotlib.pyplot as plt
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(12, 10))
fig.suptitle('硬盘健康状况报告 - Apple SSD AP2048Z', fontsize=16, fontweight='bold')
print("✓ 画布已创建:2 行 × 2 列 = 4 个独立展示区")
# 💻 终端预期输出:
# ✓ 画布已创建:2 行 × 2 列 = 4 个独立展示区
极客点评:plt.subplots(2, 2) 就像在餐厅订了一个 4 人桌,每个人(每个指标)都有自己的位置,不会互相干扰。figsize=(12, 10) 确保这张"桌子"足够大,不会显得拥挤。
Step 2:读写数据量对比 —— 用柱状图讲故事
痛点解析:硬盘的读写比例能反映使用习惯:如果读取远大于写入(如 138 TB vs 56.7 TB),说明这是一台"消费型设备"(看视频、读文档多);如果写入接近读取,可能是"生产型设备"(剪视频、跑模型)。
实战代码:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(6, 4))
# 用不同颜色区分读写:绿色=读取(安全操作),蓝色=写入(磨损操作)
ax.bar(['累计读取', '累计写入'], [138, 56.7], color=['#4CAF50', '#2196F3'])
ax.set_ylabel('数据量 (TB)', fontsize=11)
ax.set_title('累计读写数据量', fontsize=12, fontweight='bold')
# 在柱子顶部标注具体数值(避免用户眯眼看坐标轴)
for i, v in enumerate([138, 56.7]):
ax.text(i, v + 3, f'{v} TB', ha='center', fontweight='bold')
plt.tight_layout()
plt.savefig('step2_demo.png', dpi=150)
print("✓ 读写对比图已生成")
# 💻 终端预期输出:
# ✓ 读写对比图已生成
极客点评:这就像超市的收银小票,左边是"进货量"(读取),右边是"出货量"(写入)。绿色代表"只是看看"(读取不伤硬盘),蓝色代表"真金白银"(写入会消耗寿命)。
Step 3:寿命使用情况 —— 饼图的视觉冲击力
痛点解析:当你告诉老板"硬盘寿命还剩 99%",他可能无感。但如果你展示一个饼图,橙色只占 1%,灰色占 99%,他会立刻理解:“哦,这设备还很新!”
实战代码:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(6, 6))
# 饼图的精髓:用面积比例传达信息
sizes = [1, 99] # 已使用 1%,剩余 99%
colors = ['#FF9800', '#E0E0E0'] # 橙色=警示,灰色=安全余量
labels = ['已使用 1%', '剩余 99%']
ax.pie(sizes, labels=labels, autopct='%1.0f%%', colors=colors, startangle=90)
ax.set_title('硬盘寿命使用情况', fontsize=12, fontweight='bold')
plt.savefig('step3_demo.png', dpi=150)
print("✓ 寿命饼图已生成:橙色区域越小越健康")
# 💻 终端预期输出:
# ✓ 寿命饼图已生成:橙色区域越小越健康
极客点评:饼图就像手机电量显示,1% 的橙色就像"低电量警告",但这里是反向的——橙色越小越好!startangle=90 让饼图从 12 点钟方向开始切,符合人类的阅读习惯。
Step 4:健康指标 —— 红绿灯式的状态编码
痛点解析:硬盘有 4 个关键健康指标:备用空间(100% 最佳)、数据错误(0 最佳)、介质错误(0 最佳)、温度(60°C 以下正常)。如果用表格展示,用户需要逐行对比;但如果用柱状图 + 颜色编码,一眼就能看出哪里有问题。
实战代码:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(8, 5))
indicators = ['剩余备用\n空间', '数据完整性\n错误', '介质\n错误', '温度\n(°C)']
values = [100, 0, 0, 61]
# 绿色=健康,黄色=需要关注
colors_ind = ['#4CAF50', '#4CAF50', '#4CAF50', '#FFC107']
bars = ax.bar(indicators, values, color=colors_ind)
ax.set_title('健康指标', fontsize=12, fontweight='bold')
ax.set_ylabel('数值', fontsize=11)
# 智能标注:百分比、温度、纯数字分别处理
for i, (bar, v) in enumerate(zip(bars, values)):
label = f'{v}%' if i == 0 else (f'{v}°C' if i == 3 else str(v))
ax.text(bar.get_x() + bar.get_width()/2, v + 2, label,
ha='center', fontweight='bold')
plt.tight_layout()
plt.savefig('step4_demo.png', dpi=150)
print("✓ 健康指标图已生成:绿色=安全,黄色=关注")
# 💻 终端预期输出:
# ✓ 健康指标图已生成:绿色=安全,黄色=关注
极客点评:这就像汽车仪表盘,绿灯亮表示"一切正常",黄灯亮表示"该保养了"。温度 61°C 虽然不算高,但用黄色提醒"别让它再热了"。
Step 5:使用统计 —— 揭示设备的"工作强度"
痛点解析:开机时长、电源循环次数、异常关机次数,这三个指标能反映设备的使用场景:如果异常关机次数多(如 17 次),可能是用户经常强制关机,或者电源不稳定。
实战代码:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(8, 5))
stats = ['开机时长\n(小时)', '电源循环\n(次)', '异常关机\n(次)']
stat_values = [957, 125, 17]
# 紫色=时长,蓝色=正常操作,红色=异常事件
colors_stat = ['#9C27B0', '#3F51B5', '#F44336']
ax.bar(stats, stat_values, color=colors_stat)
ax.set_title('使用统计', fontsize=12, fontweight='bold')
ax.set_ylabel('次数/小时', fontsize=11)
for i, v in enumerate(stat_values):
ax.text(i, v + 20, str(v), ha='center', fontweight='bold')
plt.tight_layout()
plt.savefig('step5_demo.png', dpi=150)
print("✓ 使用统计图已生成:红色柱子越高越需要警惕")
# 💻 终端预期输出:
# ✓ 使用统计图已生成:红色柱子越高越需要警惕
极客点评:红色的"异常关机"柱子就像体检报告里的"血压偏高",虽然不致命,但需要改善使用习惯(比如别直接拔电源)。
极客实战:完整工程源码
"""
硬盘健康状况可视化报告生成器
功能:将 SMART 数据转化为 4 象限健康报告图表
适用场景:设备巡检、资产管理、向非技术人员汇报
"""
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from typing import List, Tuple
def generate_disk_health_report(
read_tb: float = 138.0,
write_tb: float = 56.7,
life_used_percent: int = 1,
spare_percent: int = 100,
data_errors: int = 0,
media_errors: int = 0,
temperature_c: int = 61,
power_on_hours: int = 957,
power_cycles: int = 125,
unsafe_shutdowns: int = 17,
output_path: str = 'disk_health_report.png'
) -> None:
"""
生成硬盘健康报告图表
Args:
read_tb: 累计读取数据量(TB)
write_tb: 累计写入数据量(TB)
life_used_percent: 寿命使用百分比
spare_percent: 剩余备用空间百分比
data_errors: 数据完整性错误次数
media_errors: 介质错误次数
temperature_c: 当前温度(摄氏度)
power_on_hours: 累计开机时长(小时)
power_cycles: 电源循环次数
unsafe_shutdowns: 异常关机次数
output_path: 输出文件路径
"""
# 创建 2×2 子图布局
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(12, 10))
fig.suptitle('硬盘健康状况报告 - Apple SSD AP2048Z',
fontsize=16, fontweight='bold')
# 1. 读写数据量对比
ax1.bar(['累计读取', '累计写入'], [read_tb, write_tb],
color=['#4CAF50', '#2196F3'])
ax1.set_ylabel('数据量 (TB)', fontsize=11)
ax1.set_title('累计读写数据量', fontsize=12, fontweight='bold')
for i, v in enumerate([read_tb, write_tb]):
ax1.text(i, v + 3, f'{v} TB', ha='center', fontweight='bold')
# 2. 寿命使用情况
life_remaining = 100 - life_used_percent
sizes = [life_used_percent, life_remaining]
colors = ['#FF9800', '#E0E0E0']
ax2.pie(sizes, labels=[f'已使用 {life_used_percent}%',
f'剩余 {life_remaining}%'],
autopct='%1.0f%%', colors=colors, startangle=90)
ax2.set_title('硬盘寿命使用情况', fontsize=12, fontweight='bold')
# 3. 健康指标
indicators = ['剩余备用\n空间', '数据完整性\n错误', '介质\n错误', '温度\n(°C)']
values = [spare_percent, data_errors, media_errors, temperature_c]
# 动态颜色:温度 > 70°C 变红,否则绿色/黄色
colors_ind = [
'#4CAF50', # 备用空间
'#4CAF50' if data_errors == 0 else '#F44336', # 数据错误
'#4CAF50' if media_errors == 0 else '#F44336', # 介质错误
'#4CAF50' if temperature_c < 60 else ('#FFC107' if temperature_c < 70 else '#F44336')
]
bars = ax3.bar(indicators, values, color=colors_ind)
ax3.set_title('健康指标', fontsize=12, fontweight='bold')
ax3.set_ylabel('数值', fontsize=11)
for i, (bar, v) in enumerate(zip(bars, values)):
label = f'{v}%' if i == 0 else (f'{v}°C' if i == 3 else str(v))
ax3.text(bar.get_x() + bar.get_width()/2, v + 2, label,
ha='center', fontweight='bold')
# 4. 使用统计
stats = ['开机时长\n(小时)', '电源循环\n(次)', '异常关机\n(次)']
stat_values = [power_on_hours, power_cycles, unsafe_shutdowns]
ax4.bar(stats, stat_values, color=['#9C27B0', '#3F51B5', '#F44336'])
ax4.set_title('使用统计', fontsize=12, fontweight='bold')
ax4.set_ylabel('次数/小时', fontsize=11)
for i, v in enumerate(stat_values):
ax4.text(i, v + 20, str(v), ha='center', fontweight='bold')
# 保存图表
plt.tight_layout()
plt.savefig(output_path, dpi=300, bbox_inches='tight')
print(f"✓ 图表已保存为: {output_path}")
# 生成健康评分(0-100 分)
health_score = calculate_health_score(
life_used_percent, spare_percent, data_errors,
media_errors, temperature_c, unsafe_shutdowns
)
print(f"✓ 综合健康评分: {health_score}/100")
def calculate_health_score(
life_used: int, spare: int, data_err: int,
media_err: int, temp: int, unsafe: int
) -> int:
"""计算硬盘综合健康评分"""
score = 100
score -= life_used # 寿命每用 1% 扣 1 分
score -= (100 - spare) # 备用空间每少 1% 扣 1 分
score -= data_err * 5 # 每个数据错误扣 5 分
score -= media_err * 10 # 每个介质错误扣 10 分
score -= max(0, (temp - 60) * 2) # 温度超 60°C 每度扣 2 分
score -= unsafe * 2 # 每次异常关机扣 2 分
return max(0, score)
if __name__ == '__main__':
# 使用默认参数生成报告
generate_disk_health_report()
# 或者自定义参数(模拟一个"亚健康"硬盘)
# generate_disk_health_report(
# life_used_percent=15,
# temperature_c=75,
# unsafe_shutdowns=50,
# output_path='unhealthy_disk.png'
# )
架构师结语
这个工具的核心价值不在于"画图",而在于将冰冷的数字转化为决策依据。当你向老板汇报"这台设备还能用 3 年"时,一张健康报告图表的说服力,远胜于一堆 SMART 日志。
记住三个工程心智:
- 可视化是沟通的桥梁:技术人员看数字,非技术人员看颜色。
- 颜色编码是视觉语言:绿色=放心,黄色=关注,红色=立刻处理。
- 参数化设计是可扩展性的基石:今天监控硬盘,明天可以监控服务器、网络设备,甚至员工的工作状态。
现在,去把你的 Mac 的 SMART 数据(可以用 smartctl -a /dev/disk0 获取)喂给这个脚本,生成你的第一份硬盘体检报告吧!