一行命令生成日志异常分析报告:Python 生产可用实战(含源码)

你是不是也遇到过:

  • 线上出问题,日志一大堆,靠人肉 grep
  • 想统计“最常见异常 / 最频繁报错模块 / 报错时间分布”
  • 想把结果发给同事/领导,但复制粘贴太丑

这篇我给你一个生产可用的小工具:
✅ 支持大日志(流式读取)
✅ 自动抽取异常块(Java/Python 常见堆栈)
✅ 聚合 Top 异常、出现次数、首末出现时间
✅ 输出Markdown 报告(可直接贴到 CSDN/飞书/钉钉)
✅ 一行命令运行


1. 目标与效果

你运行:

python log_report.py --input app.log --out report.md

会生成一份report.md,包含:

  • 异常 Top N(按出现次数排序)
  • 每种异常的:次数、首次时间、最后时间、示例片段
  • 日志整体:时间范围、错误密度(每分钟 error 数)

2. 支持的日志格式(够用且好扩展)

  • 行首时间(常见格式之一即可)

    • 2026-01-09 01:47:12
    • 2026-01-09T01:47:12
  • 错误块识别:

    • Java:Exception/Error开头 + 多行at xxx(...)
    • Python:Traceback (most recent call last):+ 多行堆栈

没命中也没关系:工具仍会统计ERROR行并输出密度。


3. 核心思路(3 句话讲清)

  1. 流式读取,不用一次性把日志读进内存
  2. 遇到“异常起始标记”时,开始收集多行堆栈,直到块结束
  3. 对异常块做指纹(hash),聚合计数、时间范围、示例

4. 直接上代码(完整可运行)

文件名:log_report.py
Python 3.9+,无三方依赖

