当前位置:首页>python>《Python 从入门到精通》109|日志处理实战:从一堆文本中提取有用信息

《Python 从入门到精通》109|日志处理实战:从一堆文本中提取有用信息

  • 2026-10-11 06:37:32
《Python 从入门到精通》109|日志处理实战:从一堆文本中提取有用信息

前面几章,我们已经把一条很重要的线慢慢串起来了。

会读 JSON 了。 会用正则提取关键信息了。 会处理时间了。 会排序、去重、统计了。 还会把脏数据清洗得更整洁了。

到这里,你会发现,很多真实问题已经不再是学不会语法,而是拿到一堆原始文本之后,不知道怎么下手。

而日志,恰好就是这种问题里最典型的一种。

你做网站,会有访问日志。 你写后端,会有运行日志。 你做自动化脚本,会有执行日志。 你处理服务器问题,会看到报错日志。 你做数据清洗,也可能会给自己留一份处理日志。

日志表面上看,就是一行一行普通文本。 可真正做项目的人都知道,日志里藏着大量有价值的信息。

哪一刻出了错。 哪种错误出现最多。 哪个接口最慢。 哪个用户操作异常。 某个任务到底有没有执行成功。 程序卡住之前,最后一步做了什么。

所以这一章很重要。

它教的不是单纯读文本文件。 而是更贴近真实工作的能力:

怎么从杂乱日志里,把真正有用的信息提取出来。


一、什么是日志,为什么程序离不开它

先别把日志想得特别玄。

你可以把日志理解成: 程序在运行过程中留下的文字记录。

就像一个人做事会写工作记录一样,程序也需要记录自己做了什么、什么时候做的、结果怎么样。

例如下面这种内容,就是很常见的日志风格:

2026-03-2909:00:01 INFO 用户登录成功 user_id=10012026-03-2909:01:15 WARNING 请求耗时过长 api=/order/list2026-03-2909:02:30 ERROR 数据库连接失败 db=mysql2026-03-2909:03:20 INFO 定时任务执行完成 task=backup

你一眼就能看出,这里面至少有这些信息:

时间 日志级别 事件描述 补充字段

这就是日志的价值。

它不是写给用户看的。 它主要是写给开发者、运维人员、排查问题的人看的。

程序平时运行得好好的,你可能不觉得它有多重要。 可一旦系统出问题,日志往往是第一现场。

所以很多时候,日志不是可有可无的附加品。 它是程序排查问题、分析行为、定位异常的重要依据。


二、为什么日志处理会成为一种实战能力

很多新手第一次接触日志,会觉得这不就是读文件吗。

其实远不止。

因为日志最大的特点就是:

量大 杂乱 重复 信息藏在文本里 并且不一定完全规整

比如你可能拿到一个几万行的日志文件。 如果靠肉眼去找错误,很快就会看花。

再比如,你想知道昨天一共报了多少次错,哪类错误最多,最早的一次错误出现在几点,哪些请求超时最严重。 这些事情用肉眼几乎没法高效完成。

所以日志处理真正考验的,不是会不会打开文件。 而是你能不能把文本里的信息批量提取、筛选、统计、整理出来。

说白了,日志处理就是数据处理能力的一次综合实战。

前面学的字符串、正则、时间、统计、清洗,在这里会同时派上用场。


三、先看一个最常见的日志长什么样

虽然不同项目日志格式会不一样,但很多日志大致都有类似结构:

时间 日志级别 具体内容

比如:

2026-03-2910:15:23 INFO 用户登录成功2026-03-2910:16:01 ERROR 支付接口调用失败2026-03-2910:16:45 WARNING 订单查询耗时过长

你可以先把它粗略拆成三部分来看:

前面是时间 中间是级别 后面是描述

其中最常见的日志级别通常有:

INFO 表示普通信息WARNING 表示警告,但未必致命ERROR 表示错误 有些项目里还会有 DEBUG、CRITICAL 等级别

所以日志处理的第一步,往往不是马上写代码。 而是先观察格式。

