node.body[i].print(stream)
}
varinnerFuncCode=stream.toString();
//清除trycatch中定义的多余语句
tryCatchAST.body[0].body.splice(0,tryCatchAST.body[0].body.length);
//用trycatch包裹函数代码
varinnerTyrCatchNode=UglifyJS.parse(innerFuncCode,{toplevel:
tryCatchAST.body[0]});
//获取函数壳
node.body.splice(0,node.body.length);
//生成有trycatch的函数
returnUglifyJS.parse(innerTyrCatchNode.print_to_string(),{toplevel:
node});
}
});
inputAST.transform(transfer);
varoutputCode=inputAST.print_to_string({beautify:
true});
returnoutputCode;
}
module.exports.globalFuncTryCatch=globalFuncTryCatch;
借助于globalFuncTryCatch,我们对每个函数进行自动化地添加trycatch语句,并使用自定义的错误处理函数:
globalFuncTryCatch(inputCode,function(error){
//此处是异常处理代码,可以上报并记录日志
console.log(error);
});
通过将globalFuncTryCatch功能集成到构建工具中,我们就可以对目标Javascript文件进行trycatch处理。
综上所述:
当静态资源服务器可以添加Access-Control-Allow-Origin:
*时,我们可以直接使用window.onError进行全局异常捕获;当静态资源服务器不受控制,window.onError失效,我们可以借助AST技术,自动化地对全部目标Javascript函数添加trycatch来捕获所有异常。
参考文档:
CaptureandreportJavaScripterrorswithwindow.onerror
onerrorisaspecialbrowsereventthatfireswheneveranuncaughtJavaScripterrorhasbeenthrown.It’soneoftheeasiestwaystologclient-sideerrorsandreportthemtoyourservers.It’salsooneofthemajormechanismsbywhichSentry’sclientJavaScriptintegration(raven-js)works.
Youlistentotheonerroreventbyassigningafunctiontowindow.onerror:
window.onerror=function(msg,url,lineNo,columnNo,error){
//...handleerror...
returnfalse;
}
Whenanerroristhrown,thefollowingargumentsarepassedtothefunction:
msg–Themessageassociatedwiththeerror,e.g.“UncaughtReferenceError:
fooisnotdefined”
url–TheURLofthescriptordocumentassociatedwiththeerror,e.g.“/dist/app.js”
lineNo–Thelinenumber(ifavailable)
columnNo–Thecolumnnumber(ifavailable)
error–TheErrorobjectassociatedwiththiserror(ifavailable)
Thefirstfourargumentstellyouinwhichscript,line,andcolumntheerroroccurred.Thefinalargument,Errorobject,isperhapsthemostvaluable.Let’slearnwhy.
TheErrorobjectanderror.stack
AtfirstglancetheErrorobjectisn’tveryspecial.Itcontains3standardizedproperties:
message,fileName,andlineNumber.Redundantvaluesthatalreadyprovidedtoyouviawindow.onerror.
Thevaluablepartisanon-standardproperty:
Error.prototype.stack.Thisstackpropertytellsyouatwhatsourcelocationeachframeoftheprogramwaswhentheerroroccurred.Thestacktracecanbeacriticalpartofdebugginganerror.Anddespitebeingnon-standard,thispropertyisavailableineverymodernbrowser.
Here’sanexampleoftheErrorobject’sstackpropertyinChrome46:
"Error:
foobar\natnewbar(:
241:
11)\natfoo(:
245:
5)\nat:
250:
5\nat:
251:
3\nat:
267:
4\natcallFunction(:
229:
33)\nat:
239:
23\nat:
240:
3\natObject.InjectedScript._evaluateOn(:
875:
140)\natObject.InjectedScript._evaluateAndWrap(:
808:
34)"
Hardtoread,right?
Thestackpropertyisactuallyjustanunformattedstring.
Here’swhatitlookslikeformatted:
Error:
foobar
atnewbar(:
241:
11)
atfoo(:
245:
5)
atcallFunction(:
229:
33)
atObject.InjectedScript._evaluateOn(:
875:
140)
atObject.InjectedScript._evaluateAndWrap(:
808:
34)
Onceit’sbeenformatted,it’seasytoseehowthestackpropertycanbecriticalinhelpingtodebuganerror.
There’sjustonesnag:
thestackpropertyisnon-standard,anditsimplementationdiffersamongbrowsers.Forexample,here’sthesamestacktracefromInternetExplorer11:
Error:
foobar
atbar(Unknownscriptcode:
2:
5)
atfoo(Unknownscriptcode:
6:
5)
atAnonymousfunction(Unknownscriptcode:
11:
5)
atAnonymousfunction(Unknownscriptcode:
10:
2)
atAnonymousfunction(Unknownscriptcode:
1:
73)
Notonlyistheformatofeachframedifferent,theframesalsohavelessdetail.Forexample,Chromeidentifiesthatthenewkeywordhasbeenused,andhasgreaterinsightintoevalinvocations.AndthisisjustIE11vsChrome–otherbrowserssimilarhavevaryingformatsanddetail.
Luckily,therearetoolsouttherethatnormalizestackpropertiessothatitisconsistentacrossbrowsers.Forexample,raven-jsusesTraceKittonormalizeerrorstrings.There’salsostacktrace.jsandafewotherprojects.
Browsercompatibility
window.onerrorhasbeenavailableinbrowsersforsometime–you’llfinditinbrowsersasoldasIE6andFirefox2.
Theproblemisthateverybrowserimplementswindow.onerrordifferently.Particularly,inhowmanyargumentsaresenttototheonerrorlistener,andthestructureofthosearguments.
Here’satableofwhichargumentsarepassedtoonerrorinmostbrowsers:
BrowserMessageURLlineNocolNoerrorObj
Firefox42
Chrome46
AndroidBrowser4.4
Edge
IE11
IE10
IE9,8
Safari9
iOS9
You’llnoticethatthelatestApplebrowsers–SafariandiOS–don’tsupporta5therrorobjectargument.AndwhilethefinalversionofInternetExplorer(11)supportstheerrorobject,Microsoft’slatestbrowser,Edge,doesnot.
Withouttheerrorobject,thereisnostacktraceproperty.Thismeansthatthesebrowserscannotretrievevaluablestackinformationfromerrorscaughtbyonerror.
Polyfillingwindow.onerrorwithtry/catch
Butthereisaworkaround–youcanwrapcodeinyourapplicationinsideatry/catchandcatchtheerroryourself.Thiserrorobjectwillcontainourcovetedstackpropertyineverymodernbrowser.
Considerthefollowinghelpermethod,invoke,whichcallsafunctiononanobjectwithanarrayofarguments:
functioninvoke(obj,method,args){
returnobj[method].apply(this,args);
}
invoke(Math,'max',[1,2]);//returns2
Here’sinvokeagain,thistimewrappedintry/catch,inordertocaptureanythrownerror:
functioninvoke(obj,method,args){
try{
returnobj[method].apply(this,args);
}catch(e){
captureError(e);//reporttheerror
throwe;//re-throwtheerror
}
}
invoke(Math,'highest',[1,2]);//throwserror,nomethodMath.highest
Ofcourse,doingthismanuallyeverywhereisprettycumbersome.Youcanmakeiteasierbycreatingagenericwrapperutilityfunction:
functionwrapErrors(fn){
//don'twrapfunctionmorethanonce
if(!
fn.__wrapped__){
fn.__wrapped__=function(){
try{
returnfn.apply(this,arguments);
}catch(e){
captureError(e);//reporttheerror
throwe;//re-throwtheerror
}
};
}
returnfn.__wrapped__;
}
varinvoke=wrapErrors(function(obj,method,args){
returnobj[method].apply(this,args);
});
invoke(Math,'highest',[1,2]);//nomethodMath.highest
BecauseJavaScriptissinglethreaded,youdon