【Python 进阶:权限校验装饰器与 logging 日志系统,12 大实战技巧一篇搞定!】
Python 进阶权限校验装饰器与 logging 日志系统12 大实战技巧一篇搞定摘要还在用 print 调试代码权限校验逻辑散落在每个函数里本文从装饰器闭包原理讲起手写一套生产级权限校验装饰器无参/有参/类装饰器三种形态再深入 Python logging 四大组件涵盖日志分级、多 Handler 输出、文件轮转和装饰器 logging 联动实战。全程代码可独立运行读完就能落地到自己的项目里。从手写装饰器到搭建可落地的日志系统12 个实战案例覆盖权限校验和 logging 全链路代码全部可复制运行Python 项目越写越大之后两个问题会反复出现一是每个敏感函数里都塞了一段if user.role ! admin: raise…代码又臭又长二是到处 print 调试信息上线后要么删不干净要么根本看不到日志。装饰器和 logging 就是为这两个问题而生的——一个把权限校验逻辑抽离复用一个把日志管理变得可控、可配置。一、装饰器基础回顾在写权限校验装饰器之前先把装饰器的工作原理吃透。不然后面带参装饰器、类装饰器会一头雾水。1.1 函数是一等公民Python 里函数就是对象可以赋值给变量、作为参数传递、在另一个函数里定义defgreet(name):returnfHello,{name}!# 函数可以赋值给变量saygreetprint(say(World))# Hello, World!# 函数可以作为参数传递defrun(func,arg):returnfunc(arg)print(run(greet,Python))# Hello, Python!1.2 最简单的装饰器装饰器本质上是一个接受函数、返回新函数的高阶函数importfunctoolsimporttimedeftimer(func):统计函数执行时间的装饰器functools.wraps(func)# 保留原函数的 __name__ 和 __doc__defwrapper(*args,**kwargs):starttime.perf_counter()resultfunc(*args,**kwargs)elapsedtime.perf_counter()-startprint(f[timer]{func.__name__}耗时:{elapsed:.4f}s)returnresultreturnwrappertimerdefcompute(n):total0foriinrange(n):totali**2returntotalprint(compute(1000000))# [timer] compute 耗时: 0.0521s# 333332833333500000要点说明timer语法糖等价于compute timer(compute)functools.wraps必须加否则compute.__name__会变成wrapper*args, **kwargs保证装饰器适配任意参数签名一句话decorator就是func decorator(func)装饰器必须返回一个 callable。二、权限校验装饰器实战2.1 场景设定以下代码假设有一个全局变量current_user代表当前登录用户生产环境中可替换为 Flask 的g.user、Django 的request.user或从 session/token 中读取# 模拟用户数据classUser:def__init__(self,id,name,role):self.ididself.namename self.rolerole# admin | editor | viewercurrent_userUser(1,张三,viewer)2.2 无参装饰器单一角色校验最简单的场景——只允许 admin 调用importfunctoolsdefrequire_admin(func):只允许 admin 角色调用 —— 无参装饰器functools.wraps(func)defwrapper(*args,**kwargs):ifcurrent_user.role!admin:raisePermissionError(f用户{current_user.name}(角色:{current_user.role}) 无权限执行{func.__name__})returnfunc(*args,**kwargs)returnwrapperrequire_admindefdelete_user(user_id):删除用户 —— 仅 admin 可调用returnf用户{user_id}已删除# 测试viewer 调用 admin 接口try:print(delete_user(42))exceptPermissionErrorase:print(e)# 用户 张三 (角色: viewer) 无权限执行 delete_user2.3 带参装饰器多角色/多权限等级需要支持允许 admin 或 editor 调用就得让装饰器接收参数importfunctoolsdefrequire_role(*allowed_roles):# 外层接收装饰器参数允许指定角色列表调用 —— 带参装饰器defdecorator(func):# 中层接收被装饰函数functools.wraps(func)# 内层 wrapper 替换原函数defwrapper(*args,**kwargs):ifcurrent_user.rolenotinallowed_roles:raisePermissionError(f用户{current_user.name}(角色:{current_user.role}) f不属于{allowed_roles}无权执行{func.__name__})returnfunc(*args,**kwargs)returnwrapperreturndecoratorrequire_role(admin,editor)defedit_article(article_id,content):编辑文章 —— admin 或 editor 可调用returnf文章{article_id}已更新为:{content}require_role(admin)defgrant_role(user_id,new_role):授权操作 —— 仅 adminreturnf用户{user_id}角色变更为{new_role}# 测试print(edit_article(1,新内容))# 触发 PermissionError (viewer 无权)三层嵌套原理层接收的参数返回第 1 层require_role*allowed_roles装饰器参数decorator函数第 2 层decoratorfunc被装饰函数wrapper函数第 3 层wrapper*args, **kwargs原函数参数原函数的返回值记住规律每多一层嵌套就多一个可配置维度。带参装饰器 无参装饰器外面再套一层。2.4 类装饰器权限校验 审计日志类装饰器比函数装饰器更适合有状态的场景——比如记录每个接口的调用次数importfunctoolsfromcollectionsimportdefaultdictclassrequire_permission:类装饰器权限校验 调用统计call_countdefaultdict(int)# 类级别统计def__init__(self,*roles):self.allowed_rolesrolesdef__call__(self,func):functools.wraps(func)defwrapper(*args,**kwargs):ifcurrent_user.rolenotinself.allowed_roles:raisePermissionError(f{current_user.name}无权访问{func.__name__})require_permission.call_count[func.__name__]1resultfunc(*args,**kwargs)print(f[AUDIT]{current_user.name}调用了{func.__name__}f(第{require_permission.call_count[func.__name__]}次))returnresultreturnwrapperrequire_permission(admin,editor)defpublish_post(post_id):returnf文章{post_id}已发布# 模拟切换用户测试current_userUser(2,李四,admin)print(publish_post(101))# [AUDIT] 李四 调用了 publish_post (第 1 次)# 文章 101 已发布print(publish_post(102))# [AUDIT] 李四 调用了 publish_post (第 2 次)# 文章 102 已发布三种装饰器对比类型写法适用场景无参函数装饰器require_admin单一校验逻辑固定带参函数装饰器require_role(admin)需要配置参数角色列表等类装饰器require_permission(admin)需要维护状态统计、缓存等三、logging 日志系统基础权限校验只告诉你谁不可以做什么真正排查问题还得靠日志。Python 内置的 logging 模块足够强大不需要装第三方库。3.1 为什么不用 printprintlogging输出到 stdout改不了目标可同时输出到文件、控制台、远程服务器只有一种级别全输出5 个级别DEBUG / INFO / WARNING / ERROR / CRITICAL上线后要删干净配置即可关闭 DEBUG 级别保留 ERROR没有时间戳、调用位置自动记录时间、文件名、行号、进程 ID无性能控制可异步写入、批量刷新3.2 logging 四大组件importlogging# 1. Logger — 日志记录器入口loggerlogging.getLogger(my_app)logger.setLevel(logging.DEBUG)# 设置最低记录级别# 2. Handler — 处理器输出目标console_handlerlogging.StreamHandler()# 输出到控制台console_handler.setLevel(logging.INFO)# 控制台只看 INFO 以上file_handlerlogging.FileHandler(app.log,encodingutf-8)file_handler.setLevel(logging.DEBUG)# 文件记录所有级别# 3. Formatter — 格式化器输出格式formatterlogging.Formatter(fmt%(asctime)s | %(levelname)-8s | %(name)s | %(funcName)s:%(lineno)d | %(message)s,datefmt%Y-%m-%d %H:%M:%S)console_handler.setFormatter(formatter)file_handler.setFormatter(formatter)# 4. Filter — 过滤器可选按条件过滤日志# 此处省略一般用 Handler 级别控制即可# 组装logger.addHandler(console_handler)logger.addHandler(file_handler)# 测试logger.debug(变量 x %d,42)# 仅写入文件logger.info(服务启动成功)# 控制台 文件logger.warning(内存使用率 85%%)# 控制台 文件logger.error(数据库连接失败,exc_infoTrue)# exc_infoTrue 附带堆栈运行后控制台输出2026-07-30 18:05:12 | INFO | my_app | module:30 | 服务启动成功 2026-07-30 18:05:12 | WARNING | my_app | module:31 | 内存使用率 85% 2026-07-30 18:05:12 | ERROR | my_app | module:32 | 数据库连接失败而app.log文件里还会多一行DEBUG日志。3.3 日志级别与使用场景级别数值使用场景DEBUG10开发调试变量值、函数入参出参INFO20关键业务流程节点用户登录、订单创建WARNING30非预期但可恢复内存告警、重试成功ERROR40需要人工介入接口调用失败、数据异常CRITICAL50系统级故障数据库宕机、磁盘满生产环境推荐 INFO 级别排查线上问题时临时降到 DEBUG。3.4 日志文件轮转RotatingFileHandler日志文件会无限增长必须做轮转fromlogging.handlersimportRotatingFileHandler# 单个文件最大 5MB保留 3 个备份app.log, app.log.1, app.log.2, app.log.3rotating_handlerRotatingFileHandler(app.log,maxBytes5*1024*1024,backupCount3,encodingutf-8)rotating_handler.setFormatter(formatter)logger.addHandler(rotating_handler)或者按时间切分每天一个新文件fromlogging.handlersimportTimedRotatingFileHandler# 每天凌晨切分保留 7 天timed_handlerTimedRotatingFileHandler(app.log,whenmidnight,interval1,backupCount7,encodingutf-8)timed_handler.setFormatter(formatter)logger.addHandler(timed_handler)3.5 装饰器 logging 联动自动记录函数调用把前面两套知识合在一起写一个实用的函数调用日志装饰器importloggingimporttimeimportfunctools logging.basicConfig(levellogging.INFO,format%(asctime)s | %(levelname)-8s | %(message)s,datefmt%H:%M:%S)loggerlogging.getLogger(audit)deflog_call(func):自动记录函数调用入参、出参、耗时、异常functools.wraps(func)defwrapper(*args,**kwargs):# 裁剪长参数避免日志爆炸args_repr[repr(a)[:50]forainargs]kwargs_repr{k:repr(v)[:50]fork,vinkwargs.items()}signaturef{func.__name__}({, .join(args_repr)},{kwargs_repr}).strip(, {})logger.info( 调用 %s,signature)starttime.perf_counter()try:resultfunc(*args,**kwargs)elapsedtime.perf_counter()-start result_reprrepr(result)[:80]logger.info( %s 完成 (%.4fs) - %s,func.__name__,elapsed,result_repr)returnresultexceptExceptionase:elapsedtime.perf_counter()-start logger.error(!!! %s 异常 (%.4fs): %s,func.__name__,elapsed,e,exc_infoTrue)raisereturnwrapper# ---- 与权限装饰器叠加使用 ----log_callrequire_role(admin,editor)deftransfer_money(from_user,to_user,amount):转账 —— 需要权限 记录日志returnf{amount}元从{from_user}转到{to_user}# 切换为 admincurrent_userUser(3,王五,admin)print(transfer_money(王五,赵六,1000))输出18:10:33 | INFO | 调用 transfer_money(王五, 赵六, 1000) 18:10:33 | INFO | transfer_money 完成 (0.0001s) - 1000 元从 王五 转到 赵六 1000 元从 王五 转到 赵六3.6 生产级 logging 配置模板不用代码拼接直接用logging.config.dictConfig集中管理importlogging.config LOGGING_CONFIG{version:1,disable_existing_loggers:False,formatters:{default:{format:%(asctime)s | %(levelname)-8s | %(name)s | %(message)s,datefmt:%Y-%m-%d %H:%M:%S,},detailed:{format:%(asctime)s | %(levelname)-8s | %(name)s | %(pathname)s:%(lineno)d | %(funcName)s | %(message)s,},},handlers:{console:{class:logging.StreamHandler,level:INFO,formatter:default,},file:{class:logging.handlers.RotatingFileHandler,level:DEBUG,formatter:detailed,filename:app.log,maxBytes:5*1024*1024,# 5MBbackupCount:5,encoding:utf-8,},error_file:{class:logging.FileHandler,level:ERROR,formatter:detailed,filename:error.log,encoding:utf-8,},},root:{# 根 Logger 配置level:DEBUG,handlers:[console,file,error_file],},}logging.config.dictConfig(LOGGING_CONFIG)loggerlogging.getLogger(__name__)logger.info(应用启动 -- 日志系统已初始化)logger.debug(数据库连接池大小: 20)logger.error(支付回调验签失败: order_id12345)运行后生成 3 条输出路径控制台INFO 及以上app.log全部级别自动轮转error.log仅 ERROR 和 CRITICAL方便告警监控直接 tail四、总结速查表4.1 装饰器速查需求写法关键点单一角色校验require_admin一层嵌套函数返回函数多角色校验require_role(admin, editor)三层嵌套外层收参数需要维护状态class require_permission__init__收参数__call__实现逻辑保留原函数元信息functools.wraps(func)写在 wrapper 上一行多个装饰器叠加log_call在上require_role在下执行顺序从下往上越靠近函数定义的越先执行4.2 logging 速查需求方法创建 Loggerlogging.getLogger(name)设置级别logger.setLevel(logging.DEBUG)输出到控制台logging.StreamHandler()输出到文件logging.FileHandler(app.log)按大小轮转RotatingFileHandler(maxBytes5MB, backupCount3)按时间轮转TimedRotatingFileHandler(whenmidnight)自定义格式logging.Formatter(fmt..., datefmt...)字典配置logging.config.dictConfig(CONFIG_DICT)记录异常logger.error(msg, exc_infoTrue)别用 root logger始终用getLogger(__name__)避免第三方库日志污染如果这篇文章帮你搞懂了装饰器和 logging欢迎点赞收藏。有疑问欢迎在评论区留言讨论