你得先看明白,一行日志大概由哪些部分组成,哪些部分是你关心的,哪些只是背景信息。

这一步特别重要。

很多人拿到日志就急着上正则,结果写了半天规则,发现连日志结构都没看明白。 所以越是文本处理,越要先观察,再动手。


四、处理日志之前,第一件事是先把文件读进来

这一点你已经很熟悉了。

假设我们有一个日志文件 app.log,内容如下:

2026-03-2910:15:23 INFO 用户登录成功 user_id=10012026-03-2910:16:01 ERROR 支付接口调用失败 order_id=50012026-03-2910:16:45 WARNING 订单查询耗时过长 api=/order/list2026-03-2910:17:10 INFO 用户退出登录 user_id=1001

最基础的读取方式就是:

with open("app.log", "r", encoding="utf-8") as f:    content = f.read()print(content)

如果你是想一行一行处理,更常见的方式是:

with open("app.log", "r", encoding="utf-8") as f:    lines = f.readlines()for line in lines:    print(line.strip())

或者写得更直接一点:

with open("app.log", "r", encoding="utf-8") as f:for line in f:        print(line.strip())

对于日志这种“一行就是一条记录”的文本,按行处理通常比整段处理更自然。

因为很多分析动作,本来就是围绕“每一条日志”展开的。


五、为什么日志特别适合按行处理

这个习惯非常值得养成。

日志不像一篇文章,它通常是天然分行的。 每一行基本就是一条独立记录。

例如:

2026-03-2910:15:23 INFO 用户登录成功 user_id=10012026-03-2910:16:01 ERROR 支付接口调用失败 order_id=50012026-03-2910:16:45 WARNING 订单查询耗时过长 api=/order/list

如果你把整段文本混在一起看,处理起来反而容易乱。 而按行处理时,你的思路会很清晰:

读到一行 判断是不是我要的 提取关键信息 放进结果集 继续下一行

这就像流水线一样。

所以日志处理里,一个非常重要的默认思维就是:

先把日志当成一条条记录,而不是一整坨文本。

这个习惯会让你后面写筛选、提取、统计都顺手很多。


六、日志处理最常见的第一个需求:先把错误日志筛出来

现实里,很多人第一次处理日志,不是想分析全部内容。 而是最简单也最急迫的需求:

把出错的那部分先找出来。

比如你有一份上万行日志,现在最关心的是所有 ERROR 级别的记录。 这时可以直接写:

with open("app.log", "r", encoding="utf-8") as f:for line in f:if"ERROR"in line:            print(line.strip())

这段代码非常朴素,但特别实用。

它体现了一个很重要的原则:

日志分析不一定一上来就要复杂规则。 很多时候,先筛出重点内容,已经能解决大半问题。

比如:

先找所有 ERROR先找所有 WARNING先找某个接口名 先找某个用户 ID 先找某个关键词

这些其实都是日志处理中最常见的第一步。

不要小看这种基础筛选。 很多真实工作里,第一轮排查问题,真就是这么开始的。


七、进一步一点:把错误日志保存下来

很多时候,我们不只是想打印出来,而是想把筛选结果单独保存成一个文件,方便后续分析。

例如把所有错误日志保存到 error.log:

with open("app.log", "r", encoding="utf-8") as f, open("error.log", "w", encoding="utf-8") as out:for line in f:if"ERROR"in line:            out.write(line)

这样,你就得到了一份只包含错误记录的新日志文件。

这个做法特别常见。

比如原始日志太大,不方便发给别人。 比如你只想拿错误部分给同事排查。 比如你后面要针对错误日志继续统计。

这一步虽然简单,但已经很接近真实日志处理工作了:

先过滤 再抽取 再单独保存

这其实就是一种非常典型的数据预处理流程。


八、只会关键词筛选还不够,因为我们还想提取结构化信息

前面我们做的是“筛出来”。 但很多时候,光筛出来还不够。

比如下面这条日志:

