#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
完整的插件脚本,能够检测并报告所有类型的错误,按问题类型分组,并将连续时间戳合并为时间段
"""
import os
import Hubble.utils.basic.HiviewLogger as HiviewLogger
import re
import datetime
from datetime import datetime, timedelta
import json
class PluginClass:
def __init__(self):
self.logger = HiviewLogger.HiviewLogger().get_logger()
self.plugin_result = {
"conclusion": {
"level_one": "",
"level_two": "",
"reason_info": "",
"solution": "",
"weight_level": "",
},
"analysis_process": {
"show_order": ["1", "2"],
"detailed_process": {
"1": {"details": "", "CN": ""},
"2": {"details": "", "CN": ""}
}
},
"feature_data": {"": []}
}
def log_str_to_datetime(self, time_str):
try:
issue_time = datetime.strptime(time_str, '%m-%d %H:%M:%S.%f')
current_year = datetime.now().year
return issue_time.replace(year=current_year)
except ValueError:
try:
issue_time = datetime.strptime(time_str, '%m-%d %H:%M:%S')
current_year = datetime.now().year
return issue_time.replace(year=current_year)
except Exception:
return datetime.now()
def merge_time_ranges(self, events, max_gap_minutes=5):
"""修复的时间段合并函数"""
if not events:
return []
# 按时间戳排序
sorted_events = sorted(events, key=lambda x: self.log_str_to_datetime(x["timestamp"]))
merged_ranges = []
# 初始化第一个时间段
current_start = self.log_str_to_datetime(sorted_events[0]["timestamp"])
current_end = current_start
current_events = [sorted_events[0]] # 存储当前时间段的事件
for i in range(1, len(sorted_events)):
event = sorted_events[i]
event_time = self.log_str_to_datetime(event["timestamp"])
# 计算与前一个事件的时间间隔
gap = event_time - current_end
# 如果间隔小于阈值,则扩展当前时间段
if gap.total_seconds() <= max_gap_minutes * 60:
current_end = event_time
current_events.append(event)
else:
# 保存当前时间段
merged_ranges.append({
"start": current_start,
"end": current_end,
"count": len(current_events),
"events": current_events.copy() # 复制当前事件列表
})
# 开始新的时间段
current_start = event_time
current_end = event_time
current_events = [event]
# 添加最后一个时间段
merged_ranges.append({
"start": current_start,
"end": current_end,
"count": len(current_events),
"events": current_events
})
return merged_ranges
# 以下是各种问题检测函数(保持不变)
def memAvailable(self, hilog):
results = []
pattern = r'(?P<timestamp>\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*curBufKB=(?P<curBufKB>\d+)'
for line in hilog:
match = re.search(pattern, line)
if match:
timestamp = match.group("timestamp")
memAvailable_value = int(match.group('curBufKB'))
if memAvailable_value < 819200:
results.append({"type": "memAvailable", "timestamp": timestamp, "value": memAvailable_value})
return results
def am_kill(self, hilog):
results = []
pattern = r'(?P<timestamp>\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*KillOneProc kill_reason=LowMemoryKill.*procName=(?P<package>[a-zA-Z0-9._]+)'
for line in hilog:
match = re.search(pattern, line)
if match:
timestamp = match.group("timestamp")
kill_proc_name = match.group("package")
results.append({"type": "am_kill", "timestamp": timestamp, "proc_name": kill_proc_name})
return results
def temperature(self, hilog):
results = []
pattern = r'(?P<timestamp>\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*new temp: (?P<new_temp>\d+)'
for line in hilog:
match = re.search(pattern, line)
if match:
timestamp = match.group("timestamp")
temp_value = int(match.group("new_temp"))
if temp_value > 43:
results.append({"type": "temperature", "timestamp": timestamp, "value": temp_value})
return results
def storage(self, hilog):
results = []
pattern = r'(?P<timestamp>\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*roundSize=(?P<round_size>\d+),.*freeSize=(?P<free_size>\d+)'
for line in hilog:
match = re.search(pattern, line)
if match:
timestamp = match.group("timestamp")
round_size = int(match.group("round_size"))
free_size = int(match.group("free_size"))
if round_size > 0:
ratio = free_size / round_size
if ratio <= 0.15:
results.append({"type": "storage", "timestamp": timestamp, "ratio": ratio})
return results
def free_sec(self, hilog_kmsg):
results = []
pattern = r'(?P<timestamp>\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*free_sec=(?P<free_sec>\d+).*Free =(?P<free>\d+)MB'
for line in hilog_kmsg:
match = re.search(pattern, line)
if match:
timestamp = match.group("timestamp")
free_sec_value = int(match.group("free_sec"))
free_value = int(match.group("free"))
if free_value > 0:
ratio = 1 - (free_sec_value * 2) / free_value
if ratio > 0.8:
results.append({"type": "free_sec", "timestamp": timestamp, "ratio": ratio})
return results
def ps_slow(self, hilog):
results = []
pattern = r'(?P<timestamp>\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*CHR name=(PS_SLOW_EVENT), type'
for line in hilog:
match = re.search(pattern, line)
if match:
timestamp = match.group("timestamp")
results.append({"type": "ps_slow", "timestamp": timestamp})
return results
def power_low(self, hilog):
results = []
pattern = r'(?P<timestamp>\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*powermgr/BatteryInfo: capacity=(?P<capacity>\d+), voltage'
for line in hilog:
match = re.search(pattern, line)
if match:
timestamp = match.group("timestamp")
capacity_value = int(match.group("capacity"))
if capacity_value < 10:
results.append({"type": "power_low", "timestamp": timestamp, "value": capacity_value})
return results
def voltage(self, hilog):
results = []
pattern = r'(?P<timestamp>\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*powermgr/BatteryInfo: .*voltage=(?P<voltage>\d+), temperature'
for line in hilog:
match = re.search(pattern, line)
if match:
timestamp = match.group("timestamp")
voltage_value = int(match.group("voltage"))
if voltage_value < 3500000:
results.append({"type": "voltage", "timestamp": timestamp, "value": voltage_value})
return results
def cup_load(self, hilog):
results = []
pattern = r'(?P<timestamp>\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*cpuLoadPercent:(?P<cpuLoadPercent>\d+), thermalLevel'
for line in hilog:
match = re.search(pattern, line)
if match:
timestamp = match.group("timestamp")
try:
cpu_value = int(match.group("cpuLoadPercent"))
if cpu_value > 600000:
results.append({"type": "cup_load", "timestamp": timestamp, "value": cpu_value})
except ValueError:
continue
return results
def gpu_load(self, hilog):
results = []
pattern = r'(?P<timestamp>\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*Get GpuTotalUsed value is (?P<gpuUsed>\d+)'
for line in hilog:
match = re.search(pattern, line)
if match:
timestamp = match.group("timestamp")
try:
gpu_value = int(match.group("gpuUsed"))
if gpu_value > 600000:
results.append({"type": "gpu_load", "timestamp": timestamp, "value": gpu_value})
except ValueError:
continue
return results
def plugin_preprocess(self, info_dict=None):
self.logger.info("预处理函数执行中...")
task_list = []
download_list = info_dict["common_info"]["log_download_list"]
max_analysis_num = 1
if len(download_list) > max_analysis_num:
download_list = download_list[:max_analysis_num]
for download_item in download_list:
task_dict = {"download_addr": [], "task_params": {}}
task_dict["download_addr"] = download_item
task_list.append(task_dict)
return task_list
def run_plugin_single(self, uuid_log_path=None, log_path=None, param_dict=None):
self.logger.info("分析脚本执行中...uuid_log_path=[%s]", uuid_log_path)
error_mapping = {
"memAvailable": {"reason": "整机低内存(RAM)",
"solution": "1、清除后台应用:桌面手势导航底部上划进入多任务中心,清理后台多任务应用卡片(三键导航点击导航键的方块图标可以进入多任务界面)2、长按电源键关机重启"},
"am_kill": {"reason": "应用因低内存被查杀",
"solution": "1、清除后台应用:桌面手势导航底部上划进入多任务中心,清理后台多任务应用卡片(三键导航点击导航键的方块图标可以进入多任务界面)2、长按电源键关机重启"},
"temperature": {"reason": "整机温度过高", "solution": "建议参考手机/平板使用过程中设备发热相关知识排查"},
"storage": {"reason": "整机可用存储空间不足",
"solution": "建议参考'华为手机/平板内存占用多,提示内存不足如何处理?'知识中场景一:存储剩余空间不足排查方案处理。"},
"free_sec": {"reason": "整机碎片化过高",
"solution": "1、建议引导用户进入设置>存储,进行整机清理加速,清理不需要的垃圾文件、联网缓存及不常用的应用;2、建议用户尝试夜间熄屏充电,持续此操作2-3晚上清理碎片化,促进系统优化"},
"ps_slow": {"reason": "整机网络不佳",
"solution": "当前所处网络环境覆盖较弱或干扰较大,建议引导用户更换网络环境"},
"power_low": {"reason": "整机低电量", "solution": "建议在电量充足情况下使用"},
"voltage": {"reason": "整机低电压", "solution": "建议检查电池健康度并在电量充足情况下继续使用体验"},
"cup_load": {"reason": "整机CPU高负载",
"solution": "1、清除后台应用:桌面手势导航底部上划进入多任务中心,清理后台多任务应用卡片(三键导航点击导航键的方块图标可以进入多任务界面);2、引导用户排查是否存在高速下载、 数据传输、多悬浮窗口使用等场景,以上高负载场景会导致设备卡顿,建议用户避免长时间使用上述场景;3、长按电源键关机重启;"},
"gpu_load": {"reason": "整机GPU高负载",
"solution": "1、清除后台应用:桌面手势导航底部上划进入多任务中心,清理后台多任务应用卡片(三键导航点击导航键的方块图标可以进入多任务界面);2、引导用户排查是否存在高速下载、 数据传输、多悬浮窗口使用等场景,以上高负载场景会导致设备卡顿,建议用户避免长时间使用上述场景;3、长按电源键关机重启;"}
}
app_path = []
kmsg_path = []
for root, dirs, files in os.walk(uuid_log_path):
for file in files:
if "hilog." in file:
app_path.append(os.path.join(root, file))
if "hilog_kmsg" in file:
kmsg_path.append(os.path.join(root, file))
app_list = []
for app in app_path:
try:
with open(app, "r", encoding="utf-8", errors='ignore') as app_reader:
app_list += app_reader.readlines()
except Exception as e:
self.logger.error(f"读取日志文件失败: {e}")
kmsg_list = []
for kmsg in kmsg_path:
try:
with open(kmsg, "r", encoding="utf-8", errors='ignore') as kmsg_reader:
kmsg_list += kmsg_reader.readlines()
except Exception as e:
self.logger.error(f"读取kmsg日志失败: {e}")
detected_issues = {
"memAvailable": self.memAvailable(app_list),
"am_kill": self.am_kill(app_list),
"temperature": self.temperature(app_list),
"storage": self.storage(app_list),
"free_sec": self.free_sec(kmsg_list),
"ps_slow": self.ps_slow(app_list),
"power_low": self.power_low(app_list),
"voltage": self.voltage(app_list),
"cup_load": self.cup_load(app_list),
"gpu_load": self.gpu_load(app_list)
}
merged_issues = {}
for issue_type, issues in detected_issues.items():
if issues:
merged_issues[issue_type] = self.merge_time_ranges(issues)
report_by_type = {}
solutions = set()
for issue_type, time_ranges in merged_issues.items():
if not time_ranges:
continue
issue_report = []
for time_range in time_ranges:
start_time = time_range["start"].strftime("%m-%d %H:%M:%S")
end_time = time_range["end"].strftime("%m-%d %H:%M:%S")
if time_range["count"] == 1:
time_info = f"{start_time}"
else:
time_info = f"{start_time} 至 {end_time} ({time_range['count']}次)"
details = error_mapping[issue_type]["reason"]
if issue_type == "am_kill":
apps = list(set(event.get("proc_name", "未知应用") for event in time_range["events"]))
details += f" ({', '.join(apps)})"
elif issue_type in ["memAvailable", "temperature", "power_low", "voltage", "cup_load", "gpu_load"]:
values = [event.get("value", 0) for event in time_range["events"]]
min_val = min(values)
max_val = max(values)
avg_val = sum(values) / len(values)
unit = ""
if issue_type == "memAvailable":
unit = "KB"
elif issue_type == "temperature":
unit = "°C"
elif issue_type == "power_low":
unit = "%"
elif issue_type == "voltage":
unit = "mV"
details += f" (值范围: {min_val}-{max_val}{unit}, 平均: {avg_val:.1f}{unit})"
elif issue_type in ["storage", "free_sec"]:
ratios = [event.get("ratio", 0) for event in time_range["events"]]
avg_ratio = sum(ratios) / len(ratios) * 100
details += f" (平均: {avg_ratio:.1f}%)"
issue_report.append(f"{time_info}: {details}")
report_by_type[issue_type] = issue_report
solutions.add(error_mapping[issue_type]["solution"])
reason_info = []
if not report_by_type:
reason_info.append("当前整机各项指标未诊断出异常")
else:
for issue_type, issue_list in report_by_type.items():
reason_info.append(f"<b>{error_mapping[issue_type]['reason']}</b>")
reason_info.extend(issue_list)
reason_info.append("")
solution_str = "<br>".join(solutions) if solutions else"建议引导用户重新复现问题反馈日志"
self.plugin_result["conclusion"]["reason_info"] = "<br>".join(reason_info)
self.plugin_result["conclusion"]["solution"] = solution_str
self.plugin_result["conclusion"]["level_one"] = "稳定性问题"
self.plugin_result["conclusion"]["weight_level"] = "medium"
return self.plugin_result
def plugin_result_refinement(self, result):
return result
这是我现在的脚本,已经没有异常了,目前整机温度过高显示的是 09-15 12:53:28: 整机温度过高 (值范围: 44-69°C, 平均: 48.7°C),这个数值收集的有点问题,日志是这样的
16598: 04-17 18:33:07.767 636 60287 I C02D15/hiview/XPower: [task_148]#ThermalDaemon:[shell_frame] new temp: 36 old temp: 37
需要收集的值是[shell_frame] new temp:后面的数字,但是[shell_front] new temp:后面的也一块收集了,现在只收集[shell_frame] new temp:后面的数字,43度以上显示显示方式原封不动,48度以上的不要显示温度是多少,直接显示温度异常,顺便帮我加一下注释,然后帮我返回完整代码: