python——筛选crash_log

python——筛选crash_log

【内容】筛选出crash_log中的包名,错误类型,详情。正则包含不了的情况,脚本会单独放到一个txt文件里,命名为“正则未匹配.txt”

【log匹配规则】在保持的字符串中, 先以空格分割,寻找到以"Exception:“结尾,但不是"java.lang.RuntimeException:”,获取到字符串,则去掉结尾":".

Count_crash_log.py的内容:
注释都写在里面了

import os
import re
import csv
import pandas
import easygui
 
 
def write_txt(file, s):
    with open(file, 'a', encoding='utf-8') as f:
        return f.write(s + '\n\n\n\n\n')
 
 
def read_txt(file):
    try:
        with open(file, 'r', encoding='utf-8') as f:
            return f.read()
    except:
        return ''
 
 
def write_csv_with_name(filepath, lines: list, columns: list):
    if os.path.exists(filepath):
        with open(filepath, 'a', newline='', encoding='utf-8-sig') as f:
            csv_file = csv.writer(f, delimiter=',')
            csv_file.writerows(lines)
    else:
        with open(filepath, 'a', newline='', encoding='utf-8-sig') as f:
            csv_file = csv.writer(f, delimiter=',')
            csv_file.writerow(columns)
            csv_file.writerows(lines)
 
 
# 报错详情,解析文本文件
def parse(file):
    txt = read_txt(file)
    # 将其分割成多个部分,每个部分以字符串"FATAL EXCEPTION"作为分隔符
    parts = re.split('(?=FATAL EXCEPTION)', txt)
    # 定义字典对象records
    records = {}
 
    for part in parts:
        if 'FATAL EXCEPTION' not in part:
            continue
        # 剔除“crash_dump helper failed to exec, or was killed”
        part = re.sub('.*crash_dump helper failed to exec, or was killed.*\n', '', part)
        # 分割出包名
        # 从字符串 part 中提取出以 AndroidRuntime: Process: 开头,以 , 结尾的部分,然后将其赋值给变量 name。
        name = re.findall('AndroidRuntime: Process: (.*?),', part)
        if name:
            name = name[0]
        else:
            continue
 
        # 报错类型匹配规则,总体
        error = re.findall('''RuntimeException:.*?: (.*?Exception)''', part)
        # 如果不符合总体的匹配规则,特殊
        if not error:
            error = re.findall(r'RuntimeException:.*?: (.*?\$k):', part)
        if not error:
            error = re.findall(r'RuntimeException:.*?: (.*?\$k):', part)
        if not error:
            error = re.findall(r'''AndroidRuntime: (.*?Exception)''', part)
        if not error:
            error = re.findall(r'''AndroidRuntime: (.*?OutOfMemoryError)''', part)
 
        if error:
            error = error[0]
        else:
            print(part + '\n\n\n\n')
            write_txt('正则未匹配.txt', part)
            continue
 
        # 使用正则表达式来匹配字符串中的特定格式,并将其替换为空字符串。
        # 然后,将处理后的字符串按行分割,并去掉前三行,最后将剩余的行重新组合成一个字符串。
        detail = re.sub('(.*?: [\t]{0,1})', '', part).split('\n')[3:]
        detail = '\n'.join(detail)
        # 如果结果为空,则跳过该部分
        if detail == []:
            continue
 
        # 使用了一个字典变量 records。如果记录中已经存在该错误类型,则将其出现次数加 1;否则,将该错误类型添加到记录中,并将其出现次数设置为 1if (name, error, detail) in records.keys():
            records[name, error, detail] += 1
        else:
            records[name, error, detail] = 1
 
    # 最终返回一个字典对象records,其中包含每个不包含"FATAL EXCEPTION"的部分
    return records
 
 
while True:
    # 显示一个简单的消息框,让用户选择目录,并将用户选择的目录路径保存到变量 path 中
    path = easygui.diropenbox('选择日志所在的主目录', '选择日志所在的主目录')
    # 判断用户选择的路径是否为目录
    if os.path.isdir(path):
        break
    else:
        easygui.msgbox('必须选择目录,不能是文件', '必须选择目录,不能是文件')
        continue
 
all = {}
# 使用一个循环遍历前面多个日志文件的解析结果,并将它们合并到一个字典 all 中
# os.walk遍历所有子目录和文件
for paths, dirnames, filenames in os.walk(path):
    for name in filenames:
        log_path = os.path.join(paths, name)
        # 不包含 'crash_log',则跳过
        if 'crash_log' not in name:
            print(log_path, '不是日志,跳过')
            continue
 
        # (name, error, detail)当做字典的键,直接判断是不是存在,存在就累加,不存在就创建
        print(log_path, '开始处理')
        result = parse(log_path)
        for key in result.keys():
            if key in all.keys():
                all[key] += result[key]
            else:
                all[key] = result[key]
# 将字典转换成一个列表,其中每个元素都是一个列表
# all.keys()获取字典中所有的键
# *key将键拆分成若干个参数
all = [[*key, all[key]] for key in all.keys()]
 
data = pandas.DataFrame(all, columns=['问题应用', '报错类型', '报错详情', '报错类型数量'])
data.sort_values(by=['问题应用', '报错类型', '报错次数'], inplace=True, ascending=False)
 
# 报错类型加上序号
numbers = []
pre = None
count = 1
for leixing in data['报错类型']:
    """ 对 data['报错类型'] 中的每个元素进行计数,并将计数结果存储在 numbers 列表中
       使用变量 pre 记录上一个处理的元素,初始值为 None。
       然后遍历 data['报错类型'],如果当前元素和上一个元素不同,则将上一个元素的计数结果添加到 numbers 列表中,并将计数器 count 重置为 1。
       如果当前元素和上一个元素相同,则将计数器 count 加一。最后将最后一个元素的计数结果添加到 numbers 列表中。
    """
    # 统计中报错类型中连续出现相同词的数量,将结果存储在列表 numbers 中
    if pre == None:
        pre = leixing
        numbers.append(count)
        count += 1
        continue
    if leixing == pre:
        numbers.append(count)
        count += 1
        continue
    if leixing != pre:
        count = 1
        numbers.append(count)
        count += 1
        pre = leixing
        continue
 
data.insert(2, column='报错序号', value=numbers)
 
counts = data.groupby('问题应用').sum('报错类型数量')
new_colunm = []
for index, row in data.iterrows():
    new_colunm.append(counts['报错类型数量'][row['问题应用']])
 
data.insert(1, column='报错次数', value=new_colunm)
data.insert(6, column='审核分析', value=None)
data.insert(7, column='解决方案', value=None)
 
data.to_excel('crash_log_result.xlsx', index=None, columns=['问题应用', '报错序号', '报错类型', '报错次数', '报错详情', '审核分析', '解决方案'])

正则是找规律,提供的日志里面有现有正则包含不了的情况我就增加正则,没出现过的我也猜不到,也没法处理
没法处理的我就单独放到一个txt文件里,命名为“正则未匹配.txt”

后续需要加新的规则的话,请自行在py脚本中增加

运行时间会根据文件大小增加
(以防脚本在新电脑里没配有环境,可以把脚本打包成应用)

【用法】运行Count_crash_log.exe
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值