2026-03-2910:16:01 ERROR 支付接口调用失败 order_id=5001

如果你只是打印这一整行,当然能看。 但如果你真正想做分析,你更希望拿到的是:

时间:2026-03-29 10:16:01 级别:ERROR 消息:支付接口调用失败 订单号:5001

一旦这些信息被拆出来,你后面就能做更多事:

统计某天错误次数 按时间排序 统计每种错误类型 找出涉及哪些订单 把结果存进表格或数据库

所以日志处理真正有价值的地方,往往在于:

把文本日志,逐步转成结构化数据。


九、最简单的日志拆分方式:先用 split 处理规则明显的日志

如果日志格式比较规整,其实不一定非要一上来就用正则。

比如日志格式始终像这样:

2026-03-2910:16:01 ERROR 支付接口调用失败

你先试着按空格拆分,就能得到不少信息。

line = "2026-03-29 10:16:01 ERROR 支付接口调用失败"parts = line.split()print(parts)

输出会是:

['2026-03-29', '10:16:01', 'ERROR', '支付接口调用失败']

这时你就能很直接地拿到:

date = parts[0]time = parts[1]level = parts[2]message = " ".join(parts[3:])

这种写法非常适合日志格式简单、字段位置稳定的场景。

所以你要记住:

文本处理不是逢日志必正则。 能用简单方法解决时,先用简单方法。

这是很重要的实战思维。


十、但很多日志并没那么规矩,这时候正则就派上用场了

真实项目里,日志格式经常更复杂一些。

例如:

2026-03-2910:16:01 ERROR 支付接口调用失败 order_id=50012026-03-2910:16:45 WARNING 订单查询耗时过长 api=/order/list2026-03-2910:17:20 INFO 用户登录成功 user_id=1001

这类日志里,前半部分相对稳定,后半部分字段不一定完全固定。 这时候正则会非常适合。

比如先把时间和级别提出来:

import reline = "2026-03-29 10:16:01 ERROR 支付接口调用失败 order_id=5001"match = re.match(r"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\s+(INFO|WARNING|ERROR)\s+(.*)", line)if match:    print("时间:", match.group(1))    print("级别:", match.group(2))    print("内容:", match.group(3))

输出大致是:

时间: 2026-03-2910:16:01级别: ERROR内容: 支付接口调用失败 order_id=5001

这一步非常关键。

因为它说明一件事:

日志虽然原本是文本,但你完全可以通过规则,把它逐步拆成更容易处理的部分。

这就是日志分析的核心起点。


十一、进一步提取日志里的具体字段

刚才我们已经把整行拆成了时间、级别、内容。 那如果我们还想从内容里继续提取字段怎么办

比如这条:

支付接口调用失败 order_id=5001

我们还想拿到订单号 5001。

这时可以继续用正则:

import remessage = "支付接口调用失败 order_id=5001"match = re.search(r"order_id=(\d+)", message)if match:    print(match.group(1))

输出:

5001

同理,你也可以提取:

user_id=1001api=/order/listcost=2.35sip=192.168.1.10

你会发现,日志处理非常像“多层提取”:

先按行读 再拆整行 再从局部字段里挖出更细的信息

这和前面学的正则提取手机号、日期、邮箱,其实是一个思路。


十二、先做一个小案例:统计错误日志一共有多少条

假设日志文件内容如下:

2026-03-2910:15:23 INFO 用户登录成功2026-03-2910:16:01 ERROR 支付接口调用失败2026-03-2910:16:45 WARNING 订单查询耗时过长2026-03-2910:17:10 ERROR 数据库连接失败2026-03-2910:18:00 INFO 用户退出登录

你想统计一共有多少条错误日志。

代码其实很直接:

count = 0with open("app.log", "r", encoding="utf-8") as f:for line in f:if"ERROR"in line:            count += 1print("错误日志数量:", count)

输出:

错误日志数量: 2

这个例子虽然简单,但它已经体现了日志处理最朴素的实用价值:

