生产上的问题你不会用 sourcemap 定位吗?(一)

简介: 生产上的问题你不会用 sourcemap 定位吗?

image.png


前言


上周在群里突然被 @ 要我查看生产上出现的问题,由于这个项目比较老 (React15),既没有埋点也没有接入错误监控,于是会得到如下的这样一个提示信息(以下错误是本地模拟的实际生产效果):

image.png

当你想要直接点击链接定位时,就会看到如下的形式:

image.png

这怎么说呢?不能说一模一样,只能说毫不相关(表面上

针对老项目再想加入埋点和错误监控可能就比较困难了,特别是当看到其中各种无法让你理解的写法和逻辑,加之不同开发人员的迭代开发,一直在往上堆 x 山保持优雅,该打码就打码),就更加没法做什么进一步的优化了,只能说能跑就行,不敢动,根本就不敢动!

那么还剩下的能够快速帮助我们定位详细错误信息的方式是什么呢?没错就是本文的主角 sourcemap,这也是为什么会有本篇与 sourcemap 相关的文章。


文中若存在不正确之处,可在评论区斧正!!!

sourcemap 的使用和规则

sourcemap 是什么?

简单的说,就是一个以 .map 为后缀的文件,例如:

image.png

.map 文件里面的内容以 json 形式存储了 源代码 打包转换后的位置信息,核心内容如下:

  • version:版本号,目前 sourcemap 标准的版本为 3
  • file:指打包构建后的 文件名,即 bundle 文件名
  • sources:指当前这个 bundle 文件所包含的所有 源码文件,因为存在分包等优化策略,一个 bundle 文件可能会包含 多个源码文件 的内容
  • sourcesContent:指上述 sources 中每个 源码文件 所对应的源码内容字符
  • names:指在代码在经历 混淆压缩 之前的 变量名,这个变量名包含 导入模块名常用方法名
  • mappings:直接进行翻译就是 映射 的意思,即根据以上信息实现的源码代码位置和构建产物之间的一一映射关系
  • sourceRoot:指源码目录
    image.png

sourcemap 有啥用?

上面已经提到了 .map 文件中以 json 形式存储的数据内容,就是包含着源代码与构建产物之间的映射关系,那么它的作用自然就是实现:运行时代码开发时代码 都能拥有相同准确的信息提示

常见的 开发时代码提示,如下:

image.png

常见的 运行时代码提示,如下:

image.png

可见 运行时代码提示 的代码提示信息不够详细准确,而且以上只是简单的模拟了运行时错误,而实际项目中大多数的运行时错误是在不同的场景下才会出现的,而此时由于处于生产环境中,在排查异常代码时就会体现出限制:

  • 首先,运行时代码 和 开发时代码 不一致,导致错误信息也不同
  • 其次,运行时代码 很难通过 debug 的方式进行调试

主要原因就是 开发时代码运行时代码 的转变都需要经历以下几个处理阶段:

  • 代码压缩,为了减小运行时代码的体积,会将源代码中的 换行符、无意义空格 等进行删除,使得代码紧凑在一起
  • 代码混淆,实际上是指将源代码转换成一种功能上等价,但是难于阅读和理解的形式,例如开发时代码中定义的 "见名知意" 的 函数名、模块名、变量名 等转换为类似 "a、b、c、..." 等无意义的名字,使得即使运行时代码被人获取,也难以猜测其作用
  • 代码分块(chunk split),在现代前端构建工具(webpack、vite 等)中都支持将多个源代码文件合并成一个 bundle 文件,目的就是减少 http 请求数量,以实现优化效果

因此,sourmap 就可以在这些处理阶段中构建出 运行时代码 和 开发时代码 代码的映射关系,使得运行时代码也能够像开发时代码一样提供给我们详细而准确的信息,帮助我们在生产环境中也能够快速定位到源代码中的位置。

如何快速生成 sourcemap?

前端构建工具很多,这里只列举最常用的两个:vitewebpack

vite 生成 sourcemap

vite 中只要通过设置 build.sourcemap 的选项配置即可,值类型包括:

  • boolean:true | false
  • 其默认值为 false,当设置为 true 时,就会生成单独的 .map 文件,并且在对应的 bundle 文件中通过 注释 来指明对应的 .map 文件,如下:
  • image.png
  • 'inline'
  • 指定为该值 source map 将作为一个 data URI 附加在输出文件中,如下:
  • image.png
  • 'hidden'
  • 'hidden' 的工作原理与 'true' 相似,只是 bundle 文件中相应的注释将不被保留

