import re import codecs def extract_errors_and_warnings(input_log_path, output_error_path, warning_stats_path="warning_statistics.txt"): """ 从日志文件中提取 WARNING 和 ERROR 及其 Traceback 堆栈信息,保存到新文件 支持多种编码格式的日志文件(UTF-8, GBK, ASCII等) 同时统计WARNING信息并输出到单独文件 """ # 正则匹配日志行开头(时间戳格式) log_pattern = re.compile(r'^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3})') # 尝试多种编码格式读取文件 encodings = ['utf-8', 'gbk', 'gb2312', 'ascii'] lines = [] for encoding in encodings: try: with open(input_log_path, 'r', encoding=encoding) as f: lines = f.readlines() print(f"✅ 成功使用 {encoding} 编码读取文件") break except UnicodeDecodeError: continue # 如果所有编码都失败,则使用二进制模式读取并尝试解码 if not lines: try: with open(input_log_path, 'rb') as f: content = f.read() # 尝试解码,忽略错误 lines = content.decode('utf-8', errors='ignore').splitlines(True) print("⚠️ 使用二进制模式读取文件,可能有字符丢失") except Exception as e: print(f"❌ 无法读取文件: {e}") return error_lines = [] i = 0 while i < len(lines): line = lines[i] # 检查是否是新的日志行(以时间戳开头) is_new_log = log_pattern.match(line) if is_new_log: # 判断是否为 WARNING 或 ERROR if ' - WARNING - ' in line or ' - ERROR - ' in line: error_lines.append(line.rstrip()) # 如果是 ERROR,捕获后续的 Traceback 信息 if ' - ERROR - ' in line: i += 1 # 继续读取后续行,直到遇到下一个时间戳行或文件结束 while i < len(lines): next_line = lines[i] # 检查是否是新的日志行 if log_pattern.match(next_line): # 回退索引,让外层循环处理这一行 i -= 1 break else: # 添加非空的后续行 if next_line.strip(): error_lines.append(next_line.rstrip()) i += 1 # 如果是DEBUG/INFO行,检查是否包含Traceback elif ' - DEBUG - ' in line and i + 1 < len(lines) and 'Traceback' in lines[i + 1]: # 这是一个包含Traceback的DEBUG信息,也提取 error_lines.append(line.rstrip()) i += 1 # 继续读取后续行,直到遇到下一个时间戳行或文件结束 while i < len(lines): next_line = lines[i] # 检查是否是新的日志行 if log_pattern.match(next_line): # 回退索引,让外层循环处理这一行 i -= 1 break else: # 添加非空的后续行 if next_line.strip(): error_lines.append(next_line.rstrip()) i += 1 else: # 不是新日志行开头,可能是多行信息的一部分 # 只有当前面一行是错误信息时才添加 if error_lines and not log_pattern.match(error_lines[-1]): if line.strip(): error_lines.append(line.rstrip()) i += 1 # 写入输出文件 with open(output_error_path, 'w', encoding='utf-8') as f: for err_line in error_lines: f.write(err_line + '\n') # 统计WARNING信息 warning_dict = {} for line in error_lines: if ' - WARNING - ' in line: # 提取WARNING后的内容作为键 warning_content = line.split(' - WARNING - ', 1)[1] if warning_content in warning_dict: warning_dict[warning_content] += 1 else: warning_dict[warning_content] = 1 # 写入统计结果到文件 with open(warning_stats_path, 'w', encoding='utf-8') as f: f.write("WARNING统计结果:\n") f.write(f"共找到 {len(warning_dict)} 种不同的WARNING信息\n\n") for warning_content, count in warning_dict.items(): f.write(f"{count}次: {warning_content}\n") print(f"✅ 提取完成!共找到 {len(error_lines)} 行错误/警告信息。") print(f"📁 已保存到: {output_error_path}") print(f"📊 WARNING统计已保存到: {warning_stats_path}") # ============ 使用示例 ============ if __name__ == "__main__": input_file = "bcl_calculator.log" # 替换为你的日志文件路径 output_file = "error_report.txt" # 输出的错误报告文件 warning_stats_file = "warning_statistics.txt" # WARNING统计结果文件 extract_errors_and_warnings(input_file, output_file, warning_stats_file)