在 SAP 生态里混过几年的前端,大概没人敢说没用过 Log.js。不管是调试一个 XMLTemplateProcessor 的解析问题,还是排查某个 Model 更新顺序的诡异 bug,第一反应基本都是打开控制台看 sap/base/Log 输出的日志。但这个每天被调用无数次的模块,内部到底是怎么设计的,可能很多人没细究过。
我最近正好把 Log.js 的源码从头到尾过了一遍,越看越觉得这东西比想象中有意思。它表面上只是个日志工具,实际上却是一个集状态管理、缓存策略、订阅发布、多端适配于一体的基础设施模块。这篇就当是我的源码阅读笔记,结合我自己在实际项目里踩过的坑,把 Log.js 的核心机制、关键 API 实现思路,以及一个很常见的 log.js:72 [echarts] can't get dom width or height 报错背后的排查逻辑,一次讲清楚。
1. 先搞清楚一件事:Log.js 到底在管什么
1.1 日志不是 console.log 的简单 wrapper
很多前端开发者对日志模块的理解就是“给 console.log 套个壳”,加个时间戳、加个级别前缀,完事。但 UI5 的 Log.js 显然不是这个思路。它更像是一个 日志数据中心,除了把日志输出到控制台,它还维护了一个内存缓冲区,供框架内部在运行时随时检索。比如有些问题在页面加载早期就发生了,等你打开控制台想复现时可能已经看不到完整链路,UI5 之所以能通过 Log.getLogEntries() 把历史日志捞回来,靠的就是这个缓冲区。
这也解释了为什么 Log.js 被归类在 sap/base/ 目录下——它是整个 UI5 框架的基础设施,几乎所有模块都会依赖它,所以它的设计必须足够轻量、稳定、无副作用,不能反向依赖任何 UI 组件。
1.2 两种模块形态:老版本与新版本
如果你看老一些的 UI5 项目,可能会看到 jQuery.sap.log 这种调用方式。这是 UI5 1.x 早期基于 jQuery 封装的日志接口,在 1.58 左右开始逐步被新的 sap/base/Log 替代。新模块是 ES2015 风格的纯 JavaScript 模块,不再强依赖 jQuery,这也是 UI5 走向现代前端架构的关键一步。
从源码阅读的角度,我建议你直接看 sap/base/Log.js,也就是新版实现。它更纯粹,没有 jQuery 兼容的历史包袱,而且模块内部大量的细节处理(比如 console 对象缺失、浏览器兼容性、回调函数异常隔离)都非常值得学习。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 源码骨架:闭包里的那些私货
2.1 模块结构与私有状态
打开 sap/base/Log.js,第一眼会看到一个标准的 IIFE(立即执行函数表达式),外层函数返回一个 Log 对象。这样做的目的很单纯:所有核心状态都锁在闭包里,外部只能通过暴露的 API 间接操作。这也是一种非常经典的“单体模式”实现,UI5 框架内大量模块都是这种风格。
模块内部的私有变量大致如下:
javascript复制var iLevel = 3; // 当前日志级别,默认 ERROR
var aLog = []; // 日志缓冲区,存所有历史条目
var bLogSupport = false; // 浏览器是否支持 console
var fOnLog = null; // 日志监听回调
var sDefaultComponent = ""; // 默认组件标识
var iMaxEntries = 500; // 缓冲区最大条数
这里有个细节值得注意:iLevel 的默认值是 3(即 ERROR),也就是说在不显式调用 Log.setLevel() 的情况下,INFO、WARNING、DEBUG 这些级别的日志是不会出现在输出里的。很多新手在调 bug 时发现 console 一片干净,以为是代码没执行,其实只是日志级别挡住了。
2.2 日志级别的业务语义
UI5 的日志级别不是一个简单的枚举,它背后有明确的业务语义:
| 级别 | 数值 | 语义 |
|---|---|---|
| FATAL | 0 | 致命错误,程序无法继续运 |