#!/usr/bin/env python3# -*- coding: utf-8 -*-importargparseimporthashlibimportrefromdataclassesimportdataclass,fieldfromdatetimeimportdatetimefromtypingimportDict,List,Optional,Tuple# --------- 时间解析(可扩展) ----------TS_PATTERNS=[# 2026-01-09 01:47:12re.compile(r"^(?P<ts>\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2})"),# 2026-01-09T01:47:12.123re.compile(r"^(?P<ts>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3})"),]defparse_ts(line:str)->Optional[datetime]:forpatinTS_PATTERNS:m=pat.search(line)ifnotm:continueraw=m.group("ts")forfmtin("%Y-%m-%d %H:%M:%S","%Y-%m-%dT%H:%M:%S","%Y-%m-%dT%H:%M:%S.%f"):try:returndatetime.strptime(raw,fmt)exceptValueError:passreturnNone# --------- 异常块识别 ----------JAVA_START=re.compile(r"(\bException\b|\bError\b|\bCaused by:)")JAVA_STACK=re.compile(r"^\s+at\s+\S+\(.*\)$")PY_START=re.compile(r"^Traceback \(most recent call last\):")PY_STACK=re.compile(r"^\s+File\s+\".*\", line \d+, in .+$")LEVEL_ERROR=re.compile(r"\bERROR\b|\bFATAL\b",re.IGNORECASE)defis_java_exception_start(line:str)->bool:# 常见:xxxException: msg / Caused by: xxxreturnbool(JAVA_START.search(line))defis_java_stack_line(line:str)->bool:returnbool(JAVA_STACK.match(line))defis_py_exception_start(line:str)->bool:returnbool(PY_START.match(line))defis_py_stack_line(line:str)->bool:returnbool(PY_STACK.match(line))deflooks_like_blank_or_new_entry(line:str)->bool:# 用“有时间戳”判断是否进入下一条日志returnparse_ts(line)isnotNone@dataclassclassExceptionAgg:count:int=0first_seen:Optional[datetime]=Nonelast_seen:Optional[datetime]=Nonesample:str=""@dataclassclassReport:total_lines:int=0error_lines:int=0start_time:Optional[datetime]=Noneend_time:Optional[datetime]=Noneper_minute_errors:Dict[str,int]=field(default_factory=dict)exceptions:Dict[str,ExceptionAgg]=field(default_factory=dict)deffingerprint_exception(block:str)->str:""" 对异常块做指纹:去掉明显变化的信息后 hash """# 去掉数字、耗时、id 等易变项(可按你的日志优化)normalized=re.sub(r"\d+","N",block)normalized=re.sub(r"0x[0-9a-fA-F]+","0xHEX",normalized)normalized=re.sub(r"\b[a-f0-9]{16,}\b","HEXSTR",normalized)# 长 hashh=hashlib.sha1(normalized.encode("utf-8",errors="ignore")).hexdigest()returnh[:12]defminute_key(ts:datetime)->str:returnts.strftime("%Y-%m-%d %H:%M")defupdate_time_range(rep:Report,ts:Optional[datetime])->None:iftsisNone:returnifrep.start_timeisNoneorts<rep.start_time:rep.start_time=tsifrep.end_timeisNoneorts>rep.end_time:rep.end_time=tsdefadd_error_minute(rep:Report,ts:Optional[datetime])->None:iftsisNone:returnk=minute_key(ts)rep.per_minute_errors[k]=rep.per_minute_errors.get(k,0)+1defcommit_exception(rep:Report,ts:Optional[datetime],block:str)->None:fp=fingerprint_exception(block)agg=rep.exceptions.get(fp)ifaggisNone:agg=ExceptionAgg(count=0,first_seen=ts,last_seen=ts,sample=block[:1200])rep.exceptions[fp]=agg agg.count+=1iftsisnotNone:ifagg.first_seenisNoneorts<agg.first_seen:agg.first_seen=tsifagg.last_seenisNoneorts>agg.last_seen:agg.last_seen=tsifnotagg.sample:agg.sample=block[:1200]defparse_log(path:str)->Report:rep=Report()in_exc=Falseexc_lines:List[str]=[]exc_ts:Optional[datetime]=Noneexc_type:Optional[str]=None# "java" / "py"defflush_exc():nonlocalin_exc,exc_lines,exc_ts,exc_typeifin_excandexc_lines:commit_exception(rep,exc_ts,"\n".join(exc_lines))in_exc=Falseexc_lines=[]exc_ts=Noneexc_type=Nonewithopen(path,"r",encoding="utf-8",errors="ignore")asf:forlineinf:rep.total_lines+=1line=line.rstrip("\n")ts=parse_ts(line)update_time_range(rep,ts)# 错误行统计(即便没形成异常块)ifLEVEL_ERROR.search(line):rep.error_lines+=1add_error_minute(rep,ts)# 异常块状态机ifnotin_exc:ifis_py_exception_start(line):in_exc=Trueexc_type="py"exc_ts=ts exc_lines=[line]continueifis_java_exception_start(line):in_exc=Trueexc_type="java"exc_ts=ts exc_lines=[line]continueelse:# 已在异常块中:判断是否继续收集ifexc_type=="java":# Java 堆栈行 or 继续的 caused by 等ifis_java_stack_line(line)oris_java_exception_start(line)orline.strip().startswith("..."):exc_lines.append(line)continue# 新日志条目出现 → 结束异常块iflooks_like_blank_or_new_entry(line):flush_exc()# 这行可能是新异常起点(递归判断)ifis_py_exception_start(line):in_exc=Trueexc_type="py"exc_ts=parse_ts(line)exc_lines=[line]elifis_java_exception_start(line):in_exc=Trueexc_type="java"exc_ts=parse_ts(line)exc_lines=[line]continue# 其他行:也可能是异常信息补充,保守收集ifline.strip():exc_lines.append(line)continue# 空行:先收集exc_lines.append(line)continueifexc_type=="py":ifis_py_stack_line(line)orline.strip().startswith(("Traceback","During handling of the above exception")):exc_lines.append(line)continue# Python 异常块通常以 “Exception: msg” 结束行出现ifline.strip()andnotlooks_like_blank_or_new_entry(line):exc_lines.append(line)# 继续收集一两行也无妨continueiflooks_like_blank_or_new_entry(line):flush_exc()ifis_py_exception_start(line):in_exc=Trueexc_type="py"exc_ts=parse_ts(line)exc_lines=[line]elifis_java_exception_start(line):in_exc=Trueexc_type="java"exc_ts=parse_ts(line)exc_lines=[line]continueexc_lines.append(line)continue# 文件结束,别忘了 flushifin_exc:flush_exc()returnrepdefrender_md(rep:Report,top_n:int=10)->str:lines:List[str]=[]lines.append("# 日志异常分析报告\n")lines.append("## 概览\n")lines.append(f"- 总行数:**{rep.total_lines}**")lines.append(f"- ERROR/FATAL 行数:**{rep.error_lines}**")ifrep.start_timeandrep.end_time:lines.append(f"- 时间范围:**{rep.start_time}** ~ **{rep.end_time}**")lines.append("")# 错误密度 Topifrep.per_minute_errors:lines.append("## 错误密度(每分钟 ERROR Top 10)\n")top_minutes=sorted(rep.per_minute_errors.items(),key=lambdax:x[1],reverse=True)[:10]lines.append("| 分钟 | ERROR 数 |")lines.append("|---|---:|")fork,vintop_minutes:lines.append(f"|{k}|{v}|")lines.append("")# 异常 Topifrep.exceptions:lines.append(f"## 异常聚合 Top{top_n}\n")items=sorted(rep.exceptions.items(),key=lambdakv:kv[1].count,reverse=True)[:top_n]lines.append("| 指纹 | 次数 | 首次出现 | 最后出现 |")lines.append("|---|---:|---|---|")forfp,agginitems:lines.append(f"| `{fp}` |{agg.count}|{agg.first_seenor'-'}|{agg.last_seenor'-'}|")lines.append("")# 详情lines.append("## 异常详情(示例片段)\n")forfp,agginitems:lines.append(f"### `{fp}`({agg.count}次)")lines.append(f"- 首次:{agg.first_seenor'-'}")lines.append(f"- 最后:{agg.last_seenor'-'}\n")lines.append("```text")lines.append(agg.sample.rstrip())lines.append("```\n")else:lines.append("## 异常聚合\n")lines.append("> 未识别到典型 Java/Python 堆栈异常块(可能是日志格式不同)。你仍可以从“错误密度”定位高发时间段。\n")return"\n".join(lines)defmain():ap=argparse.ArgumentParser(description="Generate log exception analysis report (Markdown).")ap.add_argument("--input","-i",required=True,help="log file path")ap.add_argument("--out","-o",default="report.md",help="output markdown report path")ap.add_argument("--top","-t",type=int,default=10,help="top N exceptions")args=ap.parse_args()rep=parse_log(args.input)md=render_md(rep,top_n=args.top)withopen(args.out,"w",encoding="utf-8")asf:f.write(md)print(f"[OK] Report generated:{args.out}")ifrep.start_timeandrep.end_time:print(f"[INFO] Time range:{rep.start_time}~{rep.end_time}")print(f"[INFO] Total lines:{rep.total_lines}, ERROR lines:{rep.error_lines}, exceptions:{len(rep.exceptions)}")if__name__=="__main__":main()