相关新闻

神经网络基础与实战:从原理到MNIST分类实现

神经网络基础与实战:从原理到MNIST分类实现

1. 神经网络基础:从生物神经元到数学模型 神经网络的核心思想源自对人类大脑神经元工作方式的模拟。生物神经元通过树突接收信号,当信号强度超过阈值时,轴突会触发电脉冲传递信息。在人工神经网络中,我们用数学函数来模拟这一过程…

2026/10/14 10:47:57 阅读更多 →
高通9008模式救砖全攻略:从原理到实战,拯救变砖安卓设备

高通9008模式救砖全攻略:从原理到实战,拯救变砖安卓设备

1. 项目概述:当平板变成“砖头”,我们如何自救?手里拿着一台彻底黑屏、无法开机、连充电指示灯都不亮的联想小新Pad,那种感觉就像捧着一块昂贵的“电子砖头”。对于很多用户来说,这几乎是数码设备最绝望的状态。但作为…

2026/10/12 7:51:24 阅读更多 →
基于Pact的Spring Cloud微服务契约测试实践:从消费者驱动到自动化验证

基于Pact的Spring Cloud微服务契约测试实践:从消费者驱动到自动化验证

1. 项目概述:为什么微服务集成测试是个“老大难”?在微服务架构里摸爬滚打几年,最让人头疼的往往不是单个服务的开发,而是服务之间的集成。你这边改了个接口参数,那边调用方就挂了;他那边升级了个依赖版本&…

