Skip to content

Commit

Permalink
POST error messages to reporting server (ampproject#11913)
Browse files Browse the repository at this point in the history
* POST error messages to reporting server

Fixes ampproject#8849.

* Fix types
  • Loading branch information
jridgewell authored and neko-fire committed Nov 17, 2017
1 parent efea8eb commit 52f38fb
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 232 deletions.
93 changes: 42 additions & 51 deletions src/error.js
Expand Up @@ -263,11 +263,13 @@ function reportErrorToServer(message, filename, line, col, error) {
// due to buggy browser extensions may be helpful to notify authors.
return;
}
const url = getErrorReportUrl(message, filename, line, col, error,
const data = getErrorReportData(message, filename, line, col, error,
hasNonAmpJs);
if (url) {
if (data) {
reportingBackoff(() => {
new Image().src = url;
const xhr = new XMLHttpRequest();
xhr.open('POST', urls.errorReporting, true);
xhr.send(JSON.stringify(data));
});
}
}
Expand All @@ -280,10 +282,10 @@ function reportErrorToServer(message, filename, line, col, error) {
* @param {string|undefined} col
* @param {*|undefined} error
* @param {boolean} hasNonAmpJs
* @return {string|undefined} The URL
* @return {!JsonObject|undefined} The data to post
* visibleForTesting
*/
export function getErrorReportUrl(message, filename, line, col, error,
export function getErrorReportData(message, filename, line, col, error,
hasNonAmpJs) {
let expected = false;
if (error) {
Expand Down Expand Up @@ -335,108 +337,97 @@ export function getErrorReportUrl(message, filename, line, col, error,
}

// This is the App Engine app in
// ../tools/errortracker
// https://github.com/ampproject/error-tracker
// It stores error reports via https://cloud.google.com/error-reporting/
// for analyzing production issues.
let url = urls.errorReporting +
'?v=' + getMode().rtvVersion +
'&noAmp=' + (hasNonAmpJs ? 1 : 0) +
'&m=' + encodeURIComponent(message.replace(USER_ERROR_SENTINEL, '')) +
'&a=' + (isUserError ? 1 : 0);
if (expected) {
// Errors are tagged with "ex" ("expected") label to allow loggers to
// classify these errors as benchmarks and not exceptions.
url += '&ex=1';
}
const data = /** @type {!JsonObject} */ (Object.create(null));
data['v'] = getMode().rtvVersion;
data['noAmp'] = hasNonAmpJs ? '1' : '0';
data['m'] = message.replace(USER_ERROR_SENTINEL, '');
data['a'] = isUserError ? '1' : '0';

// Errors are tagged with "ex" ("expected") label to allow loggers to
// classify these errors as benchmarks and not exceptions.
data['ex'] = expected ? '1' : '0';

let runtime = '1p';
if (self.context && self.context.location) {
url += '&3p=1';
data['3p'] = '1';
runtime = '3p';
} else if (getMode().runtime) {
runtime = getMode().runtime;
}
url += '&rt=' + runtime;
data['rt'] = runtime;

// TODO(erwinm): Remove ca when all systems read `bt` instead of `ca` to
// identify js binary type.
if (isCanary(self)) {
url += '&ca=1';
}
data['ca'] = isCanary(self) ? '1' : '0';

// Pass binary type.
url += '&bt=' + getBinaryType(self);
data['bt'] = getBinaryType(self);

if (self.location.ancestorOrigins && self.location.ancestorOrigins[0]) {
url += '&or=' + encodeURIComponent(self.location.ancestorOrigins[0]);
data['or'] = self.location.ancestorOrigins[0];
}
if (self.viewerState) {
url += '&vs=' + encodeURIComponent(self.viewerState);
data['vs'] = self.viewerState;
}
// Is embedded?
if (self.parent && self.parent != self) {
url += '&iem=1';
data['iem'] = '1';
}

if (self.AMP && self.AMP.viewer) {
const resolvedViewerUrl = self.AMP.viewer.getResolvedViewerUrl();
const messagingOrigin = self.AMP.viewer.maybeGetMessagingOrigin();
if (resolvedViewerUrl) {
url += `&rvu=${encodeURIComponent(resolvedViewerUrl)}`;
data['rvu'] = resolvedViewerUrl;
}
if (messagingOrigin) {
url += `&mso=${encodeURIComponent(messagingOrigin)}`;
data['mso'] = messagingOrigin;
}
}

if (!detectedJsEngine) {
detectedJsEngine = detectJsEngineFromStack();
}
url += `&jse=${detectedJsEngine}`;
data['jse'] = detectedJsEngine;

const exps = [];
const experiments = experimentTogglesOrNull(self);
for (const exp in experiments) {
const on = experiments[exp];
exps.push(`${exp}=${on ? '1' : '0'}`);
}
url += `&exps=${encodeURIComponent(exps.join(','))}`;
data['exps'] = exps.join(',');

if (error) {
const tagName = error && error.associatedElement
? error.associatedElement.tagName
: 'u'; // Unknown
url += `&el=${encodeURIComponent(tagName)}`;
data['el'] = tagName;

if (error.args) {
url += `&args=${encodeURIComponent(JSON.stringify(error.args))}`;
data['args'] = JSON.stringify(error.args);
}

if (!isUserError && !error.ignoreStack && error.stack) {
// Shorten
const stack = (error.stack || '').substr(0, 1000);
url += `&s=${encodeURIComponent(stack)}`;
data['s'] = error.stack;
}

error.message += ' _reported_';
} else {
url += '&f=' + encodeURIComponent(filename || '') +
'&l=' + encodeURIComponent(line || '') +
'&c=' + encodeURIComponent(col || '');
data['f'] = filename || '';
data['l'] = line || '';
data['c'] = col || '';
}
url += '&r=' + encodeURIComponent(self.document.referrer);
url += '&ae=' + encodeURIComponent(accumulatedErrorMessages.join(','));
data['r'] = self.document.referrer;
data['ae'] = accumulatedErrorMessages.join(',');
data['fr'] = self.location.originalHash || self.location.hash;

accumulatedErrorMessages.push(message);
url += '&fr=' + encodeURIComponent(self.location.originalHash
|| self.location.hash);

// Google App Engine maximum URL length.
if (url.length >= 2072) {
url = url.substr(0, 2072 - 8 /* length of suffix */)
// Full remove last URL encoded entity.
.replace(/\%[^&%]+$/, '')
// Sentinel
+ '&SHORT=1';
}
return url;

return data;
}

/**
Expand Down

0 comments on commit 52f38fb

Please sign in to comment.