flash 的消息存在会话里,读一次就删,所以它只活一跳。
日志五级:DEBUG、INFO、WARNING、ERROR、CRITICAL;setLevel 定的是下限。
发邮件这件事本沙箱只做到「组装」,发送要 SMTP 账号,照实标。
这一篇对应教材 1.3 节的 Flash、日志、邮件三小节,是「反馈」这一组的三件套。 上一篇收数据,这一篇告诉用户结果,同时留下给运维看的东西。
一、flash 解决什么问题
用户点了「保存」,页面跳回列表。这时候得告诉他一声「保存成功了」。
但跳转是重定向,跳过去那个页面并不知道刚才发生了什么。把消息塞进 URL?难看、能被改、还会被收藏。
flash 的做法是:消息存进会话,下一跳读出来就删。正好一跳,多一次刷新就没了。
这既解决了「跨请求传消息」,又天然避免了「同一个提示反复弹」。
二、两段式用法
写的那一头:flash("保存成功", "success"),第二个参数是分类。
读的那一头:模板里调 get_flashed_messages(with_categories=true),带分类就要解二元组。
分类只是个字符串,作用是在模板里挂不同的 CSS 类。常见的是 success、warning、error 三个。
实测:跟过去那次请求能看到两条,再刷一次就空了——这就是「读一次就删」的直接证据。
三、日志五级怎么选
| 级别 | 用来记什么 |
|---|---|
| DEBUG | 排查用的细节,比如「这里进了哪个分支」 |
| INFO | 正常但值得记的事件,比如「用户登录了」 |
| WARNING | 能继续跑,但有点不对,比如「配置缺项,用了默认值」 |
| ERROR | 功能失败了,比如「调用支付接口超时」 |
| CRITICAL | 整个服务不能跑了 |
setLevel 定的是下限:设成 WARNING,INFO 和 DEBUG 就直接不产生。
这一点很好用:开发时设 DEBUG 看细节,上线设 INFO 或 WARNING,同一份代码两种噪音。
app.logger 是 Flask 配好的 logger,名字就是应用名。
别自己 logging.getLogger() 另起一个,那样格式和去向都不统一。
四、日志去哪了
默认输出到 stderr。容器里 stderr 会被收集,所以最省事的做法就是「什么都不配,让它往 stderr 写」。
要落文件就得自己加 FileHandler,但要注意两件事:文件会一直长,而且多进程同时写会打架。
所以生产环境通常是「输出到 stdout/stderr,由外部系统收集」,不是自己写文件。
教材用 RotatingFileHandler 演示按大小切分,那是单机部署的写法,知道有这回事就行。
五、发邮件:能测的和不能测的
教材 1.3.6 讲用 smtplib 发邮件:组装 EmailMessage,连 SMTP 服务器,登录,发送。
组装和发送是两件事。组装纯本地,能测;发送要服务器和账号,测不了。
这次实测能真跑的是组装:设好主题、发件人、收件人、正文,序列化出来 217 字节,全是标准 MIME 格式。
发送那一段(SMTP_SSL 加 login)照原样贴出并标注不执行——没有账号密码,假装跑过没有意义。
六、动手 Demo:Flash 消息与日志记录
这一节把 flash 的一跳、日志五级、级别过滤三件事各跑一遍,最后组装一封不发送的邮件。
@app.route("/flash")
def do_flash():
flash("保存成功", "success")
return redirect(url_for("show"))
@app.route("/show")
def show():
return render_template("msg.html")
实跑输出:
--------------------------------------------------------------------
Flash 消息与日志记录
--------------------------------------------------------------------
① flash 只活一跳
--------------------------------------------------------------------
GET /flash -> 302,跳转到 /show
跟过去 GET /show,页面内容:
[success] 保存成功
[warning] 有 2 条记录没填完整
(页面渲染完毕)
再刷一次 /show -> (页面渲染完毕)
! 第二次就空了。flash 的消息存在会话里,读一次就删——这正是「提示不许重复出现」的实现方式。
② 不带 category 会怎样
--------------------------------------------------------------------
get_flashed_messages() -> ['没有分类的消息']
with_categories=True -> [('message', '没有分类的消息')]
分类就是个字符串,用来在模板里挂不同的 CSS 类。success 绿、warning 黄、error 红。
③ 日志:五级与去向
--------------------------------------------------------------------
DEBUG _d62b 这是一条 DEBUG 级别的日志
INFO _d62b 这是一条 INFO 级别的日志
WARNING _d62b 这是一条 WARNING 级别的日志
ERROR _d62b 这是一条 ERROR 级别的日志
CRITICAL _d62b 这是一条 CRITICAL 级别的日志
! 五级从轻到重:DEBUG、INFO、WARNING、ERROR、CRITICAL。
setLevel 定的是「这个级别及以上才输出」,级别以下的直接丢掉。
④ 级别真的会挡
--------------------------------------------------------------------
把级别调到 WARNING 之后,缓冲区里只有:'这条会'
! 上线时级别调高、开发时调低,同一份代码两种噪音。
⑤ 发邮件:这一步本沙箱不执行
--------------------------------------------------------------------
[平台原样 · 本沙箱不执行]
| import smtplib
| from email.message import EmailMessage
|
| msg = EmailMessage()
| msg["Subject"] = "测试"
| msg["From"] = "[email protected]"
| msg["To"] = "[email protected]"
| msg.set_content("正文")
|
| with smtplib.SMTP_SSL("smtp.example.com", 465) as s:
| s.login("账号", "应用专用密码")
| s.send_message(msg)
要一台真的 SMTP 服务器和一个能用的账号密码,本沙箱两样都没有。
能真跑的部分是「把邮件组装出来」:
Subject 头 -> 测试邮件
正文长度 -> 217 字节
! 组装和发送是两件事。组装能测、发送要账号——所以先把组装做好,发送部分等有了 SMTP 再验。
第一次跟过去能看到两条,第二次刷新只剩「页面渲染完毕」——读一次就删,这条最该记住。 五级日志用的格式串是级别、logger 名、消息,前两项不是手写的。 调到 WARNING 之后,缓冲区里只剩「这条会」——级别以下的不产生,不是产生后过滤。 邮件那一段组装出 217 字节,能验的就是这些;发送需要账号,标了不执行。
30 秒实验
- 把
flash的分类换成自定义的error,模板里什么都不改,看输出变成什么样。 - 把日志格式串里加上
%(asctime)s,看会不会多出时间戳。 - 想一想:如果一个请求里
flash了三次,get_flashed_messages()会返回几个?
常见误区
- 以为 flash 会一直显示:它只活一跳,刷新就没了,这不是 bug。
- 用 print 打日志:没级别、没时间、线上关不掉。
- 日志里写密码和令牌:日志会被收集、转发、长期保存,等于二次泄露。
- 以为所有日志都要落文件:容器里输出到 stderr 由外部收集更稳。
自测三问(点开对答案)
1. flash 的消息存哪、活多久? —— 存在会话里,下一跳读出来就删,所以只活一次请求。 2. `setLevel(WARNING)` 之后 DEBUG 日志去哪了? —— 根本不产生——级别是下限,低于它的直接丢掉。 3. 为什么邮件只做到组装? —— 发送需要真实的 SMTP 服务器和账号密码,沙箱里两样都没有,假装跑过没有意义。反馈有了,还差「记住你是谁」。下一篇讲 Cookie 与 Session,顺带把 Response 这个真正的返回值讲清。
- ☐ (本篇完,下一篇 1.8 Cookie、Session 与 Response)