从一堆肉眼难以快速统计的文本里,提炼出明确结论。

很多自动化脚本,最开始就是从这种小需求长出来的。


十三、再进一步:统计每种日志级别分别出现多少次

这就比单纯统计错误数更完整一些了。

我们想知道:

INFO 出现多少次WARNING 出现多少次ERROR 出现多少次

这时候很适合用前一章学过的 Counter。

from collections import Counterlevels = []with open("app.log", "r", encoding="utf-8") as f:for line in f:        parts = line.split()if len(parts) >= 3:            level = parts[2]            levels.append(level)result = Counter(levels)print(result)

输出大致会是:

Counter({'INFO': 2, 'ERROR': 2, 'WARNING': 1})

你看,前面学过的统计能力,在这里马上就接上了。

这也是为什么我前面一直强调: 不要把知识点孤立看。

字符串处理、列表、字典、正则、Counter,这些东西单独看可能都不算复杂。 但一旦组合起来,就能开始解决真实问题。


十四、日志处理里最常见的需求之一:找出出现最多的错误类型

这比统计 ERROR 总数更进一步。

很多时候,大家关心的不只是“有多少错”,而是:

到底哪种错最常见

比如错误日志可能有这些:

2026-03-2910:16:01 ERROR 支付接口调用失败2026-03-2910:17:10 ERROR 数据库连接失败2026-03-2910:18:05 ERROR 支付接口调用失败2026-03-2910:19:20 ERROR 支付接口调用失败

你想知道哪个错误最频繁,就可以先把错误消息提取出来,再计数。

from collections import Countererrors = []with open("app.log", "r", encoding="utf-8") as f:for line in f:if"ERROR"in line:            parts = line.split()            message = " ".join(parts[3:])            errors.append(message)result = Counter(errors)print(result)print("最常见错误:", result.most_common(1))

输出可能是:

Counter({'支付接口调用失败': 3, '数据库连接失败': 1})最常见错误: [('支付接口调用失败', 3)]

这就已经很像一个小型日志分析报告了。


十五、很多日志分析问题,真正难点不在统计,而在清洗

比如你想统计错误类型,但日志内容可能是:

支付接口调用失败 order_id=5001支付接口调用失败 order_id=5002支付接口调用失败 order_id=5003

如果你直接整行计数,那它们会被当成三种不同错误。 但从业务角度看,它们其实是同一类问题。

所以你要做的,不只是统计。 而是先把可变部分去掉。

这时候正则清洗就很有用了:

import refrom collections import Countererrors = []with open("app.log", "r", encoding="utf-8") as f:for line in f:if"ERROR"in line:            parts = line.split()            message = " ".join(parts[3:])            message = re.sub(r"order_id=\d+", "", message).strip()            errors.append(message)result = Counter(errors)print(result)

这样,即使订单号不同,也会被归到同一个错误类别里。

你会发现,这一章和前面的“函数式清洗数据”也接上了。

日志分析不是只会读和数。 很多时候,得先把日志内容标准化,统计才有意义。


十六、日志里最重要的字段之一,往往是时间

因为很多分析都和时间有关。

比如:

某种错误是几点开始出现的 某个时间段错误暴涨 某个任务多久执行一次 某一天的错误量是不是比前一天更多 一条请求从开始到结束耗时多久

所以日志处理里,时间字段通常非常关键。

例如从日志里提取时间:

import reline = "2026-03-29 10:16:01 ERROR 支付接口调用失败"match = re.match(r"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})", line)if match:    print(match.group(1))

拿到时间字符串后,你还可以继续转成 datetime 对象:

from datetime import datetimetime_text = "2026-03-29 10:16:01"dt = datetime.strptime(time_text, "%Y-%m-%d %H:%M:%S")print(dt)

一旦变成时间对象,你后面就能做真正的时间比较和统计了。


十七、一个很实用的场景:筛选某个时间段的日志

假设你只想看上午 10 点到 11 点之间的错误日志。

