资讯详情

【Web全栈进阶】监控与告警:线上系统的“体检报告”

📅 2026/10/23 2:54:31 | 华诺云谱 👁 阅读
【Web全栈进阶】监控与告警:线上系统的“体检报告”
上线不是终点是“运营”的起点。网站半夜挂了谁第一个知道——希望是你而不是用户。本篇给早报站装上体检报告日志、健康检查、Uptime监控、报警通知。从“能跑”到“知道它在跑”从“用户替你发现故障”到“你比用户先发现故障”。本篇产出/api/health深度健康检查 结构化日志打点 watchdog监控脚本 报警纪律。含代码约100行。 太长不看版给想快速上手的你项目信息一句话说明本篇目标给上线后的早报站装上监控与告警代码行数~100行含注释依赖requests已装psutil练习用核心功能健康检查 日志轮转 定时探活 报警去重跑起来的命令curl /api/healthcrontab定时跑watchdog核心知识点可观测性三支柱、深/浅健康检查、报警去重做完你能得到一套“你比用户先知道”的监控体系一、概念课可观测性三支柱与性价比哲学 可观测性Observability三支柱支柱回答的问题本篇做法日志 Logs发生了什么逐条记录logging配置 关键事件打点指标 Metrics整体状态怎么样数字健康接口 命中率/耗时追踪 Tracing请求经过了谁微服务才需要本篇一句带过 性价比哲学别一上来就上Grafana全家桶。早报站的体量“结构化日志 健康接口 外部定时探测”三层就够了——监控的花费要和系统的价值成正比。 等真有第二个服务、第十个接口时再谈Prometheus练习④。 监控三问① 活着吗进程在不在 ② 健康吗依赖都通吗 ③ 出事了能查吗日志能不能还原现场三问对应本篇三个产出。二、第0步结构化日志——出事能查的第一前提core/logging_config.pycore/logging_config.py —— 应用日志控制台 文件轮转双写importloggingfromlogging.handlersimportRotatingFileHandlerfrompathlibimportPath Path(logs).mkdir(exist_okTrue)fmtlogging.Formatter(%(asctime)s %(levelname)s %(name)s %(message)s)# ① stdout → docker logs能看到consolelogging.StreamHandler()console.setFormatter(fmt)# ② 文件轮转单文件10MB留5份file_handlerRotatingFileHandler(logs/daily.log,maxBytes10_000_000,backupCount5,encodingutf-8)file_handler.setFormatter(fmt)logging.basicConfig(levellogging.INFO,handlers[console,file_handler])loggerlogging.getLogger(daily) main.py里一行启用# app/main.py 最顶部在任何其他import之前importcore.logging_config# noqa: F401 启用日志配置⚠️必须在其他模块之前import——logging配置是全局的晚import的模块可能已经用旧配置打过日志了。 关键位置打点监控的价值在“记对了地方”logger.info(login ok user%s,username)logger.warning(login failed user%s ip%s,username,request.client.host)logger.info(upload ok file%s size%s,name,file.size)logger.info(cache hit key%s,cache_key)# 练习④的正式上岗logger.error(db error detail%s,detail) 两个工程细节#细节说明①双写stdout给docker compose logs用文件给排查归档用——只写文件会让容器日志变哑巴第八节⑤②轮转单文件10MB自动切割日志是磁盘杀手不轮转迟早撑爆第八节②三、第1步健康检查接口——浅检查 深检查app/routers/health.pyapp/routers/health.py —— 深度健康检查importredisasredis_libfromfastapiimportAPIRouter,Dependsfromsqlalchemyimporttextfromsqlalchemy.ormimportSessionfromapp.depsimportget_sessionfromcore.cacheimportrasredis_client routerAPIRouter(prefix/health,tags[health])router.get()defhealth(session:SessionDepends(get_session)):# ① 深检查依赖逐个探活坏了不抛500降级标记db_ok,redis_okTrue,Truetry:session.execute(text(SELECT 1))exceptException:db_okFalsetry:redis_client.ping()exceptredis_lib.RedisError:redis_okFalse# ② 汇总ok / degraded有依赖挂了但服务还活着statusokif(db_okandredis_ok)elsedegradedreturn{status:status,db:db_ok,redis:redis_ok} 浅检查 vs 深检查类型回答什么问题给谁用浅检查进程活着吗Docker healthcheck用curl /docs可以换成它深检查依赖都通吗监控报警用 深检查的核心纪律探活失败要降级返回degraded而不是抛500——健康接口自己崩了监控就瞎了。 挂载路由app/main.pyfromapp.routersimporthealth app.include_router(health.router,prefix/api)四、第2步watchdog——外部视角的定时探测健康接口有了还得有人定时来问。scripts/watchdog.py跑在任意一台别的机器上——外部视角才真实scripts/watchdog.py —— 每5分钟探活异常即报警cron驱动importosimporttimeimportrequests URLos.getenv(DAILY_URL,https://daily.example.com)WEBHOOKos.getenv(ALERT_WEBHOOK,)# 钉钉/飞书/Server酱的机器人地址_last_alert0.0# 报警去重静默窗口defsend_alert(text:str)-None:global_last_alertiftime.time()-_last_alert1800:# 30分钟内不重复报第八节④return_last_alerttime.time()ifWEBHOOK:requests.post(WEBHOOK,json{msgtype:text,text:{content:f[早报站告警]{text}}},timeout5)print(ALERT:,text)# 本地也留痕defcheck()-None:try:resprequests.get(f{URL}/api/health,timeout5)dataresp.json()exceptExceptionase:send_alert(f健康接口不可达:{e})returnifresp.status_code!200ordata.get(status)!ok:send_alert(f服务异常: status{data.get(status)}fdb{data.get(db)}redis{data.get(redis)})if__name____main__:check()⏰ 挂到定时器服务器上crontab -e*/5 * * * * cd /opt/daily /opt/daily/venv/bin/python scripts/watchdog.py /var/log/watchdog.log 21用venv里的python绝对路径——cron的PATH和你shell的PATH不一样直接用/usr/bin/python3会缺依赖第八节③。 报警渠道三选一渠道获取方式钉钉群机器人群设置 → 机器人 → 自定义 → 拿webhook URL飞书群机器人同上Server酱sct.ftqq.com 注册 → 拿SendKey → 拼URL 都是“创建机器人 → 拿到webhook URL → 填进ALERT_WEBHOOK环境变量”三步。报警落地自选本篇只管把URL用起来。五、第3步告警纪律——别把自己练成“狼来了”✅ 该报- 服务整体不可达 - db/redis不可达 - 磁盘超90%练习② - 同一错误持续5分钟以上❌ 别报- 单次偶发超时 - 个别404/401那可能只是爬虫或用户输错密码 - 每5分钟必然重复的同一件事去重静默窗口解决第4节已内置 告警疲劳alert fatigue报警太多 → 你开始无视它们 → 真出事那天也没人看。报警的价值 频率低 × 每次都真。宁可少报漏一次不可天天“狼来了”。六、验收清单1. GET /api/health →{status:ok,db:true,redis:true}2.dockerstop redis-daily容器 → health变degradeddb:true, redis:false且接口本身不5003. logs/daily.log出现login/upload/cache hit打点单文件超10MB自动切割4. 手动跑watchdog.py → 正常时无报警停掉Redis再跑 → 收到webhook/钉钉消息5. crontab里定时任务生效*/5自动探测6. 全程无报错后提交Gitgitadd.gitcommit-m监控告警健康检查 结构化日志 watchdog七、常见报错这6个监控上手的标配重点① health接口自己500Redis没起时 原因深检查没try/except——探活的初衷是“报告坏消息”结果自己成了坏消息。✅ 解法每个依赖的探测独立try/except降级返回degraded第3节写法。② 日志把磁盘撑爆 / 写入报No space left on device 原因RotatingFileHandler没配或maxBytes给了天文数字。✅ 解法10MB × 5份是合理起点du -sh logs/定期看一眼。③ cron任务“跑着跑着就不跑了” 原因cron的环境变量和PATH与你的shell不同——相对路径、python不在PATH、环境变量缺失。✅ 解法全部用绝对路径venv里的python、项目目录需要的环境变量写进脚本或crontab行内。④ Redis挂了1小时 → 收到12条一模一样的报警 原因没做去重——每5分钟探测一次每次都报。✅ 解法静默窗口第4节_last_alert逻辑同一问题30分钟内只报第一次恢复后可发一条“已恢复”。⑤docker compose logs里看不到应用日志 原因只配了文件输出stdout成了哑巴。✅ 解法双写console file两个handler——容器世界stdout是一等公民。⑥ health全绿但用户照样报障 原因健康检查覆盖不了所有故障连接池耗尽、慢查询、业务逻辑bug……。✅ 解法认识监控的边界——健康检查是体检报告不是全部真相用户反馈渠道哪怕一个QQ群永远是监控的补充。八、课后练习#练习难度提示1命中率打点cache_get/cache_set加日志练习④的正式版跑一天算真实命中率⭐⭐可观测性入门2磁盘监控watchdog里加磁盘占用检查⭐⭐psutil.disk_usage超90%报警3报警去重升级静默窗口内累积错误次数恢复时发一条“已恢复共失败N次”⭐⭐⭐状态机思维4选做Prometheus Grafana全案metrics接口暴露 两个容器 一块面板⭐⭐⭐⭐体验“监控全家桶”感受它与手写脚本的差别 配套代码health路由、日志配置与watchdog脚本已上传Gitpython_daily/【gitee仓库地址】
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