2026/10/13 6:06:18 阅读更多 →

最新新闻

Agentic Engine工作空间:让AI工作流从个人经验沉淀为团队可复用的方法资产

Agentic Engine工作空间:让AI工作流从个人经验沉淀为团队可复用的方法资产

上个月,我们团队有个画面让我印象特别深:A同学花了两天调出一条多轮 Agent 工作流,能自动整理行业调研报告,跑完的结果相当能打。结果三天后,B同学在隔壁项目里需要做类似的事,翻遍共享盘也没找到可用的参考…

2026/10/14 10:47:56 阅读更多 →
Spring Boot整合Kafka实战:从环境搭建到可靠消息处理

Spring Boot整合Kafka实战:从环境搭建到可靠消息处理

前阵子帮某项目做技术升级,下单链路里有几步同步调用,高峰期每次请求都可能被下游拖到超时,线程池很快就堵满了。大家聊到解决方案时,几乎一致同意引入消息队列,而选型清单里排在最前面的就是Apache Kafka。Spring整合…

2026/10/14 10:47:56 阅读更多 →
Python列表操作完全指南:增删改查与实战技巧

Python列表操作完全指南:增删改查与实战技巧

1. Python列表操作完全指南:从增删改查到实战技巧作为一名Python开发者,列表(list)是我们日常编码中最常用的数据结构之一。掌握列表的各种操作方法不仅能提高编码效率,还能让代码更加优雅和高效。本文将全面解析Python列表的增删改查操作&am…