这时候,时间解析就非常有用了。

from datetime import datetimeimport restart = datetime.strptime("2026-03-29 10:00:00", "%Y-%m-%d %H:%M:%S")end = datetime.strptime("2026-03-29 11:00:00", "%Y-%m-%d %H:%M:%S")with open("app.log", "r", encoding="utf-8") as f:for line in f:        match = re.match(r"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})", line)if match:            log_time = datetime.strptime(match.group(1), "%Y-%m-%d %H:%M:%S")if start <= log_time <= end and"ERROR"in line:                print(line.strip())

这类需求在排查线上问题时特别常见。

因为很多问题往往不是全天都发生,而是某一小段时间突然暴增。 所以时间筛选,是日志处理中非常高频的一类操作。


十八、日志里经常还藏着请求耗时、接口名、用户 ID 这类信息

比如一条日志:

2026-03-2910:16:45 WARNING 请求耗时过长 api=/order/list cost=2.35s

如果你只把它当普通文本看,就很浪费。 因为里面至少还有两个很有分析价值的字段:

接口:/order/list耗时:2.35s

我们可以用正则把它提出来:

import reline = "2026-03-29 10:16:45 WARNING 请求耗时过长 api=/order/list cost=2.35s"api_match = re.search(r"api=([^\s]+)", line)cost_match = re.search(r"cost=([\d.]+)s", line)if api_match:    print("接口:", api_match.group(1))if cost_match:    print("耗时:", cost_match.group(1))

输出:

接口: /order/list耗时: 2.35

一旦这些字段被提取出来,你就可以进一步做:

统计哪个接口最慢 筛选耗时超过 2 秒的请求 按接口分类统计平均耗时

这时候日志文本就已经开始往“可分析的数据”转变了。


十九、做一个更真实一点的小案例:找出所有耗时超过 2 秒的请求

这是非常典型的日志分析需求。

假设日志里有这些内容:

2026-03-2910:16:45 WARNING 请求耗时过长 api=/order/list cost=2.35s2026-03-2910:17:10 INFO 请求正常 api=/user/info cost=0.35s2026-03-2910:17:50 WARNING 请求耗时过长 api=/pay/create cost=3.20s

现在我们要把耗时超过 2 秒的接口筛出来。

import rewith open("app.log", "r", encoding="utf-8") as f:for line in f:        api_match = re.search(r"api=([^\s]+)", line)        cost_match = re.search(r"cost=([\d.]+)s", line)if api_match and cost_match:            api = api_match.group(1)            cost = float(cost_match.group(1))if cost > 2:                print(f"慢请求:{api},耗时 {cost} 秒")

这段代码已经很像实际排查性能问题的脚本了。

你会发现,它背后其实只是在做几件事:

读日志 提字段 转类型 做判断 输出结果

说到底,还是数据处理。


二十、日志处理里,函数式思维同样非常有用

前一章我们讲过,把清洗动作拆成函数。 这一章同样适用。

例如你可以把“解析一行日志”单独封装成函数:

import redefparse_log_line(line):    match = re.match(r"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\s+(INFO|WARNING|ERROR)\s+(.*)", line.strip())ifnot match:returnNonereturn {"time": match.group(1),"level": match.group(2),"message": match.group(3)    }

测试一下:

line = "2026-03-29 10:16:01 ERROR 支付接口调用失败 order_id=5001"print(parse_log_line(line))

输出:

{'time': '2026-03-29 10:16:01','level': 'ERROR','message': '支付接口调用失败 order_id=5001'}

这样一来,后面整份日志处理就会清楚很多。

你不再每次都在主循环里重复写正则。 而是把“如何解析一行日志”这件事,交给一个单独函数。

这就是结构化思维的价值。


二十一、再进一步:把日志中更细的字段也封装出来

例如我们希望一条日志最终被整理成这样:

{"time": "2026-03-29 10:16:01","level": "ERROR","message": "支付接口调用失败","order_id": "5001"}

可以进一步写解析函数:

