JS Advanced Debugging Tips: Capturing and parsing JavaScript error details

Source: Internet
Author: User
Tags call back stack trace

Front-end engineers know that JavaScript has basic exception handling capabilities. We can throw the new error () and the browser will throw an exception when we call the API error. But it is estimated that most front-end engineers have not considered collecting these anomaly information.

Anyway, as long as the JavaScript error after the refresh is not present, the user can solve the problem by refreshing, the browser will not crash, when it has not happened well. This assumption was established before the single Page App was popular. Now the single Page APP runs for a period of time after the state is very complex, the user may have a number of input operations to come here, said the refresh is refreshed ah? Do you want to redo all the previous operations? So we still need to capture and analyze these exception information, and then we can modify the code to avoid impacting the user experience.

How to catch exceptions

We write our own throw new Error() to capture, of course, because we know throw where to write. However, the exception that occurs when invoking the browser API is not necessarily easy to capture, and some APIs are written in the standard to throw exceptions, and some APIs only throw exceptions because of differences or flaws in the individual browsers. For the former we can also try-catch catch, for the latter we have to listen to the global exception and then capture.

Try-catch

If some browser API is known to throw an exception, then we need to put the call try-catch inside, to avoid the error caused by the entire program into an illegal state. For example window.localStorage , an API that throws an exception after the write data exceeds the capacity limit, as in Safari's private browsing mode.

Try {localstorage.setitem (' Date 'catch  (error) {ReportError (Error)}

 

Another common try-catch application scenario is callbacks. Because the code of the callback function is we are not controllable, the code quality, will not invoke other throws the exception the API, we do not know. It is necessary to put the call back inside in order not to be able to execute other code after calling the callback because of a callback error try-catch .

Listeners.foreach (functiontrycatch  (error) {ReportError ( Error); }});

 
Window.onerror

For try-catch areas not covered, if an exception occurs, it can only be window.onerror captured by the.

function (ErrorMessage, Scripturi, linenumber) {reportError ({message:errormessage, Script:scripturi, Line:linenumber});} 

 

Be careful not to use window.addEventListener or window.attachEvent to listen in a smart way window.onerror . Many browsers are only implemented window.onerror , or only window.onerror implementations are standard. Given the definition of the draft standard window.onerror , we window.onerror should use it.

property is missing

Suppose we have a reportError function that collects the caught exceptions and then sends them to the server-side store for query analysis, what information do we want to collect? Useful information includes: error type (), name error message ( message ), script file address ( script ), line number (), line column number ( column ), stack trace ( stack ). If an exception is try-catch captured, this information is Error available on the object (supported by the mainstream browser), so reportError it can be collected. But if it is window.onerror captured, we all know that this event function has only 3 parameters, so the unexpected information of these 3 parameters is lost.

Serializing messages

If the Error object is created by ourselves, then error.message we are controlled by it. Basically what we put into it error.message window.onerror is what the first parameter ( message ) will be. (The browser will actually be slightly modified, for example by ‘Uncaught Error: ‘ prefixing it.) So we can serialize the properties we care about (for example JSON.Stringify ) and store them error.message inside, and then deserialize them in the window.onerror read. Of course, this is limited to the objects we create ourselves Error .

Fifth parameter

Browser vendors are also aware window.onerror of the restrictions that people are using, so start window.onerror adding new parameters to them. Considering that only the row number does not appear to be very symmetrical, IE first adds the column number, placed in the fourth parameter. However, people are more concerned about the ability to get the complete stack, so Firefox is better to put the stack on the fifth parameter bar. But Chrome says it's better to put the whole Error object in the fifth parameter, which you want to read, including the custom properties. As a result, chrome moves faster, and new signatures are implemented in Chrome 30 window.onerror , leading to the standard draft.

functionifelse  {reportError ({message:errormessage, Script:scripturi , Line:linenumber, column:columnnumber}); }}

 

Attribute Normalization

The object properties we discussed earlier are Error all based on the name of Chrome, but different browsers are Error naming object properties differently, for example, the script file address is called in Chrome script but in Firefox filename . Therefore, we also need a special function to normalize the Error object, that is, to map different property names to the uniform property names. You can refer to this article for specific practice. Although the browser implementation is updated, it is not too difficult to manually maintain a mapping table.

Similar is the format of the stack trace ( stack ). This property saves a copy of the stack information in plain text as it occurs, and because each browser uses a different text format, manual maintenance of a regular expression is required to extract the function name ( identifier ), file () script , line number (), line and column number of each frame from the plain text ( column).

Security restrictions

If you also encounter ‘Script error.‘ the error of the message, you will understand what I'm talking about, which is actually the browser's limitations on different source (origin) script files. The reason for this security restriction is this: Suppose a net bank returns HTML after the user logs in a different way than the HTML that the anonymous user sees, a third-party site can put the URI of the net-silver into the script.src attribute. HTML is certainly not possible to be interpreted as JS, so the browser throws an exception, and this third-party site can be resolved by the location of the exception to determine whether the user has logged in. This browser for different source script files thrown by the exception is filtered to only ‘Script error.‘ leave such a constant message, all the other properties disappear.

For a certain size of the site, the script file on the CDN, the non-homologous is very normal. Now it's time to make a small website, and common frameworks like JQuery and Backbone can refer directly to the version on the public CDN to speed up user downloads. So this security limit does cause some trouble, and the exception information we collect from Chrome and Firefox is useless ‘Script error.‘ .

CORS

To circumvent this limitation, just make sure the script file and the page itself are the same. But if you put the script file on a server that does not have CDN acceleration, does it slow down user download speed? One solution is that the script files continue to be placed on the CDN, using XMLHttpRequest CORS to download the content back, and then create <script> tags to inject into the page. The code embedded in the page is of course homologous.

This is easy to say, but there are a lot of details to come up with. In a simple example:

<Scriptsrc= "Http://cdn.com/step1.js"></Script><Script> (functionStep2 () {}) ();</Script><Scriptsrc= "Http://cdn.com/step3.js"></Script>

 

We all know that this step1, STEP2, step3 if there is a dependency, it must be executed strictly in this order, otherwise there may be an error. Browsers can request step1 and step3 files in parallel, but the order is guaranteed at execution time. If we ourselves XMLHttpRequest obtain Step1 and Step3 's file content, we need to ensure that the order is correct in its own right. Also do not forget the Step2, Step1 in a non-blocking form when the STEP2 can be executed, so we must also human intervention step2 let it wait for Step1 to be completed before execution.

If we already have a complete set of tools to generate labels for the different pages on <script> our site, we need to tweak the tool to make changes to the <script> tags:

<Script>Scheduleremotescript ('Http://cdn.com/step1.js');</Script><Script>Scheduleinlinescript (functioncode () {(functionStep2 () {}) (); });</Script><Script>Scheduleremotescript ('Http://cdn.com/step3.js');</Script>

 

We need to implement the scheduleRemoteScript scheduleInlineScript two functions and ensure that they are defined before the first tag that references the external script file <script> , and then the rest of the <script> tags are rewritten in this form. Note that the function that step2 was executed immediately is put into a larger code function. The code function is not executed, it is just a container, so that the original STEP2 code does not need to be escaped to be preserved, but will not be executed immediately.

Next we need to implement a complete set of mechanisms to ensure that the contents of the scheduleRemoteScript files downloaded from the address and scheduleInlineScript directly obtained from the code can be executed one after the other in the correct order. Detailed code I will not be here to give, everyone is interested in the realization of their own.

Counter-Search by line number

Getting content through CORS and injecting code into the page can break the security limit, but introduces a new problem, which is line number collisions. Originally error.script , you can navigate to a unique script file, and then error.line you can navigate to a unique line number. Now that the code is embedded in the page, multiple <script> tags cannot be error.script distinguished, but the <script> line numbers inside each label are calculated from 1, resulting in the inability to locate the source code where the error is located.

In order to avoid line number conflicts, we can waste some line numbers, make each   <script>   tags have actual code in the line number range does not overlap with each other. For example, assuming each   <script>   The actual code in the tag is no more than 1000 lines, then I can make the first   <script> The code in the   tag occupies line 1–1000, allowing the second   <script>   The code in the tag occupies line 1001–2000 (preceded by 1000 rows of blank lines), and a third   <script>   The code for the label takes the line 2001–3000 (insert 2000 lines in front of the line), and so on. We then use   data-*   Properties to record this information for easy back-check.

<script data-src= "http://cdn.com/step1.js" data//  code for step 1// ' \ n ' * // code for Step 2</script><script data-src= "http://cdn.com/step3.js" data//  ' \ n ' *$//  code for step 3</script>

 

After such treatment, if one error.line is wrong 3005 , that means the actual error.script should be ‘http://cdn.com/step3.js‘ , and the actual error.line should be 5 . We can do this in the previous function of the reportError line number counter-search work.

Of course, since we can not guarantee that each script file has only 1000 lines, it is possible that some script files are significantly less than 1000 lines, so there is no need to fix a 1000 line of the interval to each <script> label. We can allocate intervals based on the number of actual script rows, as long as <script> the intervals used for each label are guaranteed to be non-overlapping.

Crossorigin Property

The browser's security restrictions on the content of different sources are certainly not limited to <script> labels. Since XMLHttpRequest it is possible to break through this limitation through CORS, why is the resource referenced directly through the tag not possible? This is of course possible.

<script>The restrictions that refer to different source script files for labels also work on labels referencing different source picture files. If a label is a different source, once it is used in the <canvas> drawing, it <canvas> will become a write-only state, ensuring that the site cannot steal unauthorized, different source image data through JavaScript. Later crossorigin , the label solves the problem by introducing attributes. If used crossorigin="anonymous" , it is equivalent to anonymous cors, and if you use ' crossorigin= ' use-credentials, it is equivalent to a certified cors.

Since labels can do this, why <script> can't labels do this? The browser vendor then <script> adds the same attributes to the tag to address the crossorigin security restrictions described above. Now Chrome and Firefox support for this property is absolutely no problem. Safari will treat it crossorigin="anonymous" as crossorigin="use-credentials" a result if the server only supports anonymous CORS, Safari will fail as authentication. Since the CDN server is designed to return only static content for performance reasons, it is not possible to dynamically return the HTTP Header,safari required to authenticate CORS on request equivalent to the inability to exploit this feature to address the above problem.

Summarize

JavaScript exception handling looks simple, it's no different from other languages, but it's really not that easy to catch the exceptions and then analyze the attributes. Now, while there are some third-party services that provide the kind of Google Analytics services that capture JavaScript exceptions, it's important to do it yourself if you want to figure out the details and rationale.

JS Advanced Debugging Tips: Capturing and parsing JavaScript error details

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.