5. 快速试跑(给你一个最小示例)

新建一个app.log,写入:

2026-01-09 01:47:12 ERROR c.xxx.Service - boom java.lang.NullPointerException: x is null at c.xxx.Service.run(Service.java:10) at c.xxx.App.main(App.java:5) 2026-01-09 01:47:13 INFO ok 2026-01-09 01:47:20 ERROR c.xxx.Service - boom again java.lang.NullPointerException: x is null at c.xxx.Service.run(Service.java:10) at c.xxx.App.main(App.java:5)

运行:

python log_report.py -i app.log -o report.md

打开report.md就能看到聚合结果(次数=2)。


6. 生产使用建议(别跳过)

✅ 1)放到服务器定时跑

0*/2 * * * /usr/bin/python3 /opt/tools/log_report.py -i /var/log/app/app.log -o /var/log/app/report.md

✅ 2)结合告警,把 report.md 发到飞书/钉钉

下一篇我会写一个“报告自动推送器”,异常密度超过阈值才推送。

✅ 3)扩展你的日志格式

只需要改两处:

  • TS_PATTERNS增加时间格式
  • 增加一种异常起始识别

👉 后续工具会持续补充进《程序员自动化工具箱》,喜欢的朋友别忘了点个关注订阅此专栏。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1134005.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

