JS Advanced Debugging Tips: Capturing and analyzing JavaScript error details _javascript tips

Source: Internet
Author: User
Tags call back exception handling stack trace

Anyway, as long as the JavaScript error after the refresh is no longer, the user can solve the problem by refreshing, the browser will not crash, when it did not happen well. This assumption was established before the single Page App was popular. Now the single Page APP runs for a period of time after a very complex state, the user may have a number of input operations to come here, said refresh on the refresh Ah? Do you want to redo the previous operation? 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 exceptions are caught

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

Try-catch

If some browser APIs are known to throw exceptions, then we need to put the call inside to try-catch avoid an error that causes the entire program to enter an illegal state. window.localStorageThis is an API, for example, that throws an exception when the write data exceeds the capacity limit, as is the case in Safari's privacy browsing mode.

try {
localStorage.setItem('date', Date.now());
} catch (error) {
reportError(error);
}

Another common try-catch application scenario is callbacks. Because the code for the callback function is not controllable, the quality of the code will not invoke any other API that throws an exception, we don't know. In order not to be able to execute other code after invoking the callback because of a callback error, it is necessary to put the call back into the try-catch inside.

listeners.forEach(function(listener) {
try {
listener();
} catch (error) {
reportError(error);
}
});

Window.onerror

For try-catch areas that are not covered, an exception can only be caught if there are exceptions window.onerror .

window.onerror =
function(errorMessage, scriptURI, lineNumber) {
reportError({
message: errorMessage,
script: scriptURI,
line: lineNumber
});
}

Be careful not to use window.addEventListener window.attachEvent the form of smart tricks or to listen window.onerror . Many browsers are only implemented window.onerror , or the only window.onerror implementations are standard. Given the definition of the draft standard window.onerror , we window.onerror can use it well.

property is missing

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

Serialization message

If the Error object is created by ourselves, then it error.message is controlled by us. Basically what we put in error.message there, window.onerror the first argument ( message ) is what. (Browsers actually make minor changes, such as 'Uncaught Error: ' prefixing.) So we can serialize the attributes we care about (for example JSON.Stringify ) and then store them error.message in the inside, then deserialize it when we window.onerror read it. Of course, this is limited to the objects we create ourselves Error .

Fifth parameter

Browser vendors are also aware of the restrictions they are using window.onerror , so start window.onerror adding new parameters to them. In view of the fact that only line numbers do not appear to be symmetrical, IE first add the column number and place the fourth parameter. However, we are more concerned about the ability to get the full stack, so Firefox said to put the stack on the fifth parameter bar. But Chrome says it's better to put the whole Error object in the fifth argument, and what properties you want to read, including custom attributes. As a result of the fast chrome action, the new signature was implemented in Chrome 30 window.onerror , which led to the standard draft.

window.onerror = function(
errorMessage,
scriptURI,
lineNumber,
columnNumber,
error
) {
if (error) {
reportError(error);
} else {
reportError({
message: errorMessage,
script: scriptURI,
line: lineNumber,
column: columnNumber
});
}
}
attribute Normalization

The object properties that we discussed earlier, Error whose names are based on the Chrome naming method, differ in how different browsers name the Error object properties, such as the script file address in Chrome called script but in Firefox filename . Therefore, we also need a special function to Error normalize objects, that is, to map different attribute names to uniform attribute names. The specific procedure can refer to this article. Although the browser implementation is updated, it is not too difficult for a human to maintain such a mapping.

Similar to the format of the stack trace ( stack ). This property preserves the stack information of an exception when it occurs in plain text. Because the text format used by each browser is different, it also requires manual maintenance a regular expression used to extract the function name ( identifier ), file ( script ), line number () line and column number of each frame from plain text ( column).

Security Restrictions

If you've had a bad message 'Script error.' , you'll understand what I'm talking about, which is actually a browser's restriction on different source (origin) script files. The reason for this security restriction is that a Third-party Web site can put the URI of the net silver into the attribute, assuming that the HTML returned by the user is not the same as the HTML the anonymous user sees. script.src HTML of course can not be used as JS parsing, so the browser will throw an exception, and this Third-party Web site will be able to resolve the abnormal location to determine whether the user has logged in. This browser for different source script files thrown by the exception to filter, filtered only 'Script error.' such a constant message, all the other attributes disappeared.

