UNPKG

rollbar

Version:

Effortlessly track and debug errors in your JavaScript applications with Rollbar. This package includes advanced error tracking features and an intuitive interface to help you identify and fix issues more quickly.

606 lines (548 loc) 16.7 kB
var Client = require('../rollbar'); var _ = require('../utility'); var API = require('../api'); var logger = require('./logger'); var globals = require('./globalSetup'); var Transport = require('./transport'); var urllib = require('./url'); var transforms = require('./transforms'); var sharedTransforms = require('../transforms'); var predicates = require('./predicates'); var sharedPredicates = require('../predicates'); var errorParser = require('../errorParser'); function Rollbar(options, client) { this.options = _.handleOptions(defaultOptions, options, null, logger); this.options._configuredOptions = options; var Telemeter = this.components.telemeter; var Instrumenter = this.components.instrumenter; var polyfillJSON = this.components.polyfillJSON; this.wrapGlobals = this.components.wrapGlobals; this.scrub = this.components.scrub; var truncation = this.components.truncation; var transport = new Transport(truncation); var api = new API(this.options, transport, urllib, truncation); if (Telemeter) { this.telemeter = new Telemeter(this.options); } this.client = client || new Client(this.options, api, logger, this.telemeter, 'browser'); var gWindow = _gWindow(); var gDocument = typeof document != 'undefined' && document; this.isChrome = gWindow.chrome && gWindow.chrome.runtime; // check .runtime to avoid Edge browsers this.anonymousErrorsPending = 0; addTransformsToNotifier(this.client.notifier, this, gWindow); addPredicatesToQueue(this.client.queue); this.setupUnhandledCapture(); if (Instrumenter) { this.instrumenter = new Instrumenter( this.options, this.client.telemeter, this, gWindow, gDocument, ); this.instrumenter.instrument(); } _.setupJSON(polyfillJSON); // Used with rollbar-react for rollbar-react-native compatibility. this.rollbar = this; } var _instance = null; Rollbar.init = function (options, client) { if (_instance) { return _instance.global(options).configure(options); } _instance = new Rollbar(options, client); return _instance; }; Rollbar.prototype.components = {}; Rollbar.setComponents = function (components) { Rollbar.prototype.components = components; }; function handleUninitialized(maybeCallback) { var message = 'Rollbar is not initialized'; logger.error(message); if (maybeCallback) { maybeCallback(new Error(message)); } } Rollbar.prototype.global = function (options) { this.client.global(options); return this; }; Rollbar.global = function (options) { if (_instance) { return _instance.global(options); } else { handleUninitialized(); } }; Rollbar.prototype.configure = function (options, payloadData) { var oldOptions = this.options; var payload = {}; if (payloadData) { payload = { payload: payloadData }; } this.options = _.handleOptions(oldOptions, options, payload, logger); this.options._configuredOptions = _.handleOptions( oldOptions._configuredOptions, options, payload, ); this.client.configure(this.options, payloadData); this.instrumenter && this.instrumenter.configure(this.options); this.setupUnhandledCapture(); return this; }; Rollbar.configure = function (options, payloadData) { if (_instance) { return _instance.configure(options, payloadData); } else { handleUninitialized(); } }; Rollbar.prototype.lastError = function () { return this.client.lastError; }; Rollbar.lastError = function () { if (_instance) { return _instance.lastError(); } else { handleUninitialized(); } }; Rollbar.prototype.log = function () { var item = this._createItem(arguments); var uuid = item.uuid; this.client.log(item); return { uuid: uuid }; }; Rollbar.log = function () { if (_instance) { return _instance.log.apply(_instance, arguments); } else { var maybeCallback = _getFirstFunction(arguments); handleUninitialized(maybeCallback); } }; Rollbar.prototype.debug = function () { var item = this._createItem(arguments); var uuid = item.uuid; this.client.debug(item); return { uuid: uuid }; }; Rollbar.debug = function () { if (_instance) { return _instance.debug.apply(_instance, arguments); } else { var maybeCallback = _getFirstFunction(arguments); handleUninitialized(maybeCallback); } }; Rollbar.prototype.info = function () { var item = this._createItem(arguments); var uuid = item.uuid; this.client.info(item); return { uuid: uuid }; }; Rollbar.info = function () { if (_instance) { return _instance.info.apply(_instance, arguments); } else { var maybeCallback = _getFirstFunction(arguments); handleUninitialized(maybeCallback); } }; Rollbar.prototype.warn = function () { var item = this._createItem(arguments); var uuid = item.uuid; this.client.warn(item); return { uuid: uuid }; }; Rollbar.warn = function () { if (_instance) { return _instance.warn.apply(_instance, arguments); } else { var maybeCallback = _getFirstFunction(arguments); handleUninitialized(maybeCallback); } }; Rollbar.prototype.warning = function () { var item = this._createItem(arguments); var uuid = item.uuid; this.client.warning(item); return { uuid: uuid }; }; Rollbar.warning = function () { if (_instance) { return _instance.warning.apply(_instance, arguments); } else { var maybeCallback = _getFirstFunction(arguments); handleUninitialized(maybeCallback); } }; Rollbar.prototype.error = function () { var item = this._createItem(arguments); var uuid = item.uuid; this.client.error(item); return { uuid: uuid }; }; Rollbar.error = function () { if (_instance) { return _instance.error.apply(_instance, arguments); } else { var maybeCallback = _getFirstFunction(arguments); handleUninitialized(maybeCallback); } }; Rollbar.prototype.critical = function () { var item = this._createItem(arguments); var uuid = item.uuid; this.client.critical(item); return { uuid: uuid }; }; Rollbar.critical = function () { if (_instance) { return _instance.critical.apply(_instance, arguments); } else { var maybeCallback = _getFirstFunction(arguments); handleUninitialized(maybeCallback); } }; Rollbar.prototype.buildJsonPayload = function (item) { return this.client.buildJsonPayload(item); }; Rollbar.buildJsonPayload = function () { if (_instance) { return _instance.buildJsonPayload.apply(_instance, arguments); } else { handleUninitialized(); } }; Rollbar.prototype.sendJsonPayload = function (jsonPayload) { return this.client.sendJsonPayload(jsonPayload); }; Rollbar.sendJsonPayload = function () { if (_instance) { return _instance.sendJsonPayload.apply(_instance, arguments); } else { handleUninitialized(); } }; Rollbar.prototype.setupUnhandledCapture = function () { var gWindow = _gWindow(); if (!this.unhandledExceptionsInitialized) { if (this.options.captureUncaught || this.options.handleUncaughtExceptions) { globals.captureUncaughtExceptions(gWindow, this); if (this.wrapGlobals && this.options.wrapGlobalEventHandlers) { this.wrapGlobals(gWindow, this); } this.unhandledExceptionsInitialized = true; } } if (!this.unhandledRejectionsInitialized) { if ( this.options.captureUnhandledRejections || this.options.handleUnhandledRejections ) { globals.captureUnhandledRejections(gWindow, this); this.unhandledRejectionsInitialized = true; } } }; Rollbar.prototype.handleUncaughtException = function ( message, url, lineno, colno, error, context, ) { if (!this.options.captureUncaught && !this.options.handleUncaughtExceptions) { return; } // Chrome will always send 5+ arguments and error will be valid or null, not undefined. // If error is undefined, we have a different caller. // Chrome also sends errors from web workers with null error, but does not invoke // prepareStackTrace() for these. Test for empty url to skip them. if ( this.options.inspectAnonymousErrors && this.isChrome && error === null && url === '' ) { return 'anonymous'; } var item; var stackInfo = _.makeUnhandledStackInfo( message, url, lineno, colno, error, 'onerror', 'uncaught exception', errorParser, ); if (_.isError(error)) { item = this._createItem([message, error, context]); item._unhandledStackInfo = stackInfo; } else if (_.isError(url)) { item = this._createItem([message, url, context]); item._unhandledStackInfo = stackInfo; } else { item = this._createItem([message, context]); item.stackInfo = stackInfo; } item.level = this.options.uncaughtErrorLevel; item._isUncaught = true; this.client.log(item); }; /** * Chrome only. Other browsers will ignore. * * Use Error.prepareStackTrace to extract information about errors that * do not have a valid error object in onerror(). * * In tested version of Chrome, onerror is called first but has no way * to communicate with prepareStackTrace. Use a counter to let this * handler know which errors to send to Rollbar. * * In config options, set inspectAnonymousErrors to enable. */ Rollbar.prototype.handleAnonymousErrors = function () { if (!this.options.inspectAnonymousErrors || !this.isChrome) { return; } var r = this; function prepareStackTrace(error, _stack) { // eslint-disable-line no-unused-vars if (r.options.inspectAnonymousErrors) { if (r.anonymousErrorsPending) { // This is the only known way to detect that onerror saw an anonymous error. // It depends on onerror reliably being called before Error.prepareStackTrace, // which so far holds true on tested versions of Chrome. If versions of Chrome // are tested that behave differently, this logic will need to be updated // accordingly. r.anonymousErrorsPending -= 1; if (!error) { // Not likely to get here, but calling handleUncaughtException from here // without an error object would throw off the anonymousErrorsPending counter, // so return now. return; } // Allow this to be tracked later. error._isAnonymous = true; // url, lineno, colno shouldn't be needed for these errors. // If that changes, update this accordingly, using the unused // _stack param as needed (rather than parse error.toString()). r.handleUncaughtException(error.message, null, null, null, error); } } // Workaround to ensure stack is preserved for normal errors. return error.stack; } // https://v8.dev/docs/stack-trace-api try { Error.prepareStackTrace = prepareStackTrace; } catch (e) { this.options.inspectAnonymousErrors = false; this.error('anonymous error handler failed', e); } }; Rollbar.prototype.handleUnhandledRejection = function (reason, promise) { if ( !this.options.captureUnhandledRejections && !this.options.handleUnhandledRejections ) { return; } var message = 'unhandled rejection was null or undefined!'; if (reason) { if (reason.message) { message = reason.message; } else { var reasonResult = _.stringify(reason); if (reasonResult.value) { message = reasonResult.value; } } } var context = (reason && reason._rollbarContext) || (promise && promise._rollbarContext); var item; if (_.isError(reason)) { item = this._createItem([message, reason, context]); } else { item = this._createItem([message, reason, context]); item.stackInfo = _.makeUnhandledStackInfo( message, '', 0, 0, null, 'unhandledrejection', '', errorParser, ); } item.level = this.options.uncaughtErrorLevel; item._isUncaught = true; item._originalArgs = item._originalArgs || []; item._originalArgs.push(promise); this.client.log(item); }; Rollbar.prototype.wrap = function (f, context, _before) { try { var ctxFn; if (_.isFunction(context)) { ctxFn = context; } else { ctxFn = function () { return context || {}; }; } if (!_.isFunction(f)) { return f; } if (f._isWrap) { return f; } if (!f._rollbar_wrapped) { f._rollbar_wrapped = function () { if (_before && _.isFunction(_before)) { _before.apply(this, arguments); } try { return f.apply(this, arguments); } catch (exc) { var e = exc; if (e && window._rollbarWrappedError !== e) { if (_.isType(e, 'string')) { e = new String(e); } e._rollbarContext = ctxFn() || {}; e._rollbarContext._wrappedSource = f.toString(); window._rollbarWrappedError = e; } throw e; } }; f._rollbar_wrapped._isWrap = true; if (f.hasOwnProperty) { for (var prop in f) { if (f.hasOwnProperty(prop) && prop !== '_rollbar_wrapped') { f._rollbar_wrapped[prop] = f[prop]; } } } } return f._rollbar_wrapped; } catch (e) { // Return the original function if the wrap fails. return f; } }; Rollbar.wrap = function (f, context) { if (_instance) { return _instance.wrap(f, context); } else { handleUninitialized(); } }; Rollbar.prototype.captureEvent = function () { var event = _.createTelemetryEvent(arguments); return this.client.captureEvent(event.type, event.metadata, event.level); }; Rollbar.captureEvent = function () { if (_instance) { return _instance.captureEvent.apply(_instance, arguments); } else { handleUninitialized(); } }; // The following two methods are used internally and are not meant for public use Rollbar.prototype.captureDomContentLoaded = function (e, ts) { if (!ts) { ts = new Date(); } return this.client.captureDomContentLoaded(ts); }; Rollbar.prototype.captureLoad = function (e, ts) { if (!ts) { ts = new Date(); } return this.client.captureLoad(ts); }; /* Internal */ function addTransformsToNotifier(notifier, rollbar, gWindow) { notifier .addTransform(transforms.handleDomException) .addTransform(transforms.handleItemWithError) .addTransform(transforms.ensureItemHasSomethingToSay) .addTransform(transforms.addBaseInfo) .addTransform(transforms.addRequestInfo(gWindow)) .addTransform(transforms.addClientInfo(gWindow)) .addTransform(transforms.addPluginInfo(gWindow)) .addTransform(transforms.addBody) .addTransform(sharedTransforms.addMessageWithError) .addTransform(sharedTransforms.addTelemetryData) .addTransform(sharedTransforms.addConfigToPayload) .addTransform(transforms.addScrubber(rollbar.scrub)) .addTransform(sharedTransforms.addPayloadOptions) .addTransform(sharedTransforms.userTransform(logger)) .addTransform(sharedTransforms.addConfiguredOptions) .addTransform(sharedTransforms.addDiagnosticKeys) .addTransform(sharedTransforms.itemToPayload); } function addPredicatesToQueue(queue) { queue .addPredicate(sharedPredicates.checkLevel) .addPredicate(predicates.checkIgnore) .addPredicate(sharedPredicates.userCheckIgnore(logger)) .addPredicate(sharedPredicates.urlIsNotBlockListed(logger)) .addPredicate(sharedPredicates.urlIsSafeListed(logger)) .addPredicate(sharedPredicates.messageIsIgnored(logger)); } Rollbar.prototype.loadFull = function () { logger.info( 'Unexpected Rollbar.loadFull() called on a Notifier instance. This can happen when Rollbar is loaded multiple times.', ); }; Rollbar.prototype._createItem = function (args) { return _.createItem(args, logger, this); }; function _getFirstFunction(args) { for (var i = 0, len = args.length; i < len; ++i) { if (_.isFunction(args[i])) { return args[i]; } } return undefined; } function _gWindow() { return ( (typeof window != 'undefined' && window) || (typeof self != 'undefined' && self) ); } var defaults = require('../defaults'); var scrubFields = require('./defaults/scrubFields'); var defaultOptions = { version: defaults.version, scrubFields: scrubFields.scrubFields, logLevel: defaults.logLevel, reportLevel: defaults.reportLevel, uncaughtErrorLevel: defaults.uncaughtErrorLevel, endpoint: defaults.endpoint, verbose: false, enabled: true, transmit: true, sendConfig: false, includeItemsInTelemetry: true, captureIp: true, inspectAnonymousErrors: true, ignoreDuplicateErrors: true, wrapGlobalEventHandlers: false, }; module.exports = Rollbar;