webpack 生成 sourcemap

webpack 中只要通过设置 devtool 的选项配置即可,值类型包括以下类型的 组合

  • (none) 默认值
  • eval
  • 会生成被 eval 函数包裹的模块内容,并在其中通过注释来注明是源文件位置,其中的 sourceUrl 是用来来指定文件名
  • 优点就是快,因为不用生成 .map 文件,并且 运行时代码 映射到 开发时代码 只需要提供对应的 源文件地址
  • 缺点就是包含映射信息少,并且 eavl 函数因为安全性问题也是不建议使用的
  • image.png
  • source-map
  • 会生成单独的 .map 文件包含 version、file、sources、sourcesContent、names、mappings、sourceRoot 等信息,需要进行 mapping 和 编码 工作
  • 优点就是拥有单独的 .map 文件,使得 运行时代码 体积不会过大,并且能够提供详细的信息,包含文件名、行、列等信息
  • 缺点就是慢,因为需要额外生成 .map 文件,并且随着模块内容的增多整体速度就越慢
  • image.png
  • image.png
  • cheap
  • source-map 的方式不同,cheap 只会映射到源码的 行信息,即它 不会生成源码的 列信息,也不包含 loadersourcemap,因此相对来说会比 source-map 的方式更快
  • 优点就是速度更快,只映射到源码的 行信息 的原因是:通常在进行错误定位时,大多数情况下只需要关注到 就可以知道错误原因,而很少会关注到 ,因此列信息其实不是必要性的
  • 缺点就是映射信息会不够精确,因为一个文件可能会经过不同 loader 的处理,而它又不生成 loader 相关的 sourcemap,自然会导致最终产物的信息不够精确
  • module
  • module 的方式生成的 sourcemap 就会包含和 loader 相关的 sourcemap 信息
  • 需要 loader 相关的 sourcemap 信息的原因在于:当一个文件被多个 laoder 依次进行转换处理后,其内容会发生不同的变化(例如:新内容的行列 和 源代码的行列 信息不一致),就会使得我们无法去调试最初始的代码内容
  • inline
  • 顾名思义,就是会将原本生成的 .map 文件的内容作为 DataURL(base64 形式 嵌入 bundle 文件中,不单独生成 .map 文件
  • image.png
  • hidden
  • 会生成单独的 .map 文件,但是相比于 source-map 的形式,其会在对应的 bundle 文件中隐藏 sourceMappingURL 的路径
  • image.png
  • nosources
  • source-map 生成的 .map 文件中的 sourceContent 存储的是源码内容,这样的好处是既可以根据文件路径来映射,也可以根据这部分内容来映射,换句话说 source-map 提供了双重保险,但也增加了 .map 文件体积
  • nosources 则是在能够保证文件路径可以准确建立映射的情况下,就可以把 sourceContent 的内容给去除掉,使得 .map 文件体积能够更小一些
  • image.png
  • image.png

以上和 webpack 相关的 devtool 的配置内容,evalsource-map 都可以单独使用,也可以组合使用,但 module、inline、hidden、nosources、cheap 的方式一定是包含 source-map 的内容的,如果你记不住或写错了,webpack 会给你相应的提示信息:

image.png

不同环境的 sourcemap 怎么选?

这里的环境指的就是 开发环境生产环境,由于不同的组合方式在 构建重构建 时的速度不同,另外还需要考虑 .map 文件在线上可能带来的风险问题,因此必须要 权衡 使用 sourcemap 的组合方式,好在 webpack 文档中给我们提供给了相应的组合方式,如下:

devtool performance production quality comment
(none) build: fastest rebuild: fastest yes bundle Recommended choice for production builds with maximum performance.
eval build: fast rebuild: fastest no generated Recommended choice for development builds with maximum performance.
eval-cheap-source-map build: ok rebuild: fast no transformed Tradeoff choice for development builds.
eval-cheap-module-source-map build: slow rebuild: fast no original lines Tradeoff choice for development builds.
eval-source-map build: slowest rebuild: ok no original Recommended choice for development builds with high quality SourceMaps.
cheap-source-map build: ok rebuild: slow no transformed
cheap-module-source-map build: slow rebuild: slow no original lines
source-map build: slowest rebuild: slowest yes original Recommended choice for production builds with high quality SourceMaps.
inline-cheap-source-map build: ok rebuild: slow no transformed
inline-cheap-module-source-map build: slow rebuild: slow no original lines
inline-source-map build: slowest rebuild: slowest no original Possible choice when publishing a single file
eval-nosources-cheap-source-map build: ok rebuild: fast no transformed source code not included
eval-nosources-cheap-module-source-map build: slow rebuild: fast no original lines source code not included
eval-nosources-source-map build: slowest rebuild: ok no original source code not included
inline-nosources-cheap-source-map build: ok rebuild: slow no transformed source code not included
inline-nosources-cheap-module-source-map build: slow rebuild: slow no original lines source code not included
inline-nosources-source-map build: slowest rebuild: slowest no original source code not included
nosources-cheap-source-map build: ok rebuild: slow no transformed source code not included
nosources-cheap-module-source-map build: slow rebuild: slow no original lines source code not included
nosources-source-map build: slowest rebuild: slowest yes original source code not included
hidden-nosources-cheap-source-map build: ok rebuild: slow no transformed no reference, source code not included
hidden-nosources-cheap-module-source-map build: slow rebuild: slow no original lines no reference, source code not included
hidden-nosources-source-map build: slowest rebuild: slowest yes original no reference, source code not included
hidden-cheap-source-map build: ok rebuild: slow no transformed no reference
hidden-cheap-module-source-map build: slow rebuild: slow no original lines no reference
hidden-source-map build: slowest rebuild: slowest yes original no reference. Possible choice when using SourceMap only for error reporting purposes.

sourcemap 如何生效?

要使得 sourcemap 发挥作用,单单只是生成对应的映射规则还不够,还需要一个 解析工具 负责将 源代码sourcemap 规则真正进行映射,通常这个解析工具是 浏览器异常监控系统(如:sentry)手动映射

浏览器

通常在现代浏览器中基本上会默认启用 sourcemap 映射功能,即只要对应的 bundle 文件中有 sourceMappingURLsourceURL 等指向的注释内容即可,手动开启位置如下(大同小异):

Google 浏览器

image.png

image.png

Firefox 浏览器

image.png


目录
相关文章
|
6月前
|
前端开发 JavaScript
cypress 如何定位元素?
cypress 如何定位元素?
157 0
cypress 如何定位元素?
|
JavaScript 应用服务中间件 nginx
vuecli3打包项目上线之后报错怎么使用本地的sourcemap文件定位调试?
vuecli3打包项目上线之后报错怎么使用本地的sourcemap文件定位调试?
170 0
|
2月前
|
前端开发 JavaScript 开发工具
前端项目增加eslint全过程
如何在前端项目中安装并配置ESLint和Prettier,包括VSCode插件的安装、npm包的全局安装、.eslintrc.js配置文件的生成以及编辑器设置的调整。
42 6
|
6月前
|
测试技术 Android开发 索引
XPath定位如何在App自动化测试中大显神威
本文介绍了如何在Appium中使用XPath进行自动化App测试。通过淘宝App实例,展示了XPath在定位元素上的应用,包括基础定位(如通过text、resource-id、class和content-desc属性),contains模糊定位,组合定位以及层级定位(如父、子、兄弟和祖元素定位)。XPath的灵活性和强大功能使得在Appium中高效地定位元素成为可能,从而提升移动应用的测试效率。
|
6月前
|
安全 算法 JavaScript
安卓逆向 -- 关键代码定位与分析技术
安卓逆向 -- 关键代码定位与分析技术
153 0
|
存储 缓存 安全
App极限瘦身 | 动态下发so(1)
App极限瘦身 | 动态下发so
281 0
App极限瘦身 | 动态下发so(1)
|
存储 编解码 监控
生产上的问题你不会用 sourcemap 定位吗?(二)
生产上的问题你不会用 sourcemap 定位吗?
597 0
|
移动开发 缓存 安全
App极限瘦身 | 动态下发so(2)
App极限瘦身 | 动态下发so
176 0
生产环境日志还用notepad++?难怪定位慢,UltraEdit了解一下
最近这两天运营部门反馈有会员的会员卡开卡时间与相关页面显示的开卡时间不一致,需要定位确定哪个时间是准确的。对于线上数据异常问题一定要谨慎处理,因为很有可能会是用户的某些非常规操作导致部分场景漏测。
生产环境日志还用notepad++?难怪定位慢,UltraEdit了解一下
|
存储 运维 监控
如何定位线上问题?
「你是怎么定位线上问题的?」 这个面试题我在两年社招的时候遇到过,前几天面试也遇到了。我觉得我每一次都答得中规中矩,今天来梳理复盘下,下次又被问到的时候希望可以答得更好。
151 0