PageRenderTime 45ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/src/error.js

https://gitlab.com/Blueprint-Marketing/amphtml
JavaScript | 186 lines | 114 code | 13 blank | 59 comment | 42 complexity | 329a2c7314a675217fe43fe721346b6d MD5 | raw file
  1. /**
  2. * Copyright 2015 The AMP HTML Authors. All Rights Reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS-IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import {getMode} from './mode';
  17. import {exponentialBackoff} from './exponential-backoff';
  18. // TODO(dvoytenko, #2527): Remove ASSERT_SENTINEL and isAssertErrorMessage.
  19. import {ASSERT_SENTINEL, isAssertErrorMessage} from './asserts';
  20. import {USER_ERROR_SENTINEL, isUserErrorMessage} from './log';
  21. import {makeBodyVisible} from './styles';
  22. const globalExponentialBackoff = exponentialBackoff(1.5);
  23. const CANCELLED = 'CANCELLED';
  24. /**
  25. * Reports an error. If the error has an "associatedElement" property
  26. * the element is marked with the -amp-element-error and displays
  27. * the message itself. The message is always send to the console.
  28. * If the error has a "messageArray" property, that array is logged.
  29. * This way one gets the native fidelity of the console for things like
  30. * elements instead of stringification.
  31. * @param {!Error} error
  32. * @param {!Element=} opt_associatedElement
  33. */
  34. export function reportError(error, opt_associatedElement) {
  35. if (!window.console) {
  36. return;
  37. }
  38. if (!error) {
  39. error = new Error('no error supplied');
  40. }
  41. if (error.reported) {
  42. return;
  43. }
  44. error.reported = true;
  45. const element = opt_associatedElement || error.associatedElement;
  46. if (element && element.classList) {
  47. element.classList.add('-amp-error');
  48. if (getMode().development) {
  49. element.classList.add('-amp-element-error');
  50. element.setAttribute('error-message', error.message);
  51. }
  52. }
  53. if (error.messageArray) {
  54. (console.error || console.log).apply(console,
  55. error.messageArray);
  56. } else {
  57. if (element) {
  58. (console.error || console.log).call(console,
  59. element.tagName + '#' + element.id, error.message);
  60. } else {
  61. (console.error || console.log).call(console, error.message);
  62. }
  63. if (!getMode().minified) {
  64. (console.error || console.log).call(console, error.stack);
  65. }
  66. }
  67. if (element && element.dispatchCustomEvent) {
  68. element.dispatchCustomEvent('amp:error', error.message);
  69. }
  70. reportErrorToServer(undefined, undefined, undefined, undefined, error);
  71. }
  72. /**
  73. * Returns an error for a cancellation of a promise.
  74. * @param {string} message
  75. * @return {!Error}
  76. */
  77. export function cancellation() {
  78. return new Error(CANCELLED);
  79. }
  80. /**
  81. * Install handling of global unhandled exceptions.
  82. * @param {!Window} win
  83. */
  84. export function installErrorReporting(win) {
  85. win.onerror = reportErrorToServer;
  86. win.addEventListener('unhandledrejection', event => {
  87. reportError(event.reason || new Error('rejected promise ' + event));
  88. });
  89. }
  90. /**
  91. * Signature designed, so it can work with window.onerror
  92. * @param {string|undefined} message
  93. * @param {string|undefined} filename
  94. * @param {string|undefined} line
  95. * @param {string|undefined} col
  96. * @param {!Error|undefined} error
  97. */
  98. function reportErrorToServer(message, filename, line, col, error) {
  99. // Make an attempt to unhide the body.
  100. if (this && this.document) {
  101. makeBodyVisible(this.document);
  102. }
  103. const mode = getMode();
  104. if (mode.localDev || mode.development || mode.test) {
  105. return;
  106. }
  107. const url = getErrorReportUrl(message, filename, line, col, error);
  108. globalExponentialBackoff(() => {
  109. if (url) {
  110. new Image().src = url;
  111. }
  112. });
  113. }
  114. /**
  115. * Signature designed, so it can work with window.onerror
  116. * @param {string|undefined} message
  117. * @param {string|undefined} filename
  118. * @param {string|undefined} line
  119. * @param {string|undefined} col
  120. * @param {!Error|undefined} error
  121. * @return {string|undefined} The URL
  122. * visibleForTesting
  123. */
  124. export function getErrorReportUrl(message, filename, line, col, error) {
  125. message = error && error.message ? error.message : message;
  126. if (/_reported_/.test(message)) {
  127. return;
  128. }
  129. if (message == CANCELLED) {
  130. return;
  131. }
  132. if (!message) {
  133. message = 'Unknown error';
  134. }
  135. // This is the App Engine app in
  136. // ../tools/errortracker
  137. // It stores error reports via https://cloud.google.com/error-reporting/
  138. // for analyzing production issues.
  139. let url = 'https://amp-error-reporting.appspot.com/r' +
  140. '?v=' + encodeURIComponent('$internalRuntimeVersion$') +
  141. '&m=' + encodeURIComponent(
  142. message.replace(ASSERT_SENTINEL, '')
  143. .replace(USER_ERROR_SENTINEL, '')) +
  144. '&a=' + (isAssertErrorMessage(message) ||
  145. isUserErrorMessage(message) ? 1 : 0);
  146. if (window.context && window.context.location) {
  147. url += '&3p=1';
  148. }
  149. if (window.AMP_CONFIG && window.AMP_CONFIG.canary) {
  150. url += '&ca=1';
  151. }
  152. if (window.location.ancestorOrigins && window.location.ancestorOrigins[0]) {
  153. url += '&or=' + encodeURIComponent(window.location.ancestorOrigins[0]);
  154. }
  155. if (window.viewerState) {
  156. url += '&vs=' + encodeURIComponent(window.viewerState);
  157. }
  158. if (error) {
  159. const tagName = error && error.associatedElement
  160. ? error.associatedElement.tagName
  161. : 'u'; // Unknown
  162. url += '&el=' + encodeURIComponent(tagName) +
  163. '&s=' + encodeURIComponent(error.stack || '');
  164. error.message += ' _reported_';
  165. } else {
  166. url += '&f=' + encodeURIComponent(filename) +
  167. '&l=' + encodeURIComponent(line) +
  168. '&c=' + encodeURIComponent(col || '');
  169. }
  170. url += '&r=' + encodeURIComponent(document.referrer);
  171. // Shorten URLs to a value all browsers will send.
  172. return url.substr(0, 2000);
  173. }