詳解JavaScript錯誤捕獲和上報流程

 

 

 

 

怎麼捕獲錯誤並且處理,是一門語言必備的知識。在JavaScript中也是如此。

那怎麼捕獲錯誤呢?初看好像很簡單,try-catch就可以了嘛!但是有的時候我們發現情況卻繁多複雜。

  • Q1: 同步可以try-catch,但一個異步回調,比如setTimeOut里的函數還可以try-catch嗎?

  • Q2: Promise的錯誤捕獲怎麼做?

  • Q3: async/await怎麼捕獲錯誤?

  • Q4: 我能夠在全局環境下捕獲錯誤並且處理嗎?

  • Q5: React16有什麼新的錯誤捕獲方式嗎?

  • Q6: 捕獲之後怎麼上報和處理?

 

問題有點多,我們一個一個來。

 

Q1. 同步代碼里的錯誤捕獲方式

在同步代碼里,我們是最簡單的,只要try-catch就完了 

function test1 () {
  try {
    throw Error ('callback err');
  } catch (error) {
    console.log ('test1:catch err successfully');
  }
}
test1();

輸出結果如下,顯然是正常的

Q2. 普通的異步回調里的錯誤捕獲方式(Promise時代以前)

上面的問題來了,我們還能通過直接的try-catch在異步回調外部捕獲錯誤嗎?我們試一試 

// 嘗試在異步回調外部捕獲錯誤的結果
function test2 () {
  try {
    setTimeout (function () {
      throw Error ('callback err');
    });
  } catch (error) {
    console.log ('test2:catch err successfully');
  }
}
test2(); 

輸出

注意這裏的Uncaught Error的文本,它告訴我們錯誤沒有被成功捕捉。

為什麼呢? 因為try-catch的是屬於同步代碼,它執行的時候,setTimeOut內部的的匿名函數還沒有執行呢。而內部的那個匿名函數執行的時候,try-catch早就執行完了。( error的內心想法:哈哈,只要我跑的夠慢,try-catch還是追不上我!)

但是我們簡單想一想,誒我們把try-catch寫到函數裏面不就完事了嘛!

 

 

function test2_1 () {
  setTimeout (function () {
    try {
      throw Error ('callback err');
    } catch (error) {
      console.log ('test2_1:catch err successfully');
    }
  });
}
test2_1();

輸出結果如下,告訴我們這方法可行

 

總結下Promise時代以前,異步回調中捕獲和處理錯誤的方法

  • 在異步回調內部編寫try-catch去捕獲和處理,不要在外部哦

  • 很多異步操作會開放error事件,我們根據事件去操作就可以了

Q3. Promise里的錯誤捕獲方式

可通過Promise.catch方法捕獲

function test3 () {
  new Promise ((resolve, reject) => {
    throw Error ('promise error');
  }).catch (err => {
    console.log ('promise error');
  });
}

輸出結果

>> reject方法調用和throw Error都可以通過Promise.catch方法捕獲

function test4 () {
  new Promise ((resolve, reject) => {
    reject ('promise reject error');
  }).catch (err => {
    console.log (err);
  });
} 

輸出結果

 

>> then方法中的失敗回調和Promise.catch的關係

  • 如果前面的then方法沒寫失敗回調,失敗時後面的catch是會被調用的

  • 如果前面的then方法寫了失敗回調,又沒拋出,那麼後面的catch就不會被調用了

// then方法沒寫失敗回調
function test5 () {
  new Promise ((resolve, reject) => {
    throw Error ('promise error');
  })
    .then (success => {})
    .catch (err => {
      console.log ('the error has not been swallowed up');
    });
}
// then方法寫了失敗回調
function test5 () {
  new Promise ((resolve, reject) => {
    throw Error ('promise error');
  })
    .then (success => {},err => {})
    .catch (err => {
      console.log ('the error has not been swallowed up');
    });
}

輸出分別為

1.the error has not been swallowed up
2.無輸出

Q4.async/await里的錯誤捕獲方式

對於async/await這種類型的異步,我們可以通過try-catch去解決

async function test6 () {
  try {
    await getErrorP ();
  } catch (error) {
    console.log ('async/await error with throw error');
  }
}
 
function getErrorP () {
  return new Promise ((resolve, reject) => {
    throw Error ('promise error');
  });
}
test6();

輸出結果如下

 

>> 如果被await修飾的Promise因為reject調用而變化,它也是能被try-catch的

(我已經證明了這一點,但是這裏位置不夠,我寫不下了)

Q5.在全局環境下如何監聽錯誤

window.onerror可以監聽全局錯誤,但是很顯然錯誤還是會拋出

window.onerror = function (err) {
  console.log ('global error');
};
throw Error ('global error');

 

輸出如下

 

