最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

前端JS異常捕獲與統(tǒng)一格式化的完整指南

 更新時間:2026年03月16日 09:16:41   作者:越重天  
在前端開發(fā)中,異常監(jiān)控是保證應(yīng)用穩(wěn)定性的重要一環(huán),本文將帶你深入理解?console.log(error)?的底層實現(xiàn),并給出一個通用的錯誤格式化方案,最后演示如何將格式化后的異常信息上報到后端,感興趣的小伙伴可以了解下

引言

在前端開發(fā)中,異常監(jiān)控是保證應(yīng)用穩(wěn)定性的重要一環(huán)。當(dāng)用戶遇到頁面白屏、功能不可用等問題時,如果能及時收集到詳細(xì)的錯誤信息(包括堆棧、行列號、瀏覽器環(huán)境等),就能快速定位并修復(fù) bug。瀏覽器提供了 window.onerrorunhandledrejection 兩個全局事件,分別用于捕獲未處理的 JavaScript 異常和未捕獲的 Promise 拒絕。然而,不同瀏覽器對這些事件的參數(shù)支持存在差異,錯誤對象的格式也各不相同,如何編寫一個兼容所有瀏覽器、并能像 console.log(error) 那樣輸出完整堆棧的格式化函數(shù),是搭建前端監(jiān)控系統(tǒng)的第一步。

本文將帶你深入理解 console.log(error) 的底層實現(xiàn),并給出一個通用的錯誤格式化方案,最后演示如何將格式化后的異常信息上報到后端。

為什么需要統(tǒng)一格式化

當(dāng)你在控制臺直接執(zhí)行 console.log(new Error('something wrong')) 時,瀏覽器會打印出類似這樣的信息:

Error: something wrong
    at <anonymous>:1:13
    at ...

但如果使用 window.onerror 捕獲,你拿到的參數(shù)可能只有消息、腳本 URL、行號、列號和一個可選的 error 對象。這些參數(shù)組合起來未必能還原出完整的堆棧。此外,unhandledrejectionreason 可能是任意類型(字符串、對象、Error 實例等),如何安全地提取信息并拼接成可讀的字符串,也需要仔細(xì)處理。

一個優(yōu)秀的異常上報方案應(yīng)該做到:

  • 完整性:盡可能包含錯誤名稱、消息、調(diào)用堆棧、發(fā)生位置(文件、行號、列號)。
  • 兼容性:支持所有主流瀏覽器(包括 IE9+)。
  • 健壯性:處理循環(huán)引用、非 Error 對象等特殊情況,避免二次異常。
  • 一致性:最終上報的字符串格式統(tǒng)一,便于后端解析或搜索。

console.log(error)的底層原理

在深入實現(xiàn)之前,我們先了解一下瀏覽器是如何打印錯誤對象的。以 Chrome 的 V8 引擎為例:

  • console.log 接收一個對象后,會調(diào)用該對象的 [Symbol.toStringTag] 或自定義的 inspect 方法(DevTools 擴展)。對于 Error 對象,V8 內(nèi)部會檢查其是否有 stack 屬性。
  • error.stack 是一個非標(biāo)準(zhǔn)但所有現(xiàn)代瀏覽器都支持的屬性,它包含了當(dāng)前調(diào)用棧的快照。這個堆棧字符串的生成依賴于 Error.captureStackTrace(Node.js 中)或運行時自動收集的調(diào)用幀。
  • 如果 error.stack 存在,瀏覽器直接輸出該字符串;否則,退而使用 error.toString()(通常是 "Error: message" 的形式)。

因此,要獲得與 console.log 相同的輸出,我們只需在全局事件中盡量獲取到 error.stack 即可。當(dāng)無法獲取 stack 時,再根據(jù)事件參數(shù)手動拼接位置信息。

統(tǒng)一錯誤格式化函數(shù)

下面是一個健壯的 formatError 函數(shù),它接受任意類型的錯誤值以及可選的 URL、行號、列號,返回格式化的錯誤字符串。

/**
 * 將任意錯誤值格式化為包含堆棧信息的字符串
 * @param {*} error - 錯誤對象或任意值
 * @param {string} fallbackMessage - 當(dāng)無法獲取有效信息時的備選消息
 * @param {string} [url] - 發(fā)生錯誤的腳本URL(從onerror獲取)
 * @param {number} [line] - 行號(從onerror獲?。?
 * @param {number} [col] - 列號(從onerror獲取)
 * @returns {string} 格式化后的錯誤字符串
 */
function formatError(error, fallbackMessage, url, line, col) {
  let result = '';

  // 情況1:error 是對象類型,嘗試提取 stack 或 message
  if (error && typeof error === 'object') {
    // 優(yōu)先使用 stack(包含完整的調(diào)用堆棧)
    if (typeof error.stack === 'string') {
      result = error.stack;
    }
    // 其次使用標(biāo)準(zhǔn) error 屬性(name 和 message)
    else if (typeof error.message === 'string') {
      const name = error.name || 'Error';
      result = `${name}: ${error.message}`;
    }
    // 否則嘗試 JSON 序列化(避免循環(huán)引用)
    else {
      try {
        result = JSON.stringify(error, null, 2);
      } catch (e) {
        // 序列化失?。ㄈ缪h(huán)引用),使用默認(rèn)字符串轉(zhuǎn)換
        result = String(error);
      }
    }
  } else {
    // 原始類型直接轉(zhuǎn)為字符串
    result = String(error);
  }

  // 情況2:結(jié)果中不包含行列信息(如只拿到 message),但通過 onerror 獲得了具體位置
  // 簡單判斷堆棧中是否已有類似 ":數(shù)字" 的行號標(biāo)記
  const hasLineInfo = /:\d+/.test(result);
  if (!hasLineInfo && url && line) {
    const location = `${url}:${line}${col ? ':' + col : ''}`;
    result = result ? `${result} at ${location}` : `Error at ${location}`;
  }

  // 情況3:仍然沒有有效內(nèi)容,使用 fallbackMessage
  if (!result && fallbackMessage) {
    result = fallbackMessage;
  }

  return result;
}

關(guān)鍵點說明

  • 優(yōu)先使用 error.stack:只要錯誤對象有 stack 屬性,就直接使用它,因為 stack 已經(jīng)包含了最完整的調(diào)用鏈和位置信息。
  • 降級使用 namemessage:如果對象是 Error 實例但 stack 可能被篡改或不存在,則拼接 name: message。
  • JSON 序列化兜底:對于普通對象(如 { code: 500, msg: 'fail' }),嘗試用 JSON.stringify 展示其結(jié)構(gòu),并捕獲循環(huán)引用異常。
  • 附加行列號:當(dāng)最終字符串中沒有明顯的數(shù)字位置(如 :10)且外部提供了 URL 和行號時,將位置信息附加到末尾。這可以彌補某些場景下 error.stack 缺失行列的不足。
  • fallbackMessage 參數(shù):當(dāng) error 為 undefined 或空值時,可以傳入默認(rèn)消息,例如 'Unhandled Rejection'。

全局監(jiān)聽器:window.onerror 和 unhandledrejection

有了格式化函數(shù),我們就可以在全局事件中調(diào)用它,并將結(jié)果上報。

window.onerror

window.onerror = function (message, source, lineno, colno, error) {
  const errorStr = formatError(error, message, source, lineno, colno);
  
  // 上報錯誤(示例:使用 sendToServer 函數(shù))
  sendToServer({
    type: 'onerror',
    message: message,
    stack: errorStr,
    url: source,
    line: lineno,
    column: colno,
    userAgent: navigator.userAgent,
    timestamp: Date.now()
  });

  // 返回 true 可以阻止瀏覽器默認(rèn)處理(如控制臺打印錯誤)
  // return true;
};

注意:舊版 IE(<=10)不會傳遞 error 參數(shù),此時 errorundefined,我們的 formatError 會使用 fallbackMessage(即 message)和行列號來構(gòu)造字符串。

unhandledrejection

window.addEventListener('unhandledrejection', function (event) {
  const reason = event.reason;
  const errorStr = formatError(reason, 'Unhandled Rejection');

  sendToServer({
    type: 'unhandledrejection',
    reason: errorStr,
    userAgent: navigator.userAgent,
    timestamp: Date.now()
  });

  // 可選:阻止默認(rèn)行為(某些瀏覽器會打印錯誤)
  event.preventDefault();
});

event.reason 可以是任何類型,我們的 formatError 已經(jīng)做了充分處理。

上報函數(shù)實現(xiàn)

最簡單的上報可以通過 navigator.sendBeaconfetch 發(fā)送到后端接口。為了不影響用戶體驗,建議使用 sendBeacon,它會在頁面卸載時也能確保請求發(fā)出。

function sendToServer(data) {
  // 避免頻繁上報(例如使用采樣率)
  if (Math.random() > 0.1) return; // 10% 采樣

  const url = 'https://your-monitor-server.com/api/error';
  const body = JSON.stringify(data);

  if (navigator.sendBeacon) {
    navigator.sendBeacon(url, body);
  } else {
    fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: body,
      keepalive: true // 類似 sendBeacon 的行為
    }).catch(() => {}); // 忽略 fetch 失敗
  }
}

兼容性深度解析

不同瀏覽器的window.onerror參數(shù)

瀏覽器messagesourcelinenocolnoerror
Chrome / Firefox / Safari / Edge (現(xiàn)代)??????????
IE 10+??????????(但可能為 null)
IE 9-????????

我們的 formatError 能夠適應(yīng)以上所有情況:當(dāng) error 不存在時,利用 message、source、lineno 構(gòu)造一個簡化版本。

堆棧格式差異

不同瀏覽器生成的 error.stack 格式略有不同,例如:

  • Chrome: Error: message\n at function (file:line:column)
  • Firefox: Error: message\n function@file:line:column
  • Safari: Error: message\n function@file:line:column
  • IE: Error: message\n at function (file:line:column)

這些格式差異通常不影響可讀性,我們的格式化函數(shù)直接保留原始 stack,不進行解析和重組,以保證信息不丟失。

完整示例代碼

將上述片段整合,得到一個完整的監(jiān)控模塊:

// error-monitor.js
(function() {
  'use strict';

  function formatError(error, fallbackMessage, url, line, col) {
    let result = '';
    if (error && typeof error === 'object') {
      if (typeof error.stack === 'string') {
        result = error.stack;
      } else if (typeof error.message === 'string') {
        const name = error.name || 'Error';
        result = `${name}: ${error.message}`;
      } else {
        try {
          result = JSON.stringify(error, null, 2);
        } catch (e) {
          result = String(error);
        }
      }
    } else {
      result = String(error);
    }

    const hasLineInfo = /:\d+/.test(result);
    if (!hasLineInfo && url && line) {
      const location = `${url}:${line}${col ? ':' + col : ''}`;
      result = result ? `${result} at ${location}` : `Error at ${location}`;
    }

    if (!result && fallbackMessage) {
      result = fallbackMessage;
    }

    return result;
  }

  function sendToServer(data) {
    // 采樣:僅上報 10% 的錯誤,可根據(jù)需要調(diào)整
    if (Math.random() > 0.1) return;

    const url = 'https://your-monitor-server.com/api/error';
    const body = JSON.stringify({
      ...data,
      userAgent: navigator.userAgent,
      timestamp: Date.now(),
      page: window.location.href
    });

    if (navigator.sendBeacon) {
      navigator.sendBeacon(url, body);
    } else {
      fetch(url, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: body,
        keepalive: true
      }).catch(() => {});
    }
  }

  window.onerror = function (message, source, lineno, colno, error) {
    const errorStr = formatError(error, message, source, lineno, colno);
    sendToServer({
      type: 'onerror',
      rawMessage: message,
      stack: errorStr,
      url: source,
      line: lineno,
      column: colno
    });
  };

  window.addEventListener('unhandledrejection', function (event) {
    const reason = event.reason;
    const errorStr = formatError(reason, 'Unhandled Rejection');
    sendToServer({
      type: 'unhandledrejection',
      stack: errorStr
    });
    event.preventDefault();
  });
})();

進階考慮

1. 去重與聚合

大量相同錯誤重復(fù)上報會浪費資源??梢栽谇岸司彺孀罱蠄蟮腻e誤指紋(如 error.stack 的哈希),短時間內(nèi)相同的錯誤不再發(fā)送。

2. 錯誤采樣

對于高流量的應(yīng)用,可以設(shè)置采樣率,只上報一部分錯誤,減輕服務(wù)器壓力。

3. 附加上下文

除了錯誤信息,還可以記錄用戶的登錄狀態(tài)、操作路徑、API 請求參數(shù)等,幫助復(fù)現(xiàn)問題。

4. 跨域腳本的堆棧

如果引用了 CDN 上的腳本,錯誤堆棧中可能只有 Script error. 而沒有詳細(xì)信息。需要為腳本添加 crossorigin="anonymous" 屬性,并確保服務(wù)器響應(yīng)頭包含 Access-Control-Allow-Origin。

總結(jié)

本文從 console.log(error) 的底層原理出發(fā),設(shè)計了一個兼容所有瀏覽器的錯誤格式化函數(shù),并結(jié)合 window.onerrorunhandledrejection 實現(xiàn)了全局異常捕獲與上報。這個方案能夠像原生控制臺一樣輸出完整的錯誤堆棧,同時處理了各種邊界情況(非 Error 對象、舊版 IE、循環(huán)引用等)。將此模塊集成到項目中,你就擁有了一個可靠的前端監(jiān)控基礎(chǔ),為后續(xù)的故障排查和數(shù)據(jù)分析奠定堅實的基礎(chǔ)。

前端異常監(jiān)控并非一勞永逸,還需要不斷優(yōu)化上報策略、豐富上下文信息,以及結(jié)合后端分析工具形成閉環(huán)。但至少,從今天開始,你不再對用戶的錯誤一無所知。

