真正起作用的是“延迟求值”逻辑——把高开销计算包装成可调用对象,在日志真正需要输出时才执行;封装的作用是让这种延迟行为更清晰、更可控、更易复用。

面向对象的属性封装本身不能直接避免字符串拼接内耗,真正起作用的是“延迟求值”逻辑——把高开销计算包装成可调用对象,在日志真正需要输出时才执行。封装的作用是让这种延迟行为更清晰、更可控、更易复用。
用属性+惰性计算替代即时拼接
不要在日志语句里直接调用耗时方法或拼接字符串,而是把计算逻辑藏进类的属性或方法中,配合日志框架的占位符机制。
- 定义一个日志上下文类,把 expensive_data 封装为 property,内部加缓存或条件触发
- 调用时只传 self.expensive_data(实际是触发 getter),但前提是 logger.debug("Data: {}", obj.expensive_data) 中的 {} 占位符已启用延迟求值
- 若该属性返回的是 lambda 或 Supplier(如 Python 的 functools.partial 或 Java 的 Supplier
),效果更稳
封装日志方法,内置级别预检
在自定义 Logger 类中,把日志写入逻辑和开关判断合为一体,避免外部重复判断。
- 提供 debug_with_expensive() 方法:先 checkEnabled(DEBUG),再执行昂贵参数生成,最后调用原生 logger.debug()
- 对不同场景封装专用方法,比如 logUserAccess(user) 内部只在 DEBUG 启用时才调用 user.toJson() 或 user.getFullTrace()
- 避免暴露原始 logger 接口,防止使用者绕过封装直接写 logger.debug("user=" + user.toString())
结构化日志 + 属性字典封装
不拼字符串,改用字典传参,由日志处理器统一格式化。属性封装可用来构建这个字典。
- 定义 LogEvent 类,含 user_id、action、duration_ms 等字段,重写 __str__ 或提供 to_dict() 方法
- 日志调用变成 logger.info("Action performed", extra=event.to_dict()),完全规避拼接
- 搭配 structlog 或 loguru 时,extra 字典会自动扁平化或嵌套输出,且所有字段仅在日志启用时序列化
警惕封装带来的假安全
封装不等于优化。如果封装体内部仍强制执行字符串操作,性能问题照旧。
- 避免在 property getter 中做 JSON 序列化、远程调用、大对象遍历——这些应进一步外包给 Supplier 或异步任务
- 不要把 user.getName() 写成 @property def name(self): return self._user.name,然后 logger.debug(f"User {self.name}") —— f-string 仍会立即求值
- 真正安全的写法是 logger.debug("User {}", lambda: self._user.name),并确保日志框架支持 Supplier











