# !/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
说明:此文件是插件脚本的入口Py文件模板,任何其他插件脚本要加入Hubble,需以此模板为基础进行扩展开发
注意事项:
1. 插件文件命名和所在文件夹相同,例如DemoPlugin下面的DemoPlugin.py
2. 在HubblePlatform.py的trigger_plugin函数中,要更新配置变量plugin_property_dict(以后会放到线上去)
3. 新引入的Python3模块需要告知Hubble开发团队的后台部署人员 WX601464,pip3 install NewPackage.py后才能正常运行,否则会导致后台程序错误!
4. 任何问题请联系?WX601464/w30016103
"""
import os
import Hubble.utils.basic.HiviewLogger as HiviewLogger
import re
import datetime
from datetime import datetime, timedelta
import json
class PluginClass():
"""
Hubble程序会自动检测PluginClass名称的类,进行加工
1. self.logger: Hubble日志打印模块变量,可打印详细日志信息,便于后期调试,推荐使用self.logger.info("xxx"),本地调试时请注释self.logger相关命令。
2. self.plugin_result: Hubble能识别到的输出接口,最小填写量"conclusion""reason_info",具体解释请见变量定义处
3. plugin_preprocess: 预处理Hubble传入的info_dict,包含程序运行的基本信息
4. run_plugin_single: 单日志分析函数,插件脚本的分析能力体现于此
5. plugin_result_refinement: 对于批量处理多日志情况,run_plugin_single的单日志处理结果会汇总成一个结果列表,
基于此列表,用户可以进行二次精细化汇总,例如统计平均值等
"""
def __init__(self):
"""
初始化函数
"""
self.logger = HiviewLogger.HiviewLogger().get_logger()
self.plugin_result = { # 填好对应项,会在Hubble单任务分析报告里看见结果展示
"conclusion": { # 分析结论(定界结论)展示
"level_one": "", # 一级定界
"level_two": "", # 二级定界
"reason_info": "", # 根因信息
"solution": "",
"weight_level": "" # 脚本权重,界面展示时作为排序依据,有效值范围[5,10]
},
"analysis_process": { # 分析结论(定界过程)展示,列表中每一个dict元素代表定界过程中的一步
"show_order": [ # 展示顺序, 定义"1", "2", ...步骤
"1", "2"
],
"detailed_process": { # 按show_order的步骤取出过程
"1": {
"details": "", # 第一步详细信息(做了哪些处理总结下,例如在applogcat中找到Hubble关键字)
"CN": "" # 步骤名称(例如找到applogcat文件)
},
"2": {
"details": "", # 第二步详细信息
"CN": "" # 步骤名称
}
}
},
"feature_data": { # 调用相似问题识别能力,需先填好此变量,可能有多个识别到的故障及其特征
"": [] # key值代表发现的故障类型,value值是list of string,由识别到的特征字符串组成
}
}
# 将'05-12 14:51:55'的时间格式转换成时间2024-05-12 14:51:55
def log_str_to_datetime(self, time_str):
issue_time = datetime.strptime(time_str, '%m-%d %H:%M:%S')
current_year = datetime.now().year
issue_time_with_current_year = issue_time.replace(year=current_year)
return issue_time_with_current_year
def extract_datetime(self, string):
pattern = r"_(\d{8})_(\d{4})"
match = re.search(pattern, string)
if match:
return match.group(1) + "_" + match.group(2)
else:
return None
# 1整机低内存
def memAvailable(self, hilog):
for line in hilog:
monsc_re = re.compile(
r'(?P<timestamp>\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*curBufKB=(?P<curBufKB>\d+)').search(line)
if monsc_re:
timestamp = monsc_re.group("timestamp")
memAvailable_value = int(monsc_re.group('curBufKB'))
if int(memAvailable_value) < 819200:
return True, timestamp
return False, None
# 2杀后台
def am_kill(self, hilog):
esja_re = re.compile(
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:
if 'KillOneProc kill_reason=LowMemoryKill' in line:
match = esja_re.search(line)
if match:
timestamp = match.group("timestamp")
kill_proc_name = match.group("package")
return True, timestamp, kill_proc_name
return False, None, None
# 4整机温度过高
def temperature(self, hilog):
for line in hilog:
frame_re = re.compile(r'(?P<timestamp>\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*$$shell_frame$$ new temp: (?P<new_temp>\d+) old temp').search(line)
if frame_re:
timestamp = frame_re.group("timestamp")
frame_value = int(frame_re.group("new temp"))
if int(frame_value) > 43:
return True, timestamp
return False, None
# 5整机存储不足
def storage(self, hilog):
round_size = 0
free_size = 0
for line in hilog:
round_se = re.compile('roundSize=(.*),').search(line)
free_se = re.compile('freeSize=(.*)').search(line)
if round_se:
round_tuples_first = round_se.groups()
round_tuples = round_tuples_first[0].split(',')
round_value = int(round_tuples[0])
round_size = round_value
if free_se:
free_tuple = free_se.groups()
free_value = int(free_tuple[0])
free_size = free_value
if round_size and free_size:
tmp = free_size / round_size
if tmp <= 0.15:
return 1
return 0
# 6整机碎片化过高
def free_sec(self, hilog_kmsg):
for line in hilog_kmsg:
free_sec_re = re.compile('free_sec=(.*),reserved_sec').search(line)
free_re = re.compile(',Free =(.*)MB').search(line)
if free_sec_re and free_re:
free_sec_tuples = free_sec_re.groups()
free_sec_value = free_sec_tuples[0]
free_tuples = free_re.groups()
free_value = free_tuples[0]
res_value = 1 - (int(free_sec_value) * 2) / int(free_value)
if res_value > 0.8:
return 1
return 0
# 7网络不佳
def ps_slow(self, hilog):
num_chr = 0
timestamp = None
for line in hilog:
slow_re = re.compile(r'(?P<timestamp>\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*CHR name=(.*), type').search(line)
if slow_re:
timestamp = slow_re.group("timestamp")
slow_value = slow_re.group(2)
if slow_value == 'PS_SLOW_EVENT':
num_chr += 1
if num_chr >= 5 and timestamp:
return True,timestamp
return False,None
# 8电池电量过低
def power_low(self, hilog):
for line in hilog:
power_re = re.compile(r'(?P<timestamp>\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*powermgr/BatteryInfo: capacity=(.*), voltage').search(line)
if power_re:
timestamp = power_re.groups("timestamp")
power_value = power_re.group(2)
if int(power_value) < 10:
return True,timestamp
return False,None
# 9电池电压过低
def voltage(self, hilog):
for line in hilog:
voltage_re = re.compile(r'(?P<timestamp>\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*powermgr/BatteryInfo: (.*), voltage=(?P<voltage>\d+), temperature').search(line)
if voltage_re:
timestamp = voltage_re.group("timestamp")
voltage_value = voltage_re.group("voltage")
if int(voltage_value) < 3500000:
return True,timestamp
return False,None
# 10 整机CPU高负载
def cup_load(self, hilog):
for line in hilog:
cpu_re = re.compile( r'(?P<timestamp>\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*cpuLoadPercent:(?P<cpuLoadPercent>\d+), thermalLevel').search(line)
if cpu_re:
timestamp = cpu_re.group("timestamp")
cpu_value_str = cpu_re.group("cpuLoadPercent")
try:
cpu_value = int(cpu_value_str)
if cpu_value > 600000:
return True, timestamp
except ValueError:
# 处理无法转换为整数的情况
continue
return False, None
# 11 整机GPU高负载
def gpu_load(self, hilog):
for line in hilog:
gpu_re = re.compile(r'(?P<timestamp>\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*Get GpuTotalUsed value is (.*)').search(line)
if gpu_re:
timestamp = gpu_re.groups("timestamp")
gpu_value = gpu_re.group(1)
if int(gpu_value) > 600000:
return True,timestamp
return False,None
def plugin_preprocess(self, info_dict=None):
"""
预处理Hubble传入的info_dict,包含程序运行的基本信息
此函数的基础操作是获取下载日志列表地址,形成task_list任务分析列表,供Hubble调用
"""
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.plugin_result
1. uuid_log_path: Hubble单任务的顶级分析路径,包含生成的基本信息文件、结果文件和递归解压的日志文件夹
2. log_path: 递归解压的顶级日志文件夹,一般是uuid_log_path的子文件
3. param_dict: 脚本程序所需的参数信息,例如日志来源、领域等,
---------------------------------------------------
参数示例
BetaClub的param_dict示例:
param_dict = {"common_info":{'happen_time': '20230225182000', 'betano': '2103539765',
'version': 'FCO-AL00 3.1.0.115(SP2C00E110R2P3log)', 'product': 'FCO-AL00',
'logsource': 'BetaClub', 'beta_info': {'quesDetail': '图库退出卡顿。',
'faultNameLv1': '性能', 'faultNameLv2': '整机操作各种卡',
'activityName': '【旗舰手机PDU】【关键】FCO-AL00 Beta测试', 'faultCode': '1008_1002'},
'happen_time_unixtime': 1677320400000, 'label': '动效卡顿', 'l_pkgs': ['com.huawei.photos']}}
APR的param_dict示例:
param_dict = {'common_info': {'version': 'MNA-AL00 3.1.0.125(SP1DEMC735E126R2P2)',
'eventid': '901001002','errorcode2': '', 'errorcode4': '', 'logsource': 'AresOcean',
'errorcode3': '', 'product': 'MNA-AL00', 'datetime_str': '20230327112414'}}
建议通过字典的get方法进行取值,如可通过下述语句获取用户描述和产品名称:
brief = param_dict.get("common_info", {}).get("beta_info", {}).get("quesDetail", "")
product = param_dict.get("common_info", {}).get("product", "")
"""
self.logger.info("这是测试插件!uuid_log_path=[%s], log_path=[%s], param_dict=[%s]",
uuid_log_path, log_path, param_dict) # 此句为DemoPlugin.py独有,可删除
# 此代码块为文件路径的遍历方法,以hiapplogcat-log为例
kmsg_path = []
app_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))
level_one = "稳定与流畅" # 临时二级定界字符串
conclusion = '详情请看根因分析'
# 打开app日志
app_list = []
for app in app_path:
with open(app, "r", encoding="utf-8", errors='ignore') as app_reader:
app_line = app_reader.readlines()
app_list += app_line
kmsg_list = []
for kmsg in kmsg_path:
with open(kmsg, "r", encoding="utf-8", errors='ignore') as app_reader:
app_line = app_reader.readlines()
app_list += app_line
memAvailable_flag, nenAvailable_timestamp = self.memAvailable(app_list)
am_kill_flag, am_kill_timestamp, am_kill_name = self.am_kill(app_list)
temperature_flag,temperature_timestamp = self.temperature(app_list)
storage = self.storage(app_list)
free_sec = self.free_sec(kmsg_list)
ps_slow_flag,ps_slow_timestamp = self.ps_slow(app_list)
power_low_flag,power_low_timestamp = self.power_low(app_list)
voltage_flag,voltage_timestamp = self.voltage(app_list)
cup_load_flag,cpu_load_timestamp = self.cup_load(app_list)
gpu_load_flag,gpu_load_timestamp = self.gpu_load(app_list)
memAvailable_result = ['整机低内存(RAM)','1、清除后台应用:桌面手势导航底部上划进入多任务中心,清理后台多任务应用卡片(三键导航点击导航键的方块图标可以进入多任务界面)2、长按电源键关机重启']
am_kill_result = ['因低内存被查杀','1、清除后台应用:桌面手势导航底部上划进入多任务中心,清理后台多任务应用卡片(三键导航点击导航键的方块图标可以进入多任务界面)2、长按电源键关机重启']
temperature_result = ['整机温度过高', '建议参考手机/平板使用过程中设备发热相关知识排查']
storage_result = ['整机可用存储空间不足','建议参考“华为手机/平板内存占用多,提示内存不足如何处理? ”知识中场景一:存储剩余空间不足排查方案处理。']
free_sec_result = ['整机碎片化过高','1、建议引导用户进入设置>存储,进行整机清理加速,清理不需要的垃圾文件、联网缓存及不常用的应用;2、建议用户尝试夜间熄屏充电,持续此操作2-3晚上清理碎片化,促进系统优化']
ps_slow_result = ['整机网络不佳', '当前所处网络环境覆盖较弱或干扰较大,建议引导用户更换网络环境']
power_low_result = ['整机低电量', '建议保证在电量充足情况下继续使用体验']
voltage_result = ['整机低电压', '建议检查电池健康度并在电量充足情况下继续使用体验']
cup_load_result = ['整机CPU高负载','1、清除后台应用:桌面手势导航底部上划进入多任务中心,清理后台多任务应用卡片(三键导航点击导航键的方块图标可以进入多任务界面);2、引导用户排查是否存在高速下载、 数据传输、多悬浮窗口使用等场景,以上高负载场景会导致设备卡顿,建议用户避免长时间使用上述场景;3、长按电源键关机重启;']
gpu_load_result = ['整机GPU高负载','1、清除后台应用:桌面手势导航底部上划进入多任务中心,清理后台多任务应用卡片(三键导航点击导航键的方块图标可以进入多任务界面);2、引导用户排查是否存在高速下载、 数据传输、多悬浮窗口使用等场景,以上高负载场景会导致设备卡顿,建议用户避免长时间使用上述场景;3、长按电源键关机重启;']
con_res = ''
solu_res = ''
if memAvailable_flag:
con_res += nenAvailable_timestamp + '出现了' + memAvailable_result[0] + '<br>'
if memAvailable_result[1] not in solu_res:
solu_res += memAvailable_result[1] + '<br>'
if am_kill_flag:
con_res += am_kill_timestamp + am_kill_name + am_kill_result[0] + '<br>'
if am_kill_result[1] not in solu_res:
solu_res += am_kill_result[1] + '<br>'
if temperature_flag:
con_res += temperature_timestamp.strftime("%Y-%m-%d %H:%M:%S")+temperature_result[0] + '<br>'
if temperature_result[1] not in solu_res:
solu_res += temperature_result[1] + '<br>'
if storage:
con_res += storage_result[0] + '<br>'
if storage_result[1] not in solu_res:
solu_res += storage_result[1] + '<br>'
if free_sec:
con_res += free_sec_result[0] + '<br>'
if free_sec_result[1] not in solu_res:
solu_res += free_sec_result[1] + '<br>'
if ps_slow_flag:
con_res += ps_slow_timestamp+ps_slow_result[0] + '<br>'
if ps_slow_result[1] not in solu_res:
solu_res += ps_slow_result[1] + '<br>'
if power_low_flag:
con_res += power_low_timestamp + power_low_result[0] + '<br>'
if power_low_result[1] not in solu_res:
solu_res += power_low_result[1] + '<br>'
if voltage_flag:
con_res += voltage_timestamp + voltage_result[0] + '<br>'
if voltage_result[1] not in solu_res:
solu_res += voltage_result[1] + '<br>'
if cup_load_flag:
con_res += cpu_load_timestamp + cup_load_result[0] + '<br>'
if cup_load_result[1] not in solu_res:
solu_res += cup_load_result[1] + '<br>'
if gpu_load_flag:
con_res += gpu_load_timestamp + gpu_load_result[0] + '<br>'
if gpu_load_result[1] not in solu_res:
solu_res += gpu_load_result[1] + '<br>'
if not con_res:
self.plugin_result["conclusion"]['level_one'] = level_one # 一级定界,反映插件功能
self.plugin_result["conclusion"]['level_two'] = '整机卡顿' # 二级定界,根据插件得到的不同结论进行调整
self.plugin_result["conclusion"]['reason_info'] = '当前整机各项指标未诊断出异常' # 二级定界,根据插件得到的不同结论进行调整
self.plugin_result["conclusion"]['solution'] = '建议引导用户重新复现问题反馈日志' # 二级定界,根据插件得到的不同结论进行调整
return self.plugin_result # 如果想直接利用Hubble前端分析报告的展示项,请按定义填写好self.plugin_result变量
else:
self.plugin_result["conclusion"]['level_one'] = level_one # 一级定界,反映插件功能
self.plugin_result["conclusion"]['level_two'] = '整机卡顿' # 二级定界,根据插件得到的不同结论进行调整
self.plugin_result["conclusion"]['reason_info'] = con_res # 二级定界,根据插件得到的不同结论进行调整
self.plugin_result["conclusion"]['solution'] = solu_res # 二级定界,根据插件得到的不同结论进行调整
self.logger.info(self.plugin_result)
return self.plugin_result # 如果想直接利用Hubble前端分析报告的展示项,请按定义填写好self.plugin_result变量
def plugin_result_refinement(self, result_list=None):
"""
二次精细化加工产生的此脚本分析结果列表?
result_list中是self.plugin_result元素的列表集合
也即result_list = [self.plugin_result_1, self.plugin_result_2,...]
"""
self.logger.info("这是分析结果精加工函数")
return result_list # 如果是此分析脚本定制化的分析结果数据结构,需要和Hubble对齐前端取数逻辑才能将分析结论显示在分析报告内
帮我改一份完整的代码可以么,添加上多个返回值,让他可以把所有错误全部返回