智能翻译系统灾备方案:CSANMT数据备份策略

智能翻译系统灾备方案&#xff1a;CSANMT数据备份策略 &#x1f310; AI 智能中英翻译服务 (WebUI API) 项目背景与灾备需求 随着全球化进程加速&#xff0c;AI 驱动的智能翻译服务在企业出海、跨国协作和内容本地化中扮演着关键角色。本项目基于 ModelScope 平台提供的 CSANM…

Cowabunga:重新定义iOS个性化体验的终极工具

Cowabunga&#xff1a;重新定义iOS个性化体验的终极工具 【免费下载链接】Cowabunga iOS 14.0-15.7.1 & 16.0-16.1.2 MacDirtyCow ToolBox 项目地址: https://gitcode.com/gh_mirrors/co/Cowabunga 在追求个性化的时代&#xff0c;iPhone用户渴望摆脱千篇一律的系统…

零售价签识别:门店巡检机器人搭载OCR模块实现

零售价签识别&#xff1a;门店巡检机器人搭载OCR模块实现 一、OCR文字识别在零售场景中的核心价值 在现代智能零售体系中&#xff0c;商品价格信息的实时性与准确性直接影响消费者的购买决策和门店的运营效率。传统的人工巡检方式不仅耗时耗力&#xff0c;还容易因人为疏忽导致…

安全编码:工程师如何构建可测试的防护体系

面向对象&#xff1a;软件测试工程师一、可测试性设计的核心原则模块化安全控制点采用安全中间件架构&#xff08;如Auth0、Keycloak&#xff09;隔离认证授权逻辑示例&#xff1a;将加密模块封装为独立服务&#xff0c;支持测试桩注入优势&#xff1a;测试人员可单独验证加密强…

SOMEIP开发效率提升秘籍

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 构建一个SOMEIP应用&#xff0c;重点展示快速开发流程和效率优势。点击项目生成按钮&#xff0c;等待项目生成完整后预览效果 在汽车电子和智能驾驶领域&#xff0c;SOMEIP&#x…

PBC 患者新选择:司拉德帕的临床应用、安全性与可及性解析

对于原发性胆汁性胆管炎&#xff08;PBC&#xff09;患者而言&#xff0c;选择一款疗效确切、安全性高且可及的治疗药物&#xff0c;是控制病情、提升生活质量的关键。司拉德帕&#xff08;Seladelpar&#xff09;作为全球最新获批的 PBC 治疗药物&#xff0c;凭借其明确的适应…

零基础入门:用MNIST学习深度学习

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 创建一个面向初学者的MNIST数字识别教程。从Python环境配置开始&#xff0c;逐步讲解数据加载、简单的神经网络构建&#xff08;如全连接网络&#xff09;、训练和评估。要求代码注…

智能看图卸载全攻略:从手动到自动

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 开发一个图形界面工具&#xff0c;专门用于卸载智能看图应用。要求&#xff1a;1. 可视化界面显示已安装的智能看图版本&#xff1b;2. 提供一键卸载按钮&#xff1b;3. 包含强制卸…

B站直播自动化实战手册:从零打造智能互动直播间

B站直播自动化实战手册&#xff1a;从零打造智能互动直播间 【免费下载链接】Bilibili-MagicalDanmaku 【神奇弹幕】哔哩哔哩直播万能场控机器人&#xff0c;弹幕姬答谢姬回复姬点歌姬各种小骚操作&#xff0c;目前唯一可编程机器人 项目地址: https://gitcode.com/gh_mirror…

Open-SAE-J1939 完整开发指南:快速掌握工业车辆通信核心技术

Open-SAE-J1939 完整开发指南&#xff1a;快速掌握工业车辆通信核心技术 【免费下载链接】Open-SAE-J1939 SAE J1939 protocol free to use for embedded systems or PC with CAN-bus 项目地址: https://gitcode.com/gh_mirrors/op/Open-SAE-J1939 想要在工业车辆通信领…

