angular代碼分析之異常日志設計


angular代碼分析之異常日志設計

錯誤異常是面向對象開發中的記錄提示程序執行問題的一種重要機制,在程序執行發生問題的條件下,異常會在中斷程序執行,同時會沿着代碼的執行路徑一步一步的向上拋出異常,最終會由頂層拋出異常信息。而與異常同時出現的往往是日志,而日志往往需要記錄具體發生異常的模塊、編碼、詳細的錯誤信息、執行堆棧等,方便問題的快速定位分析。angularjs作為前端的框架,其對異常信息的處理是怎樣的呢?

一、定義模塊和異常類型

首先每個模塊的日志模塊編碼是一樣的,只需要本模塊負責初始化一次即可。通過以下代碼我們可以看到angular通過minErr函數傳入module參數,並在返回的閉包函數中引用module參數實現這一點。

//構造模塊異常類,module為模塊編碼,ErrorConstructor異常構造函數

function minErr(module, ErrorConstructor) {

//如果未傳入異常構造函數則使用Error

ErrorConstructor = ErrorConstructor || Error;

//返回異常對象構建方法

return function() {

 

};

}

這里需要注意的是傳入參數ErrorConstructor,這里可以傳入特定類型的異常類,比如ES6內置了RangeErrorReferenceErrorSyntaxErrorTypeErrorURIError異常類型,這樣我們就可以實現拋出特定類型的異常。但是我感覺這里不好的一點是無法實現模塊內自由靈活的拋出不同的特定類型異常,其實這點可以在閉包函數參數中實現。

二、定義日志編碼和日志信息模板

當我們每次記錄日志的時候,需要傳入日志編碼及日志信息模板,方便記錄異常發生的代碼塊及更具可讀性的異常信息。通過以下的代碼,我們可以看到angular通過閉包函數的可變參數的第一個參數定義日志編碼,通過第二個參數定義日志信息模板。

//構造模塊異常類,module為模塊編碼,ErrorConstructor異常構造函數

function minErr(module, ErrorConstructor) {

//如果未傳入異常構造函數則使用Error

ErrorConstructor = ErrorConstructor || Error;

//返回異常對象構建方法,傳入參數可變

return function() {

//日志編碼,只能是第一個參數

var code = arguments[0],

//構造日志前綴

prefix = '[' + (module ? module + ':' : '') + code + '] ',

//日志模板

template = arguments[1],

//傳入參數

templateArgs = arguments,

};

}

三、日志模板信息替換

通過傳入日志相關對象來替換日志模板中的信息,從而形成更有意義的異常信息。通過以下代碼我們可以看到angular通過replace方法實現日志模板中站位符的替換。

//模板信息替換

message = prefix + template.replace(/\{\d+\}/g, function(match) {

//獲取站位符的索引,並轉化為數字

var index = +match.slice(1, -1), arg;

//獲取替換站位符的傳入參數對象的字符串表示形式

if (index + 2 < templateArgs.length) {

//對象轉換為字符串

return toDebugString(templateArgs[index + 2]);

}

return match;

});

 

在這里很巧妙的使用了stringreplace方法,通過其正則表達式參數和生成替換字符串的方法實現模板中所有站位符的替換。replace的方法定義如下

stringObject.replace(regexp/substr,replacement)

參數

描述

regexp/substr

必需。規定子字符串或要替換的模式的RegExp 對象。

請注意,如果該值是一個字符串,則將它作為要檢索的直接量文本模式,而不是首先被轉換為RegExp 對象。

replacement

必需。一個字符串值。規定了替換文本或生成替換文本的函數。


 

 

 

 

 

 

replacement方法里很巧妙的使用了stringslice方法,實現獲取站位符索引。這里使用了兩個技巧,其中的+號實現了字符串轉化為數字,其中slice中傳入的參數1-1巧妙的提取了站位索引。slice的方法定義如下

stringObject.slice(start,end)

參數

描述

start