import redefparse_log_line(line):    match = re.match(r"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\s+(INFO|WARNING|ERROR)\s+(.*)", line.strip())ifnot match:returnNone    time_text = match.group(1)    level = match.group(2)    raw_message = match.group(3)    order_match = re.search(r"order_id=(\d+)", raw_message)    order_id = order_match.group(1) if order_match elseNone    message = re.sub(r"order_id=\d+", "", raw_message).strip()return {"time": time_text,"level": level,"message": message,"order_id": order_id    }

这就更像项目代码了。

因为日志原来只是普通字符串。 现在它已经变成了一个结构化字典。 后面你再统计、筛选、排序,就会容易很多。


二十二、把整份日志转成列表后,后面事情就顺了

例如我们先把每一行都解析成字典,放进一个列表里:

logs = []with open("app.log", "r", encoding="utf-8") as f:for line in f:        parsed = parse_log_line(line)if parsed:            logs.append(parsed)print(logs)

这时候,logs 可能会变成这样:

[    {"time": "2026-03-29 10:15:23", "level": "INFO", "message": "用户登录成功", "order_id": None},    {"time": "2026-03-29 10:16:01", "level": "ERROR", "message": "支付接口调用失败", "order_id": "5001"}]

看到这里你应该会有一个明显感受:

日志原本难处理,是因为它只是文本。 一旦转成列表加字典,整个世界都清楚了。

你可以按级别筛选,可以按时间排序,可以统计错误类型,可以导出表格。 这就是“把文本变成结构化数据”的力量。


二十三、做一个完整一点的小案例:统计错误类型、错误次数、最早时间

我们来写一个更像实战的小脚本。

假设日志格式如下:

2026-03-2910:16:01 ERROR 支付接口调用失败 order_id=50012026-03-2910:17:10 ERROR 数据库连接失败 db=mysql2026-03-2910:18:05 ERROR 支付接口调用失败 order_id=50022026-03-2910:19:20 ERROR 支付接口调用失败 order_id=50032026-03-2910:20:00 INFO 用户登录成功 user_id=1001

我们想要得到这些结果:

错误总数 每种错误分别出现几次 第一条错误发生在什么时候

代码可以这样写:

import refrom collections import Counterfrom datetime import datetimedefparse_error_message(message):    message = re.sub(r"order_id=\d+", "", message)    message = re.sub(r"db=\w+", "", message)return message.strip()error_messages = []error_times = []with open("app.log", "r", encoding="utf-8") as f:for line in f:        match = re.match(r"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\s+(INFO|WARNING|ERROR)\s+(.*)", line.strip())ifnot match:continue        time_text = match.group(1)        level = match.group(2)        message = match.group(3)if level == "ERROR":            clean_message = parse_error_message(message)            error_messages.append(clean_message)            dt = datetime.strptime(time_text, "%Y-%m-%d %H:%M:%S")            error_times.append(dt)print("错误总数:", len(error_messages))print("错误统计:", Counter(error_messages))if error_times:    print("第一条错误时间:", min(error_times))

这段代码已经非常接近日常日志分析脚本的样子了。


二十四、日志处理里还有一种很常见的需求:找某个用户做了什么

比如有些日志里会记录 user_id=1001。 这时候你可能想查:

这个用户今天都干了哪些操作 他在报错前做了什么 他是不是频繁请求某个接口

例如筛出某个用户的日志:

target_user = "1001"with open("app.log", "r", encoding="utf-8") as f:for line in f:iff"user_id={target_user}"in line:            print(line.strip())

如果你愿意再往前走一步,也可以先提取所有用户 ID,再统计最活跃的用户。

你会发现,日志文本虽然表面普通,但实际上已经很像数据库里的记录了。 只不过它还没被正式整理出来而已。

而日志处理的过程,本质上就是在做这件事: 把文本里的行为记录,整理成可分析的数据。


二十五、日志处理特别适合和前面学过的知识联动

这一章其实就是前面很多知识点的一次综合应用。