Q6.在React16以上如何監聽錯誤

>> componentDidCatch和getDerivedStateFromError鈎子函數

class Bar extends React.Component {
  // 監聽組件錯誤
  componentDidCatch(error, info) {
    this.setState({ error, info });
  }
  // 更新 state 使下一次渲染能夠显示降級后的 UI
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }
  render() {
  }
}

 

 

有錯誤,那肯定要上報啊!不上報就發現不了Bug這個樣子。Sentry這位老哥就是個人才,日誌記錄又好看,每次見面就像回家一樣

 

 

Sentry簡單介紹

Sentry provides open-source and hosted error monitoring that helps all software
teams discover, triage, and prioritize errors in real-time.
One million developers at over fifty thousand companies already ship
better software faster with Sentry. Won’t you join them?
—— Sentry官網

 

Sentry是一個日誌上報系統,Sentry 是一個實時的日誌記錄和匯總處理的平台。專註於錯誤監控,發現和數據處理,可以讓我們不再依賴於用戶反饋才能發現和解決線上bug。讓我們簡單看一下Sentry支持哪些語言和平台吧

 

在JavaScript領域,Sentry的支持也可以說是面面俱到

 

參考鏈接
https://docs.sentry.io/platforms/ 

Sentry的功能簡單說就是,你在代碼中catch錯誤,然後調用Sentry的方法,然後Sentry就會自動幫你分析和整理錯誤日誌,例如下面這張圖截取自Sentry的網站中

 

在JavaScript中使用Sentry 

1.首先呢,你當然要註冊Sentry的賬號

這個時候Sentry會自動給你分配一個唯一標示,這個標示在Sentry里叫做 dsn

2. 安卓模塊並使用基礎功能

安裝@sentry/browser 

npm install @sentry/browser

 

在項目中初始化並使用

import * as Sentry from '@sentry/browser';
 
Sentry.init ({
  dsn: 'xxxx',
});
 
try {
  throw Error ('我是一個error');
} catch (err) {
    // 捕捉錯誤
  Sentry.captureException (err);
}

3.上傳sourceMap以方便在線上平台閱讀出錯的源碼

 

// 安裝
$ npm install --save-dev @sentry/webpack-plugin
$ yarn add --dev @sentry/webpack-plugin
 
// 配置webpack
const SentryWebpackPlugin = require('@sentry/webpack-plugin');
module.exports = {
  // other configuration
  plugins: [
    new SentryWebpackPlugin({
      include: '.',
      ignoreFile: '.sentrycliignore',
      ignore: ['node_modules', 'webpack.config.js'],
      configFile: 'sentry.properties'
    })
  ]
}; 

4. 為什麼不是raven.js?

 

// 已經廢棄,雖然你還是可以用
var Raven = require('raven-js');
Raven
  .config('xxxxxxxxxxx_dsn')
  .install();

 

Sentry的核心功能總結

捕獲錯誤

try { aFunctionThatMightFail(); } catch (err) { Sentry.captureException(err); }

 

設置該錯誤發生的用戶信息

下面每個選項都是可選的,但必須 存在一個選項 才能使Sentry SDK捕獲用戶: id 

Sentry.setUser({
    id:"penghuwan12314"
  email: "penghuwan@example.com",
  username:"penghuwan",
  ip_addressZ:'xxx.xxx.xxx.xxx'
  });

 

設置額外數據

Sentry.setExtra("character_name", "Mighty Fighter");
設置作用域 
Sentry.withScope(function(scope) {
    // 下面的set的效果只存在於函數的作用域內
  scope.setFingerprint(['Database Connection Error']);
  scope.setUser(someUser);
  Sentry.captureException(err);
});
// 在這裏,上面的setUser的設置效果會消失

 

設置錯誤的分組

整理日誌信息,避免過度冗餘 

Sentry.configureScope(function(scope) {
  scope.setFingerprint(['my-view-function']);
});

 

設置錯誤的級別

在閱讀日誌時可以確定各個bug的緊急度,確定排查的優先書序

Sentry.captureMessage('this is a debug message', 'debug');
//fatal,error,warning,info,debug五個值
// fatal最嚴重,debug最輕

 

自動記錄某些事件

例如下面的方法,會在每次屏幕調整時完成上報 

window.addEventListener('resize', function(event){
  Sentry.addBreadcrumb({
    category: 'ui',
    message: 'New window size:' + window.innerWidth + 'x' + window.innerHeight,
    level: 'info'
  });
})

Sentry實踐的運用

根據環境設置不同的dsn

let dsn;
  if (env === 'test') {
    dsn = '測試環境的dsn';
  } else {
    dsn =
      '正式環境的dsn';
  }
 
Sentry.init ({
  dsn
});

 

 

 

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?

您可能也會喜歡…