You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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:
    1. 用sentence-transformers生成异常详情的向量
    2. 计算余弦相似度,设置阈值(如0.8)判断相似性
    3. 合并或标记相似异常

示例(相似异常去重):

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])

整体实现思路

  1. 日志解析:根据日志格式编写正则,精准捕获时间戳、异常类型、完整异常详情(含多行栈)
  2. 去重处理:
    • 优先用字符串匹配处理完全重复异常
    • 需处理相似异常时,再引入Word Embeddings或文本相似度计算
  3. 格式输出:按需求整理信息,写入目标文件
  4. 验证优化:测试单行、多行、重复异常场景,调整正则或去重逻辑

内容的提问来源于stack exchange,提问作者anything

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 03:37:29