2026/10/14 10:47:56 阅读更多 →
SAP财务模块K/821报错处理与折旧码配置优化

SAP财务模块K/821报错处理与折旧码配置优化

1. SAP报错K/821问题解析与实战处理在SAP系统日常运维中,K/821属于典型的财务模块报错,通常出现在固定资产折旧运行(AFAB)或相关事务码执行过程中。这个报错直接关系到月结关账的顺利进行,我在处理某制造业客户年度资产…

2026/10/14 10:47:56 阅读更多 →
具身智能入门指南:从仿真到真机的软硬兼修学习路径

具身智能入门指南:从仿真到真机的软硬兼修学习路径

1. 具身智能到底在解决什么问题第一次听到“具身智能”这个词,很多人会下意识把它归到“机器人大模型”的范畴里,觉得无非是给机械臂接个语言模型,让它听懂人话去抓杯子。这个理解不算错,但只碰到了表皮。具身智能真正要啃的硬骨头…

2026/10/14 10:47:56 阅读更多 →
agent-skills:智能体技能化封装与复用实践指南

agent-skills:智能体技能化封装与复用实践指南

1. 从“agent-skills”这个标题说起:它到底在解决什么问题第一次看到“agent-skills”这个标题,我的直觉是:这大概率是一个围绕智能体能力封装、复用与编排的项目。事实也确实如此。过去一年里,我接触过不少团队在做智能体相关的落…

2026/10/14 10:46:54 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →