Path: blob/trunk/third_party/closure/goog/debug/errorreporter.js
2868 views
// Copyright 2009 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Definition of the ErrorReporter class, which creates an error16* handler that reports any errors raised to a URL.17*18*/1920goog.provide('goog.debug.ErrorReporter');21goog.provide('goog.debug.ErrorReporter.ExceptionEvent');2223goog.require('goog.asserts');24goog.require('goog.debug');25goog.require('goog.debug.Error');26goog.require('goog.debug.ErrorHandler');27goog.require('goog.debug.entryPointRegistry');28goog.require('goog.events');29goog.require('goog.events.Event');30goog.require('goog.events.EventTarget');31goog.require('goog.log');32goog.require('goog.net.XhrIo');33goog.require('goog.object');34goog.require('goog.string');35goog.require('goog.uri.utils');36goog.require('goog.userAgent');37383940/**41* Constructs an error reporter. Internal Use Only. To install an error42* reporter see the {@see #install} method below.43*44* @param {string} handlerUrl The URL to which all errors will be reported.45* @param {function(!Error, !Object<string, string>)=}46* opt_contextProvider When a report is to be sent to the server,47* this method will be called, and given an opportunity to modify the48* context object before submission to the server.49* @param {boolean=} opt_noAutoProtect Whether to automatically add handlers for50* onerror and to protect entry points. If apps have other error reporting51* facilities, it may make sense for them to set these up themselves and use52* the ErrorReporter just for transmission of reports.53* @constructor54* @extends {goog.events.EventTarget}55*/56goog.debug.ErrorReporter = function(57handlerUrl, opt_contextProvider, opt_noAutoProtect) {58goog.debug.ErrorReporter.base(this, 'constructor');5960/**61* Context provider, if one was provided.62* @type {?function(!Error, !Object<string, string>)}63* @private64*/65this.contextProvider_ = opt_contextProvider || null;6667/**68* The string prefix of any optional context parameters logged with the error.69* @private {string}70*/71this.contextPrefix_ = 'context.';7273/**74* The number of bytes after which the ErrorReporter truncates the POST body.75* If null, the ErrorReporter won't truncate the body.76* @private {?number}77*/78this.truncationLimit_ = null;7980/**81* Additional arguments to append to URL before sending XHR.82* @private {!Object<string,string>}83*/84this.additionalArguments_ = {};8586/**87* XHR sender.88* @type {function(string, string, string, (Object|goog.structs.Map)=)}89* @private90*/91this.xhrSender_ = goog.debug.ErrorReporter.defaultXhrSender;9293/**94* The URL at which all errors caught by this handler will be logged.95*96* @type {string}97* @private98*/99this.handlerUrl_ = handlerUrl;100101if (goog.debug.ErrorReporter.ALLOW_AUTO_PROTECT) {102if (!opt_noAutoProtect) {103/**104* The internal error handler used to catch all errors.105*106* @private {goog.debug.ErrorHandler}107*/108this.errorHandler_ = null;109110this.setup_();111}112} else if (!opt_noAutoProtect) {113goog.asserts.fail(114'opt_noAutoProtect cannot be false while ' +115'goog.debug.ErrorReporter.ALLOW_AUTO_PROTECT is false. Setting ' +116'ALLOW_AUTO_PROTECT to false removes the necessary auto-protect code ' +117'in compiled/optimized mode.');118}119};120goog.inherits(goog.debug.ErrorReporter, goog.events.EventTarget);121122123/**124* @define {boolean} If true, the code that provides additional entry point125* protection and setup is exposed in this file. Set to false to avoid126* bringing in a lot of code from ErrorHandler and entryPointRegistry in127* compiled mode.128*/129goog.define('goog.debug.ErrorReporter.ALLOW_AUTO_PROTECT', true);130131132133/**134* Event broadcast when an exception is logged.135* @param {Error} error The exception that was was reported.136* @param {!Object<string, string>} context The context values sent to the137* server alongside this error.138* @constructor139* @extends {goog.events.Event}140* @final141*/142goog.debug.ErrorReporter.ExceptionEvent = function(error, context) {143goog.events.Event.call(this, goog.debug.ErrorReporter.ExceptionEvent.TYPE);144145/**146* The error that was reported.147* @type {Error}148*/149this.error = error;150151/**152* Context values sent to the server alongside this report.153* @type {!Object<string, string>}154*/155this.context = context;156};157goog.inherits(goog.debug.ErrorReporter.ExceptionEvent, goog.events.Event);158159160/**161* Event type for notifying of a logged exception.162* @type {string}163*/164goog.debug.ErrorReporter.ExceptionEvent.TYPE =165goog.events.getUniqueId('exception');166167168/**169* Extra headers for the error-reporting XHR.170* @type {Object|goog.structs.Map|undefined}171* @private172*/173goog.debug.ErrorReporter.prototype.extraHeaders_;174175176/**177* Logging object.178*179* @type {goog.log.Logger}180* @private181*/182goog.debug.ErrorReporter.logger_ =183goog.log.getLogger('goog.debug.ErrorReporter');184185186/**187* Installs an error reporter to catch all JavaScript errors raised.188*189* @param {string} loggingUrl The URL to which the errors caught will be190* reported.191* @param {function(!Error, !Object<string, string>)=}192* opt_contextProvider When a report is to be sent to the server,193* this method will be called, and given an opportunity to modify the194* context object before submission to the server.195* @param {boolean=} opt_noAutoProtect Whether to automatically add handlers for196* onerror and to protect entry points. If apps have other error reporting197* facilities, it may make sense for them to set these up themselves and use198* the ErrorReporter just for transmission of reports.199* @return {!goog.debug.ErrorReporter} The error reporter.200*/201goog.debug.ErrorReporter.install = function(202loggingUrl, opt_contextProvider, opt_noAutoProtect) {203var instance = new goog.debug.ErrorReporter(204loggingUrl, opt_contextProvider, opt_noAutoProtect);205return instance;206};207208209/**210* Default implementation of XHR sender interface.211*212* @param {string} uri URI to make request to.213* @param {string} method Send method.214* @param {string} content Post data.215* @param {Object|goog.structs.Map=} opt_headers Map of headers to add to the216* request.217*/218goog.debug.ErrorReporter.defaultXhrSender = function(219uri, method, content, opt_headers) {220goog.net.XhrIo.send(uri, null, method, content, opt_headers);221};222223224/**225* Installs exception protection for an entry point function in addition226* to those that are protected by default.227* Has no effect in IE because window.onerror is used for reporting228* exceptions in that case.229*230* @this {goog.debug.ErrorReporter}231* @param {Function} fn An entry point function to be protected.232* @return {Function} A protected wrapper function that calls the entry point233* function or null if the entry point could not be protected.234*/235goog.debug.ErrorReporter.prototype.protectAdditionalEntryPoint =236goog.debug.ErrorReporter.ALLOW_AUTO_PROTECT ? function(fn) {237if (this.errorHandler_) {238return this.errorHandler_.protectEntryPoint(fn);239}240return null;241} : function(fn) {242goog.asserts.fail(243'Cannot call protectAdditionalEntryPoint while ALLOW_AUTO_PROTECT ' +244'is false. If ALLOW_AUTO_PROTECT is false, the necessary ' +245'auto-protect code in compiled/optimized mode is removed.');246return null;247};248249250if (goog.debug.ErrorReporter.ALLOW_AUTO_PROTECT) {251/**252* Sets up the error reporter.253*254* @private255*/256goog.debug.ErrorReporter.prototype.setup_ = function() {257if (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('10')) {258// Use "onerror" because caught exceptions in IE don't provide line259// number.260goog.debug.catchErrors(261goog.bind(this.handleException, this), false, null);262} else {263// "onerror" doesn't work with FF2 or Chrome264this.errorHandler_ =265new goog.debug.ErrorHandler(goog.bind(this.handleException, this));266267this.errorHandler_.protectWindowSetTimeout();268this.errorHandler_.protectWindowSetInterval();269this.errorHandler_.protectWindowRequestAnimationFrame();270goog.debug.entryPointRegistry.monitorAll(this.errorHandler_);271}272};273}274275276/**277* Add headers to the logging url.278* @param {Object|goog.structs.Map} loggingHeaders Extra headers to send279* to the logging URL.280*/281goog.debug.ErrorReporter.prototype.setLoggingHeaders = function(282loggingHeaders) {283this.extraHeaders_ = loggingHeaders;284};285286287/**288* Set the function used to send error reports to the server.289* @param {function(string, string, string, (Object|goog.structs.Map)=)}290* xhrSender If provided, this will be used to send a report to the291* server instead of the default method. The function will be given the URI,292* HTTP method request content, and (optionally) request headers to be293* added.294*/295goog.debug.ErrorReporter.prototype.setXhrSender = function(xhrSender) {296this.xhrSender_ = xhrSender;297};298299300/**301* Handler for caught exceptions. Sends report to the LoggingServlet and302* notifies any listeners.303*304* @param {Object} e The exception.305* @param {!Object<string, string>=} opt_context Context values to optionally306* include in the error report.307*/308goog.debug.ErrorReporter.prototype.handleException = function(e, opt_context) {309var error = /** @type {!Error} */ (goog.debug.normalizeErrorObject(e));310311// Construct the context, possibly from the one provided in the argument, and312// pass it to the context provider if there is one.313var context = opt_context ? goog.object.clone(opt_context) : {};314if (this.contextProvider_) {315try {316this.contextProvider_(error, context);317} catch (err) {318goog.log.error(319goog.debug.ErrorReporter.logger_,320'Context provider threw an exception: ' + err.message);321}322}323// Truncate message to a reasonable length, since it will be sent in the URL.324// The entire URL length historically needed to be 2,083 or less, so leave325// some room for the rest of the URL.326var message = error.message.substring(0, 1900);327if (!(e instanceof goog.debug.Error) || e.reportErrorToServer) {328this.sendErrorReport(329message, error.fileName, error.lineNumber, error.stack, context);330}331332try {333this.dispatchEvent(334new goog.debug.ErrorReporter.ExceptionEvent(error, context));335} catch (ex) {336// Swallow exception to avoid infinite recursion.337}338};339340341/**342* Sends an error report to the logging URL. This will not consult the context343* provider, the report will be sent exactly as specified.344*345* @param {string} message Error description.346* @param {string} fileName URL of the JavaScript file with the error.347* @param {number} line Line number of the error.348* @param {string=} opt_trace Call stack trace of the error.349* @param {!Object<string, string>=} opt_context Context information to include350* in the request.351*/352goog.debug.ErrorReporter.prototype.sendErrorReport = function(353message, fileName, line, opt_trace, opt_context) {354try {355// Create the logging URL.356var requestUrl = goog.uri.utils.appendParams(357this.handlerUrl_, 'script', fileName, 'error', message, 'line', line);358359if (!goog.object.isEmpty(this.additionalArguments_)) {360requestUrl = goog.uri.utils.appendParamsFromMap(361requestUrl, this.additionalArguments_);362}363364var queryMap = {};365queryMap['trace'] = opt_trace;366367// Copy context into query data map368if (opt_context) {369for (var entry in opt_context) {370queryMap[this.contextPrefix_ + entry] = opt_context[entry];371}372}373374// Copy query data map into request.375var queryData = goog.uri.utils.buildQueryDataFromMap(queryMap);376377// Truncate if truncationLimit set.378if (goog.isNumber(this.truncationLimit_)) {379queryData = queryData.substring(0, this.truncationLimit_);380}381382// Send the request with the contents of the error.383this.xhrSender_(requestUrl, 'POST', queryData, this.extraHeaders_);384} catch (e) {385var logMessage = goog.string.buildString(386'Error occurred in sending an error report.\n\n', 'script:', fileName,387'\n', 'line:', line, '\n', 'error:', message, '\n', 'trace:',388opt_trace);389goog.log.info(goog.debug.ErrorReporter.logger_, logMessage);390}391};392393394/**395* @param {string} prefix The prefix to appear prepended to all context396* variables in the error report body.397*/398goog.debug.ErrorReporter.prototype.setContextPrefix = function(prefix) {399this.contextPrefix_ = prefix;400};401402403/**404* @param {?number} limit Size in bytes to begin truncating POST body. Set to405* null to prevent truncation. The limit must be >= 0.406*/407goog.debug.ErrorReporter.prototype.setTruncationLimit = function(limit) {408goog.asserts.assert(409!goog.isNumber(limit) || limit >= 0,410'Body limit must be valid number >= 0 or null');411this.truncationLimit_ = limit;412};413414415/**416* @param {!Object<string,string>} urlArgs Set of key-value pairs to append417* to handlerUrl_ before sending XHR.418*/419goog.debug.ErrorReporter.prototype.setAdditionalArguments = function(urlArgs) {420this.additionalArguments_ = urlArgs;421};422423424/** @override */425goog.debug.ErrorReporter.prototype.disposeInternal = function() {426if (goog.debug.ErrorReporter.ALLOW_AUTO_PROTECT) {427goog.dispose(this.errorHandler_);428}429goog.debug.ErrorReporter.base(this, 'disposeInternal');430};431432433