dify工作流集成OCR:低代码平台连接CRNN镜像教程

dify工作流集成OCR&#xff1a;低代码平台连接CRNN镜像教程 &#x1f4cc; 背景与需求&#xff1a;为什么需要在dify中集成OCR&#xff1f; 随着企业数字化进程的加速&#xff0c;非结构化图像数据&#xff08;如发票、合同、证件、路牌等&#xff09;中的文字提取成为自动化流…

从平面到立体:钣金设计中的折叠智慧

在现代工业制造领域&#xff0c;从精密的电子产品外壳到稳固的机柜、汽车车身部件&#xff0c;钣金件的身影无处不在。这些看似由多个复杂曲面构成的立体产品&#xff0c;其诞生之初&#xff0c;往往只是一张平整的二维金属板材。实现这一神奇转变的核心&#xff0c;便是专业的…

3步掌握视频转GIF:从菜鸟到高手的完整教程

3步掌握视频转GIF&#xff1a;从菜鸟到高手的完整教程 【免费下载链接】gifski GIF encoder based on libimagequant (pngquant). Squeezes maximum possible quality from the awful GIF format. 项目地址: https://gitcode.com/gh_mirrors/gif/gifski 想要将精彩的视频…

Roblox帧率解锁终极指南:彻底释放游戏性能潜力

Roblox帧率解锁终极指南&#xff1a;彻底释放游戏性能潜力 【免费下载链接】rbxfpsunlocker FPS Unlocker for Roblox 项目地址: https://gitcode.com/gh_mirrors/rb/rbxfpsunlocker &#x1f3ae; 还在为Roblox游戏卡顿而烦恼吗&#xff1f;Roblox FPS Unlocker作为一款…

免费OpenAI API密钥完整获取与使用指南

免费OpenAI API密钥完整获取与使用指南 【免费下载链接】FREE-openai-api-keys collection for free openai keys to use in your projects 项目地址: https://gitcode.com/gh_mirrors/fr/FREE-openai-api-keys 核心价值定位 本指南为您提供完整的免费OpenAI API密钥解…

Java调用OCR服务:Spring Boot集成REST API实战

Java调用OCR服务&#xff1a;Spring Boot集成REST API实战 &#x1f4d6; 技术背景与应用场景 在数字化转型加速的今天&#xff0c;OCR&#xff08;Optical Character Recognition&#xff0c;光学字符识别&#xff09; 已成为企业自动化流程中的关键技术之一。无论是发票识别、…

完整指南:Renderdoc网格数据快速导出FBX格式的终极方案

完整指南&#xff1a;Renderdoc网格数据快速导出FBX格式的终极方案 【免费下载链接】RenderdocResourceExporter The main feature is to export mesh.Because I dont want to switch between other software to do this.So I wrote this thing. 项目地址: https://gitcode.c…

Docker Compose入门:从零开始编排你的第一个应用

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 请创建一个最基础的Docker Compose教程项目&#xff0c;包含&#xff1a;1) 一个简单的Python Flask应用&#xff1b;2) 一个Redis服务。要求&#xff1a;提供逐步的说明文档&…

Chrome扩展批量下载网页资源终极指南:一键解决资源收集难题

Chrome扩展批量下载网页资源终极指南&#xff1a;一键解决资源收集难题 【免费下载链接】ResourcesSaverExt Chrome Extension for one click downloading all resources files and keeping folder structures. 项目地址: https://gitcode.com/gh_mirrors/re/ResourcesSaverE…

Sony-PMCA-RE:解锁索尼相机隐藏潜能的完整指南

Sony-PMCA-RE&#xff1a;解锁索尼相机隐藏潜能的完整指南 【免费下载链接】Sony-PMCA-RE Reverse Engineering Sony Digital Cameras 项目地址: https://gitcode.com/gh_mirrors/so/Sony-PMCA-RE 你是否曾经对索尼相机那些被厂商锁定的功能感到好奇&#xff1f;是否想过…