要抽取的片斷的起始下標。如果是負數,則該參數規定的是從字符串的尾部開始算起的位置。也就是說,-1 指字符串的最后一個字符,-2 指倒數第二個字符,以此類推。

end

緊接着要抽取的片段的結尾的下標。若未指定此參數,則要提取的子串包括start 到原字符串結尾的字符串。如果該參數是負數,那么它規定的是從字符串的尾部開始算起的位置。

 

 

 

 

 

 

angular將傳入的對象轉化為便於調試的字符串形式,具體代碼如下,其中函數對象會清空函數體,undefined變量直接返回undefined字符串,字符串則直接返回,其他類型對象則調用serializeObject進行序列化。這里需要注意對函數對象的處理,對匿名函數的處理並不盡如人意,這里我也沒有想到更好的處理方式。

//將傳入的對象轉化為便於調試的字符串形式

function toDebugString(obj) {

//如果是函數則清空函數體

if (typeof obj === 'function') {

return obj.toString().replace(/ \{[\s\S]*$/, '');

} else if (typeof obj === 'undefined') {

return 'undefined';

} else if (typeof obj !== 'string') {//如果是非字符串的其他對象則進行序列化

return serializeObject(obj);

}

return obj;

}

 

angular將對象轉化為json字符串的代碼如下,其通過JSONstringify方法的第二個方法參數控制鍵值對的序列化結果,同時也通過seen數組防止引用閉環導致序列化死循環。

//將對象轉化為json字符串

function serializeObject(obj) {

var seen = [];


return JSON.stringify(obj, function(key, val) {

//獲取val的具體字符串表示形式

val = toJsonReplacer(key, val);

//防止引用閉環導致死循環

if (isObject(val)) {


if (seen.indexOf(val) >= 0) return '<<already seen>>';


seen.push(val);

}

return val;

});

}

 

JSON.stringify的方法定義如下

JSON.stringify(value[, replacer[, space]])

參數

描述

value

待序列化為JSON字符串的對象

replacer

可傳入數組定義序列化需要包含的屬性,或者傳入修改序列化行為的方法,如果不傳入參數,則默認序列化所有的非方法屬性。

space

分割符,具體參考以下

A String or Number object that's used to insert white space into the output JSON string for readability purposes. If this is a Number, it indicates the number of space characters to use as white space; this number is capped at 10 if it's larger than that. Values less than 1 indicate that no space should be used. If this is a String, the string (or the first 10 characters of the string, if it's longer than that) is used as white space. If this parameter is not provided (or is null), no white space is used.



 

 

 

 

 

 

 

 

 

angular針對內置一些復雜對象的序列化處理代碼如下

//過濾對angular復雜對象的序列化

function toJsonReplacer(key, value) {

var val = value;

//兩個$$開頭的字符串返回undefined

if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {

val = undefined;

} else if (isWindow(value)) {//window

val = '$WINDOW';

} else if (value && document === value) {//document

val = '$DOCUMENT';

} else if (isScope(value)) {//scope

val = '$SCOPE';

}


return val;

}

四、構建在線錯誤鏈接

針對特定的異常能夠提供在線解決方案是十分人性化的,angular默認提供跳轉到angular的在線錯誤頁面,我們可以根據實際需要替換為我們的。這里直接返回異常實例,方便調用者直接調用函數使用。

//構建錯誤在線鏈接

message = message + '\nhttp://errors.angularjs.org/1.3.9-local+sha.a3c3bf3/' +

(module ? module + '/' : '') + code;

for (i = 2; i < arguments.length; i++) {

message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' +

encodeURIComponent(toDebugString(arguments[i]));

}

//構建異常錯誤實例

return new ErrorConstructor(message);

 

五、整個處理流程如下圖所示



 

 

 

 

 

 

 

 

 

 

 

 

 

 

六、實際使用樣例

調用代碼如下

var codeartistminErr = minErr("CodeRrtist");

throw codeartistminErr("AngularExceptionDesinBlog",'this is my AngularExceptionDesinBlog,{0}',{name:'codeartist'});

調用結果



 

添加注釋的angular.js源代碼文件下載地址 



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM