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

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

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

引言

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

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

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

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

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

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

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

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

console.log(error)的底層原理

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

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

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

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

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

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

  // 情況1:error 是對(duì)象類型,嘗試提取 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) {
        // 序列化失敗(如循環(huán)引用),使用默認(rèn)字符串轉(zhuǎn)換
        result = String(error);
      }
    }
  } else {
    // 原始類型直接轉(zhuǎn)為字符串
    result = String(error);
  }

  // 情況2:結(jié)果中不包含行列信息(如只拿到 message),但通過(guò) onerror 獲得了具體位置
  // 簡(jiǎn)單判斷堆棧中是否已有類似 ":數(shù)字" 的行號(hào)標(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:仍然沒(méi)有有效內(nèi)容,使用 fallbackMessage
  if (!result && fallbackMessage) {
    result = fallbackMessage;
  }

  return result;
}

關(guān)鍵點(diǎn)說(shuō)明

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

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

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

window.onerror

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

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

注意:舊版 IE(<=10)不會(huì)傳遞 error 參數(shù),此時(shí) errorundefined,我們的 formatError 會(huì)使用 fallbackMessage(即 message)和行列號(hào)來(lái)構(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)行為(某些瀏覽器會(huì)打印錯(cuò)誤)
  event.preventDefault();
});

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

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

最簡(jiǎn)單的上報(bào)可以通過(guò) navigator.sendBeaconfetch 發(fā)送到后端接口。為了不影響用戶體驗(yàn),建議使用 sendBeacon,它會(huì)在頁(yè)面卸載時(shí)也能確保請(qǐng)求發(fā)出。

function sendToServer(data) {
  // 避免頻繁上報(bào)(例如使用采樣率)
  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 不存在時(shí),利用 messagesource、lineno 構(gòu)造一個(gè)簡(jiǎn)化版本。

堆棧格式差異

不同瀏覽器生成的 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,不進(jìn)行解析和重組,以保證信息不丟失。

完整示例代碼

將上述片段整合,得到一個(gè)完整的監(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) {
    // 采樣:僅上報(bào) 10% 的錯(cuò)誤,可根據(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();
  });
})();

進(jìn)階考慮

1. 去重與聚合

大量相同錯(cuò)誤重復(fù)上報(bào)會(huì)浪費(fèi)資源。可以在前端緩存最近上報(bào)的錯(cuò)誤指紋(如 error.stack 的哈希),短時(shí)間內(nèi)相同的錯(cuò)誤不再發(fā)送。

2. 錯(cuò)誤采樣

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

3. 附加上下文

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

4. 跨域腳本的堆棧

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

總結(jié)

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

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

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

相關(guān)文章

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

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

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

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

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

    基于javascript的無(wú)縫滾動(dòng)動(dòng)畫(huà)實(shí)現(xiàn)2

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

    JS實(shí)現(xiàn)的加減乘除四則運(yùn)算計(jì)算器示例

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

    JS?try?catch基本用法以及常見(jiàn)的異常處理

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

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

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

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

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

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

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

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

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

    利用pixi.js制作簡(jiǎn)單的跑酷小游戲

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

最新評(píng)論

萝北县| 自治县| 涟源市| 贡嘎县| 广德县| 萨迦县| 云安县| 南城县| 来安县| 平邑县| 东宁县| 赣州市| 乐业县| 台前县| 牡丹江市| 沁水县| 阿合奇县| 正宁县| 资阳市| 连城县| 徐汇区| 平乐县| 葫芦岛市| 丽江市| 通江县| 平谷区| 前郭尔| 丹凤县| 南陵县| 志丹县| 瑞昌市| 揭西县| 贵定县| 广德县| 永兴县| 华池县| 鸡西市| 尚志市| 兴安县| 陇川县| 唐山市|