For a certain size of the site, the script file on the CDN, not homologous is very normal. Now, as a small web site, common frameworks such as jQuery and backbone can directly reference the version on the public CDN to speed up user downloads. So this security constraint 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 that the script file and the page itself are homologous. But is the script file on the server without CDN acceleration, does not reduce the user download speed? One solution is that the script files continue to be on the CDN, using the XMLHttpRequest CORS to download the content back, and then create the <script> tag to inject it into the page. The code embedded in the page is of course homologous.

This is simple to say, but there are a lot of details to realize. In a simple example:

<script src="http://cdn.com/step1.js"></script>
<script>
(function step2() {})();
</script>
<script src="http://cdn.com/step3.js"></script>

We all know that this step1, STEP2, step3 if there is a dependency, you must strictly follow this order, or you may be wrong. Browsers can request step1 and step3 files in parallel, but the order of execution is guaranteed. If we ourselves XMLHttpRequest get the contents of Step1 and Step3 files, we need to ensure that the order is correct. In addition, don't forget to Step2, step1 in the form of non-blocking download Step2 can be executed, so we must also artificially intervene step2 let it wait for Step1 to complete and then execute.

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

<script>
scheduleRemoteScript('http://cdn.com/step1.js');
</script>
<script>
scheduleInlineScript(function code() {
(function step2() {})();
});
</script>
<script>
scheduleRemoteScript('http://cdn.com/step3.js');
</script>

We need to implement the scheduleRemoteScript scheduleInlineScript two functions and make sure they are defined before the first tag that references the external script file <script> , and then the remaining <script> tags will be rewritten in this form. Notice that the function that step2 was executed immediately was put into a larger code function. The code function is not executed, it is just a container, so that the originally Step2 code can be preserved without escaping, but it will not be executed immediately.

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

Line number Reverse Check

Getting content through CORS and then injecting code into the page can break the security limit, but introduces a new problem, which is line number conflict. It was originally possible to navigate to a error.script unique script file, and then error.line navigate to a unique line number. Now because the page is embedded in the code, multiple <script> tags can not be error.script differentiated by, however, each <script> tag inside the line number from 1, the result is that we can not use the exception information to locate the source code location of the error.

To avoid line number collisions, we can waste a number of line numbers so that the <script> line number intervals used by the actual code in each label do not overlap. For example, assuming that the <script> actual code in each label is no more than 1000 lines, I can have <script> the code in the first tab occupy the 1–1000 line, so that <script> the code in the Second tab occupies the 1001–2000 line (insert a 1000-line blank line before), <script>the code for the three label species occupies the 2001–3000 line (insert a 2000-line blank line before), and so on. Then we use data-* attributes to record this information for easy retrieval.

<script
data-src="http://cdn.com/step1.js"
data-line-start="1"
>
// code for step 1
</script>
<script data-line-start="1001">
// '\n' * 1000
// code for step 2
</script>
<script
data-src="http://cdn.com/step3.js"
data-line-start="2001"
>
// '\n' * 2000
// 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 reportError complete this line number in the previous mentioned function to check the work.

Of course, since we have no way to 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 allocate 1000 lines of the interval to each <script> label. We can allocate intervals based on the number of actual script lines, as long as <script> the intervals used by each tag do not overlap.

Crossorigin Property

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

<script>The restrictions that refer to different source script files for labels also work on tags referencing different source picture files. If a label is a different source, once 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 tag solves the problem by introducing attributes. If used crossorigin="anonymous" , it is equivalent to an anonymous CORS, and if the ' crossorigin= ' use-credentials is used, it is equivalent to a CORS with authentication.

Since labels can do this, why <script> can't labels do that? The browser vendor then <script> adds the same attribute to the label to crossorigin address the security restrictions mentioned above. Now the Chrome and Firefox support for this property is completely fine. Safari treats it crossorigin="anonymous" crossorigin="use-credentials" as if the server only supports anonymous CORS and Safari will fail as a certificate. Because the CDN server is designed to return only static content because of its performance, it is impossible to dynamically return the required HTTP Header,safari of the authentication CORS according to the request equivalent to not being able to use this feature to solve the above problem.

Summary

JavaScript exception handling looks very simple, it's no different from other languages, but it's really not easy to capture the exceptions and then analyze the attributes. Now, although there are third-party services that provide a Google Analytics service to capture JavaScript exceptions, it is necessary to make sure that the details and principles are made in your own hands.

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.