15

爲了使調試更容易,我在Chrome中捕獲所有控制檯日誌,以便提交反饋條目的用戶也將所有日誌提交到我們的服務器。當有人在生產中遇到問題時,我可以首先讓他們重新工作,這樣我就可以坐下來更徹底地查看所有日誌,以確定用戶在生產中遇到的任何問題的根本原因。如何在Chrome的JavaScript中重寫/擴展ReferenceError?

我用來捕獲日誌的技術涉及覆蓋console.log,以便在第一個參數中輸入的所有文本都存儲在數組中,同時調用遺留函數,以便我仍然可以在控制檯中看到日誌。

問題是當偶爾有未捕獲的異常。這些不包含在上傳的日誌中,因此並不總是清楚導致問題的原因。所以我試着通過編寫一個以函數作爲參數的JavaScript函數來覆蓋ReferenceError,然後返回一個新的函數來處理它,比如將數據存儲在變量中,然後調用傳統函數作爲最後一步:

function overrideException(legacyFn) { 

    /** arguments for original fn **/ 
    return function() { 

     var args = []; 

     args[0] = arguments[0]; 

     // pass in as arguments to original function and store result to 
      // prove we overrode the ReferenceError 
     output = ">> " + legacyFn.apply(this, args).stack; 

     return legacyFn.apply(this, arguments); 
    }   

} 

要測試overrideException功能,我跑在控制檯上下面的代碼:

ReferenceError = overrideException(ReferenceError); 

後來,我通過手動拋出的ReferenceError測試返回的功能,新的ReferenceError,:

throw new ReferenceError("YES!! IT WORKS! HAHAHA!"); 

在控制檯上的輸出結果是:

ReferenceError: YES!! IT WORKS! HAHAHA!

而且檢查從overrideException功能全局變量output表明,它確實運行:

output 
    ">> ReferenceError: YES!! IT WORKS! HAHAHA! 
    at ReferenceError (<anonymous>) 
    at new <anonymous> (<anonymous>:18:35) 
    at <anonymous>:2:7 
    at Object.InjectedScript._evaluateOn (<anonymous>:562:39) 
    at Object.InjectedScript._evaluateAndWrap (<anonymous>:521:52) 
    at Object.InjectedScript.evaluate (<anonymous>:440:21)" 

現在,這裏的事情開始下降分開。在我們的代碼,我們沒有嘗試運行了一個不存在的函數去時未捕獲的異常發生時就知道了,所以我測試了它:

ttt(); 

導致:

ReferenceError: ttt is not defined

但是,與我們明確拋出錯誤的情況不同,在這種情況下,函數不會觸發,而我們只剩下遺留功能。變量output的內容與第一個測試中的內容相同。

所以這個問題似乎是這樣的:我們如何重寫JavaScript引擎用來引發錯誤的ReferenceError功能,以便在我們拋出ReferenceError時使用它?

請記住,我的問題目前僅限於Chrome;我正在構建Chrome Packaged應用。

+0

猜測你不想將代碼包裝在try/catch中,然後處理錯誤對象然後重新拋出? –

+0

@瘋狂火車 - 我可以做到,我可能會。然而,這是乏味的,也可能會錯過一些東西。我喜歡防僞和全包的解決方案,因爲它們通常也是「我太忙了」。:)我的計劃當然是使用更多的嘗試/捕獲,但這似乎是一個很好的解決方案,所以我開始深入研究它。 – jmort253

+0

我的意思是一個單一的'try/catch'包裝所有的代碼。由於看起來目標並不是處理錯誤,而是以某種方式重新格式化它,然後將所有代碼包裝在一個'try/catch'中看起來與您當前的替換構造函數的方法有所不同。你只需要確定你重新拋出你得到的任何錯誤。 –

回答

12

由於相同的原因,我做了相當多的研究:我想記錄錯誤並報告它們。

「覆蓋」原生類型(是否ReferenceErrorStringArray)是不可能的。

Chrome在任何Javascript運行之前綁定這些,因此重新定義window.ReferenceError沒有任何效果。

您可以使用ReferenceError.prototype.extension = function() { return 0; }之類的東西來擴展ReferenceError,甚至可以覆蓋toString(爲了保持一致性,請在頁面上嘗試它,而不是Dev Tools)。

這並沒有多大幫助。

但別擔心....

(1)使用window.onerror獲取文件名,爲1索引的行號,未被捕獲的錯誤的0索引的位置,以及錯誤本身。

var errorData = []; 
onerror = function(message, file, line, position, error) { 
    errorData.push({message:message, file:file, line:line, position:position, error:error}); 
}; 

查看fiddle的例子。由於OP是Chrome特定的,因此只能在Chrome中進行測試。 (2)由於對(1)的改進,這不再是必要的,但爲了完整性,我在此留下第二種技術,並且因爲onerrornot guaranteed to work for all errors on all browsers。您也有時會看到以下內容:

var errors = []; 
function protectedFunction(f) { 
    return function() { 
     try { 
      f.apply(this, arguments); 
     } catch(e) { 
      errors.push(e); 
      throw e; 
     } 
    }; 
} 
setTimeout = protectedFunction(setTimeout); 
setInterval = protectedFunction(setInterval); 
etc... 

僅供參考,這一切都是非常相似,已經在谷歌關閉編譯庫已經完成,在goog.debug,Gmail的發展與正是這種做的意圖期間創建的。特別感興趣的是goog.debug.ErrorHandlergoog.debug.ErrorReporter

+1

你先生,是個天才!我試了一下,實際上我的代碼中有一個真正未捕獲的異常,我不知道,它會記錄到我的日誌框架中!我將需要多花一點時間,因爲我發現它在某些回調中不起作用。我希望你的setTimeout/setInterval方法適用於這些情況。 +1 – jmort253

+0

謝謝。 (1)應該適用於所有未捕獲的異常(無論來自腳本評估,事件監聽器等)。讓我們知道,如果你發現它不起作用的情況。 –

+0

我將不得不在明天玩弄它,我可能只是犯了一個錯誤,但我記得其他事件處理程序拋出chrome.app.window.create中生成的錯誤,而不是處理它們的window.onerror。我在Chrome打包應用程序'create'方法回調函數中嘗試這個。 – jmort253