Python日志处理求助:提取多线异常去重写入exceptions.txt
问题与解决方案
需求概述
从同目录下的logs.txt中提取单行/多行异常,按指定格式写入exceptions.txt,要求:
- 输出格式:
错误编号 异常类型:XXX 时间戳:XXX 详情:XXX - 避免重复记录
- 咨询是否需要用word embeddings实现去重,以及整体实现思路
现有代码问题分析
1. logging模块代码无输出原因
- Handler配置冲突:
logging.basicConfig已默认添加StreamHandler,自定义的MultiStreamHandler直接操作文件流,未正确绑定日志格式,且可能未触发flush导致内容未写入文件 - 路径占位符未替换:代码中
'path\\exceptions.txt'和r'path\\logs.txt'是占位符,未替换为实际路径(同目录直接写'logs.txt'即可) - 解析逻辑缺陷:将日志行转小写后判断
startswith('e '),但未提取时间戳、异常类型,仅输出错误行,不符合指定格式;多行异常拼接逻辑混乱,无法正确识别完整异常栈
2. loguru模块代码仅处理单行异常原因
- 正则表达式缺陷:原正则的非贪婪匹配会提前截断多行异常内容,无法捕获完整栈轨迹
- 多行异常处理逻辑错误:识别到多行异常开头后,后续
match已指向新错误条目,无法拼接当前异常的后续栈行 - 未提取时间戳:完全忽略了时间戳的捕获和输出
修复方案(推荐自定义解析逻辑,无需依赖日志框架)
以下是直接实现需求的代码,灵活可控:
import re def extract_exceptions(log_path, output_path): # 正则匹配:捕获时间戳、异常类型、异常详情(支持多行栈轨迹) # 适配日志格式:[时间戳] ... E [异常类型]: [详情],后续栈行以"at "开头 pattern = r"(\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}) .*? E (.*?): (.*?)(?=\n\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}.*? E |\Z)" with open(log_path, 'r', encoding='utf-8', errors='ignore') as f: logs_content = f.read() # 提取所有异常,DOTALL让.匹配换行符 matches = re.finditer(pattern, logs_content, flags=re.DOTALL) # 去重:用异常类型+标准化详情作为唯一标识 seen_exceptions = set() result = [] error_num = 1 for match in matches: timestamp = match.group(1).strip() exception_type = match.group(2).strip() # 清理详情格式:合并多余空格,还原栈行换行 details = re.sub(r'\s+', ' ', match.group(3).strip()).replace(' at ', '\nat ') unique_key = f"{exception_type}|{details}" if unique_key not in seen_exceptions: seen_exceptions.add(unique_key) # 按指定格式拼接 entry = ( f"错误编号 {error_num} 异常类型:{exception_type}\n" f"时间戳:{timestamp}\n" f"详情:{details}\n" "------------------------\n" # 异常分隔线,可移除 ) result.append(entry) error_num += 1 # 写入目标文件 with open(output_path, 'w', encoding='utf-8') as f: f.writelines(result) print(f"提取完成,共写入{len(result)}条唯一异常") # 调用(同目录直接用文件名) extract_exceptions("logs.txt", "exceptions.txt")
代码说明
- 正则优化:通过正向预查匹配下一个异常开头或文件结尾,确保捕获完整多行异常栈
- 去重逻辑:用
异常类型+标准化详情作为唯一键,避免完全重复的异常 - 格式输出:严格贴合需求格式,添加分隔线提升可读性
去重方案选择:是否需要Word Embeddings?
- 无需使用的场景:如果日志中的重复异常是完全重复(相同异常类型、栈轨迹、错误信息),用上述字符串唯一键即可,简单高效
- 需要使用的场景:如果存在相似但不完全重复的异常(如
NullPointerException但触发参数不同),可引入Word Embeddings:- 用
sentence-transformers生成异常详情的向量 - 计算余弦相似度,设置阈值(如0.8)判断相似性
- 合并或标记相似异常
- 用
示例(相似异常去重):
from sentence_transformers import SentenceTransformer, util model = SentenceTransformer('all-MiniLM-L6-v2') # 假设已提取所有异常为列表:exceptions = [{"type": "...", "details": "...", ...}, ...] embeddings = model.encode([e["details"] for e in exceptions], convert_to_tensor=True) seen_indices = set() unique_exceptions = [] for i in range(len(exceptions)): if i in seen_indices: continue # 计算当前异常与其他异常的相似度 cos_scores = util.cos_sim(embeddings[i], embeddings)[0] # 筛选相似度>0.8的异常 similar_indices = [j for j, score in enumerate(cos_scores) if score > 0.8 and j != i] seen_indices.update(similar_indices) unique_exceptions.append(exceptions[i])
整体实现思路
- 日志解析:根据日志格式编写正则,精准捕获时间戳、异常类型、完整异常详情(含多行栈)
- 去重处理:
- 优先用字符串匹配处理完全重复异常
- 需处理相似异常时,再引入Word Embeddings或文本相似度计算
- 格式输出:按需求整理信息,写入目标文件
- 验证优化:测试单行、多行、重复异常场景,调整正则或去重逻辑
内容的提问来源于stack exchange,提问作者anything
相关产品推荐
相关产品推荐