字符串方法,用来做基础切分和清洗。 正则表达式,用来提取时间、级别、订单号、接口名、耗时。 时间处理,用来筛选某个时间段、计算先后顺序。 排序、去重、统计,用来总结日志规律。 函数式清洗思路,用来把解析逻辑写得更清晰。

所以如果你发现这一章有点像“综合题”,那是正常的。 因为真实工作里,本来就很少只考一个孤立知识点。

很多有价值的脚本,都是几种基础能力组合出来的。

而日志处理,恰好就是非常典型的组合型场景。


二十六、真实日志和练习日志最大的区别是什么

练习题里的日志通常都很干净。 格式统一,字段整齐,没有异常行。

但真实日志未必。

你可能会遇到:

空行 格式不完整的行 级别缺失 某些字段有时有,有时没有 时间格式偶尔不一致 消息内容里带额外空格或符号

所以写日志处理脚本时,一个特别重要的习惯就是:

不要默认每一行都完美。 要给异常情况留余地。

比如解析失败时直接跳过:

parsed = parse_log_line(line)if parsed isNone:continue

比如提取某个字段时,先判断匹配是否存在:

match = re.search(r"user_id=(\d+)", line)if match:    user_id = match.group(1)else:    user_id = None

这种“写得稳一点”的意识,和单纯会语法,是两回事。


二十七、给初学者一个非常实用的日志处理套路

以后你只要拿到一份日志,基本都可以按这个顺序想:

先观察格式。 看每一行大概由哪些部分组成。

再做基础筛选。 比如先找 ERROR,先找某个关键词。

然后提取结构。 把时间、级别、内容、附加字段拆出来。

接着清洗标准化。 去掉不重要的可变字段,让同类日志归到一起。

最后再统计和分析。 看数量、频次、时间分布、异常模式。

你按这个套路去处理,思路通常不会太乱。

最怕的是一上来就想一步到位,结果规则还没看清,代码已经写乱了。


二十八、这一章真正重要的,不只是会处理日志,而是会从文本里挖信息

你可以把日志看成一种特别典型的文本数据。 它比普通文章更规整,但又没有表格那么整洁。

所以学日志处理,本质上是在训练一种非常实用的能力:

面对一堆文本,能不能把里面有价值的信息提出来,并整理成可分析的结果。

这不只对日志有用。

以后你处理聊天记录、爬虫原始文本、服务器输出、命令行结果、系统导出文件,很多时候都在做类似的事。

所以这一章表面讲的是日志。 更深一层,讲的是文本信息提取和结构化分析能力。

这是非常有实战价值的一步。


本章小结

日志本质上是程序运行过程留下的文本记录。 它通常包含时间、级别、事件描述,以及各种补充字段。

日志处理的核心,不是把文件读出来就结束。 而是要把日志从一堆原始文本,逐步变成可筛选、可统计、可分析的结构化信息。

这一章你要重点掌握这些思路:

先按行处理日志。 先做关键词筛选,比如先找 ERROR。 能用 split() 解决的场景,不必一上来就用正则。 格式复杂时,用正则提取时间、级别、接口、用户 ID、耗时等字段。 把可变部分清洗掉,再做统计,结果会更可靠。 尽量把解析逻辑封装成函数,让代码更清楚、更像正式项目。

当你真正理解这一点,日志对你来说就不再只是“程序吐出来的一堆字”。 它会慢慢变成一份可以被提炼、被分析、被利用的数据来源。


课后练习

第一,准备一份简单日志文件,统计其中 ERROR 一共出现多少次。

第二,把日志中的 INFO、WARNING、ERROR 级别分别统计出来。

第三,写一个正则,把每行日志里的时间、级别、内容拆出来。

第四,从日志中提取所有 user_id 或 order_id,整理成列表。

第五,把错误日志的可变字段去掉后,再统计哪种错误最常见。

第六,筛选某个时间段内的日志,看看这段时间里到底发生了什么。

最新文章

随机文章