You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

动态脚本栈追踪:如何识别抛出异常的目标动态脚本?

如何让动态注入脚本的栈追踪信息明确标识来源脚本

假设我们有两个本地生成的动态脚本:

const scripts = [
    "console.log(new Error().stack)",
    "console.log(new Error().stack)"
];

目前常用的两种运行方式:

for(let i = 0; i < scripts.length; ++i) {
     (new Function(scripts[i]))();
     // 或
     let script = document.createElement("script");
     script.textContent = scripts[i];
     document.body.append(script);
}

在Firefox浏览器中,这两种方式生成的栈追踪信息无法区分脚本来源:

  • new Function():anonymous@....html line ... > Function:3:12
  • body.append():@....html line ... > injectedScript:1:12

这导致当某一动态脚本抛出异常时,无法通过栈追踪信息定位具体是哪个脚本引发的问题。

现有一种粗糙的临时方案:通过构建嵌套Function并添加空行来区分,代码如下:

new Function(`
     ${"".padEnd(scriptID, "\n")}
     new Function("${script}");
`)

Firefox中生成的栈追踪信息为:... > Function line ${offset+scriptID} > Function:3:13,但该方案实现不够优雅,以下是几种更优的实现方式:

更优实现方案

1. 给异常附加自定义标识

直接在每个动态脚本的执行逻辑中添加异常捕获,给异常对象附加脚本ID,无需依赖栈追踪的行号hack:

function runScriptWithId(scriptCode, scriptId) {
  try {
    const wrappedCode = `
      try {
        ${scriptCode}
      } catch (e) {
        e.scriptId = ${JSON.stringify(scriptId)};
        throw e;
      }
    `;
    (new Function(wrappedCode))();
  } catch (e) {
    console.error(`脚本[${e.scriptId}]抛出异常`, e);
  }
}

// 使用示例
scripts.forEach((code, idx) => runScriptWithId(code, `script-${idx}`));

这种方式定位异常更直接,异常对象会明确携带脚本标识。

2. 为动态脚本添加源映射(Source Map)

这是最规范的方案,通过生成简易的源映射,让浏览器栈追踪直接显示自定义的脚本名称:

function runScriptWithSourceMap(scriptCode, scriptName) {
  // 构建带源映射注释的脚本内容
  const sourceMap = btoa(JSON.stringify({
    version: 3,
    file: scriptName,
    sources: [scriptName],
    names: [],
    mappings: ';' // 仅做名称映射,无需精确行号对应
  }));
  const scriptWithMap = `
    ${scriptCode}
    //# sourceMappingURL=data:application/json;base64,${sourceMap}
  `;

  const scriptEl = document.createElement('script');
  scriptEl.textContent = scriptWithMap;
  document.body.appendChild(scriptEl);
}

// 使用示例
scripts.forEach((code, idx) => runScriptWithSourceMap(code, `dynamic-script-${idx}.js`));

在Firefox中,栈追踪会直接显示自定义的脚本名称(如dynamic-script-0.js),彻底解决来源区分问题。

3. 用命名函数包装动态脚本

如果仅需要简单区分,可将动态代码包裹在命名函数中,让栈追踪显示函数名称:

function runNamedScript(scriptCode, scriptName) {
  // 处理脚本名称,确保符合标识符规则
  const validFuncName = scriptName.replace(/[^a-zA-Z0-9_]/g, '_');
  const wrappedCode = `
    function ${validFuncName}() {
      ${scriptCode}
    }
    ${validFuncName}();
  `;
  (new Function(wrappedCode))();
}

// 使用示例
scripts.forEach((code, idx) => runNamedScript(code, `dynamic_script_${idx}`));

栈追踪信息会显示命名函数的名称(如dynamic_script_0@...),快速区分脚本来源。

内容的提问来源于stack exchange,提问作者Denis Migdal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 18:45:09