从一个你可能真的会用的小脚本开始
把访问日志里的 5xx 错误按路径计数,显示最常出错的页面。空行跳过,坏行计数但不让程序崩溃。本课只处理虚构日志,不上传服务器真实日志。
可以在 Qwen Chat粘贴任务,也可以用已经配置好的 Qwen Code 打开练习目录。模型负责提出代码,编程工具负责文件和运行环境;本课不要求部署开放权重。Qwen Code 官方仓库。
样例数据先固定下来
本课每行只有三个字段:时间、状态码、路径,空白分隔。不是完整 Nginx 标准格式,别把其他日志格式直接丢进来。
10:00 200 /home
10:01 500 /search
10:02 502 /search
10:03 503 /login
bad-line
10:04 404 /missing
预期结果:/search 为 2,/login 为 1,坏行 1;404 不算本题的服务器错误。计数相同按路径字母顺序排列,避免输出顺序忽然变化。
给助手的任务
实现 summarize(text),输入上面的三列教学日志字符串。
忽略空行。字段数不是3、状态码非100~599整数、路径不以/开头,都算坏行。
只统计500~599,按次数降序、路径升序返回列表,同时返回坏行数。
不使用第三方包,不读取外部文件,不发送网络请求。
给出完整脚本和断言,说明这只适配本题三列格式。
先让它说明坏行怎么处理,再看代码。要求“任何异常都忽略”容易把程序自身错误也隐藏掉;应只捕获确实预期的解析错误。
可运行参考
保存为 log_summary.py,运行 python log_summary.py:
from collections import Counter
def summarize(text):
errors, bad = Counter(), 0
for line in text.splitlines():
if not line.strip():
continue
parts = line.split()
if len(parts) != 3:
bad += 1
continue
_, raw_status, path = parts
if not raw_status.isascii() or not raw_status.isdigit():
bad += 1
continue
status = int(raw_status)
if not 100 <= status <= 599 or not path.startswith("/"):
bad += 1
continue
if status >= 500:
errors[path] += 1
return sorted(errors.items(), key=lambda item: (-item[1], item[0])), bad
sample = """10:00 200 /home
10:01 500 /search
10:02 502 /search
10:03 503 /login
bad-line
10:04 404 /missing"""
assert summarize(sample) == ([("/search", 2), ("/login", 1)], 1)
assert summarize("") == ([], 0)
assert summarize("x nope /a\nx 700 /a\nx 500 a") == ([], 3)
assert summarize("x 500 /b\nx 500 /a")[0] == [("/a", 1), ("/b", 1)]
print(summarize(sample))
预期打印 ([('/search', 2), ('/login', 1)], 1)。Counter 只负责累计次数,排序规则由最后一行明确指定。
运行不对,怎样向模型反馈
若结果把 /missing 也算进去:
样例中的404不属于本题5xx。现在输出里出现了 /missing,请只改过滤条件为500~599,保留坏行计数与同次数路径排序。重新运行原样例、空输入和700状态码三组测试。
若函数遇到坏行直接抛错,附上最小坏行“bad-line”,说明期望是 bad 加一并继续。不要把几十 MB 日志全贴进去;能复现问题的一行更容易定位。
怎样把它用于自己的文件
先复制一份已经脱敏的小日志,确认字段顺序与本课一致,再让助手增加本地文件读取。真实日志如果有引号内空格、请求方法和时间区间,必须重新定义解析器,不能靠 split 碰运气。
输出先用终端列表,确认统计可信后再考虑图表。一个漂亮排行榜如果把 404 当 500,仍然没有解决问题。