到此這篇關(guān)于前端JS異常捕獲與統(tǒng)一格式化的完整指南的文章就介紹到這了,更多相關(guān)前端異常捕獲與統(tǒng)一格式化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JavaScript實現(xiàn)大數(shù)的運算

    JavaScript實現(xiàn)大數(shù)的運算

    js的'MAX_SAFE_INTEGER'是9007199254740991,而'MIN_SAFE_INTEGER'為-9007199254740991,那么如何實現(xiàn)一些特別大的數(shù)目相加?今天我們就來探討下
    2014-11-11
  • js漢字轉(zhuǎn)拼音實現(xiàn)代碼

    js漢字轉(zhuǎn)拼音實現(xiàn)代碼

    漢字轉(zhuǎn)拼音,比較娛樂的一款應(yīng)用,感興趣的朋友可以了解下,或許對你學(xué)習(xí)js有所幫助
    2013-02-02
  • 基于javascript的無縫滾動動畫實現(xiàn)2

    基于javascript的無縫滾動動畫實現(xiàn)2

    這篇文章主要介紹了基于javascript的無縫滾動動畫實現(xiàn)2,文章通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • JS實現(xiàn)的加減乘除四則運算計算器示例

    JS實現(xiàn)的加減乘除四則運算計算器示例

    這篇文章主要介紹了JS實現(xiàn)的加減乘除四則運算計算器,涉及javascript事件響應(yīng)及數(shù)學(xué)運算相關(guān)操作技巧,需要的朋友可以參考下
    2017-08-08
  • JS?try?catch基本用法以及常見的異常處理

    JS?try?catch基本用法以及常見的異常處理

    JS異常處理的作用是幫助開發(fā)者識別和處理運行時的錯誤和異常情況,確保程序在出現(xiàn)問題時能夠優(yōu)雅地降級或恢復(fù),而不是導(dǎo)致整個應(yīng)用崩潰或產(chǎn)生不可預(yù)測的行為,這篇文章主要介紹了JS?try?catch基本用法以及常見的異常處理,需要的朋友可以參考下
    2025-04-04
  • js 數(shù)組詳細(xì)操作方法及解析合集

    js 數(shù)組詳細(xì)操作方法及解析合集

    在開發(fā)中,數(shù)組的使用場景非常多,平日中也涉及到很多數(shù)組的api/相關(guān)操作,一直也沒有對這塊內(nèi)容進行一塊整理總結(jié),很多時候就算用過幾次這個api,在開發(fā)中也很容易忘記,還是要谷歌一下
    2018-06-06
  • 前端JS大文件上傳失敗問題深度解析和完美解決方案

    前端JS大文件上傳失敗問題深度解析和完美解決方案

    在當(dāng)今數(shù)字化時代,視頻內(nèi)容已成為信息傳遞的主要載體,我們經(jīng)常需要處理各種大小的視頻文件,本文將通過一個真實的技術(shù)支持案例,深入分析大文件上傳過程中的各種陷阱,并提供從基礎(chǔ)到高級的完整解決方案,大家可以根據(jù)需要進行選擇
    2025-11-11
  • 功能強大的Bootstrap組件(結(jié)合js)

    功能強大的Bootstrap組件(結(jié)合js)

    這篇文章主要介紹了功能強大的Bootstrap組件,介紹js結(jié)合Bootstrap組件的使用方法,感興趣的小伙伴們可以參考一下
    2016-08-08
  • 12種JavaScript常用的MVC框架比較分析

    12種JavaScript常用的MVC框架比較分析

    這篇文章主要介紹了12種JavaScript常用的MVC框架比較分析,以獨特的視角分析了12中常見的JavaScript MVC框架各種優(yōu)缺點,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-11-11
  • 利用pixi.js制作簡單的跑酷小游戲

    利用pixi.js制作簡單的跑酷小游戲

    PixiJS 提供一個適用于所有設(shè)備的快速輕量級 2D 庫。PixiJS 具有完整的 WebGL 支持,并且可以無縫地回退到 HTML5 的畫布。 本文將使用pixi.js制作簡單的跑酷小游戲,感興趣的可以嘗試一下
    2022-07-07

最新評論

上饶市| 鄂尔多斯市| 宝鸡市| 大同县| 曲沃县| 临汾市| 泸西县| 顺昌县| 鞍山市| 淮安市| 绵竹市| 屯留县| 陕西省| 临海市| 五华县| 西乌| 邵武市| 确山县| 玉山县| 寿宁县| 乌拉特中旗| 汾阳市| 延川县| 高邑县| 富宁县| 辉县市| 珲春市| 彭水| 林甸县| 青龙| 泸西县| 洞口县| 资溪县| 安康市| 宜州市| 广南县| 常熟市| 德江县| 巨鹿县| 黄平县| 栾川县|