日志装饰器需严格保持原函数签名不变,预制LogFunc1/LogFunc2E等通用包装器;用logrus/zap结构化打日志,带上下文字段、起止时间、参数快照与耗时;不recover panic,仅记录不兜底;支持开关与采样控制。
直接用装饰器包一层,保持原函数签名完全不变,就能注入日志逻辑。核心不是加功能,而是不惊动调用方——别人还在写
,你不能让它突然报错说“找不到这个函数”。
必须严格匹配原函数签名
装饰器返回的包装函数,参数类型、数量、顺序和返回值类型,要和原函数一模一样。比如原函数是
,包装后也得是这个签名。否则无法赋值给同类型变量,也无法传给期望该类型的接口或回调。
别用
动态适配所有签名:运行慢、无编译检查、调试困难
推荐按常用模式预制几个通用包装器,如
(1入1出)、
(2入+error返回)等,靠 IDE 补全快速复用
Python 中用
可覆盖多数情况;Go 中需为每类签名单独写,这是权衡后的稳态方案
用结构化日志替代 print 或 log.Printf
只打一句
很快会失效。真实场景需要字段可查、上下文可追溯、错误可定位。
优先选用
或
,用
带上下文打日志
Go 中注意
非并发安全,包装器里应一次性构造子 logger,不要反复
后丢弃
记录起止时间、参数快照、返回值(或错误)、耗时,这些信息要作为结构体字段输出,而非拼接字符串
不处理 panic,也不静默吞异常
日志装饰器只负责“记发生了什么”,不是兜底层。加
看似健壮,实则埋雷。
避免在包装器里写
—— 会吃掉 panic,上层收不到错误,HTTP 返回 200 却没做事
真要捕获 panic,至少打印完整 stack trace:
,否则
返回的
几乎没法排查
panic 处理应该放在真正能响应的位置,比如 HTTP handler 中间件、goroutine 启动入口,而不是每个业务函数包装器里
支持开关与采样,尤其在线上环境
日志不是越多越好。高频函数全量打日志,可能压垮磁盘或日志服务。
提供全局开关,比如环境变量
或配置项一键关闭
对高频率函数启用采样,例如每 100 次调用只记录 1 次:
关键路径(如支付、登录)默认全量;后台任务、健康检查等可设低频或关闭
process(u)func(id int, name string) (bool, error)reflectLogFunc1LogFunc2E*args, **kwargs"calling GetUser 123"logruszapWithField("user_id", u.ID).WithField("trace_id", tid)zap.LoggerWith()recover()defer func() { recover() }debug.PrintStack()recover()interface{}LOG_ENABLED=falseif rand.Intn(100) == 0 { logger.Info(...) }