From 7e91ce23febb458c6396821e9f04be90107c3ead Mon Sep 17 00:00:00 2001 From: Austen Lacy Date: Tue, 2 Jun 2020 13:17:49 -0400 Subject: [PATCH] Add opentracing integration (#860) * add lightstep integration * separate validation of tracer + span b/c we dont always have an active span when validating tracer at client instantiation * add item.custom data to custom field in browserjs implementation * move tracer config to Client rather than each implementation * revert spreading of data.custom attrs to root data obj in browserjs * add item.data.custom to all occurrences for both nodejs and browserjs * add item.data.custom for react-native too --- sdks/rollbar.js/src/browser/rollbar.js | 1 - sdks/rollbar.js/src/browser/transforms.js | 3 +- .../rollbar.js/src/react-native/transforms.js | 3 +- sdks/rollbar.js/src/rollbar.js | 142 +++++++++++++-- sdks/rollbar.js/src/server/rollbar.js | 166 +++++++++--------- sdks/rollbar.js/src/server/transforms.js | 3 +- sdks/rollbar.js/test/browser.rollbar.test.js | 44 +++++ .../test/react-native.rollbar.test.js | 42 +++++ sdks/rollbar.js/test/server.rollbar.test.js | 103 ++++++++++- 9 files changed, 398 insertions(+), 109 deletions(-) diff --git a/sdks/rollbar.js/src/browser/rollbar.js b/sdks/rollbar.js/src/browser/rollbar.js index 817356289..f30e6097e 100644 --- a/sdks/rollbar.js/src/browser/rollbar.js +++ b/sdks/rollbar.js/src/browser/rollbar.js @@ -19,7 +19,6 @@ function Rollbar(options, client) { this.options._configuredOptions = options; var api = new API(this.options, transport, urllib); this.client = client || new Client(this.options, api, logger, 'browser'); - var gWindow = _gWindow(); var gDocument = (typeof document != 'undefined') && document; this.isChrome = gWindow.chrome && gWindow.chrome.runtime; // check .runtime to avoid Edge browsers diff --git a/sdks/rollbar.js/src/browser/transforms.js b/sdks/rollbar.js/src/browser/transforms.js index e4a9ccfb3..ecd02f6ed 100644 --- a/sdks/rollbar.js/src/browser/transforms.js +++ b/sdks/rollbar.js/src/browser/transforms.js @@ -53,7 +53,8 @@ function addBaseInfo(item, options, callback) { notifier: { name: 'rollbar-browser-js', version: options.version - } + }, + custom: item.custom }); callback(null, item); } diff --git a/sdks/rollbar.js/src/react-native/transforms.js b/sdks/rollbar.js/src/react-native/transforms.js index 747bfa27a..5db21a865 100644 --- a/sdks/rollbar.js/src/react-native/transforms.js +++ b/sdks/rollbar.js/src/react-native/transforms.js @@ -11,7 +11,8 @@ function baseData(item, options, callback) { language: 'javascript', framework: item.framework || options.framework, uuid: item.uuid, - notifier: JSON.parse(JSON.stringify(options.notifier)) + notifier: JSON.parse(JSON.stringify(options.notifier)), + custom: item.custom }; if (options.codeVersion) { diff --git a/sdks/rollbar.js/src/rollbar.js b/sdks/rollbar.js/src/rollbar.js index e1dca25ae..82469045d 100644 --- a/sdks/rollbar.js/src/rollbar.js +++ b/sdks/rollbar.js/src/rollbar.js @@ -18,6 +18,18 @@ function Rollbar(options, api, logger, platform) { Rollbar.rateLimiter.setPlatformOptions(platform, this.options); this.api = api; this.queue = new Queue(Rollbar.rateLimiter, api, logger, this.options); + + // This must happen before the Notifier is created + var tracer = this.options.tracer || null; + if (validateTracer(tracer)) { + this.tracer = tracer; + // set to a string for api response serialization + this.options.tracer = 'opentracing-tracer-enabled'; + this.options._configuredOptions.tracer = 'opentracing-tracer-enabled'; + } else { + this.tracer = null; + } + this.notifier = new Notifier(this.queue, this.options); this.telemeter = new Telemeter(this.options); setStackTraceLimit(options); @@ -32,81 +44,99 @@ var defaultOptions = { Rollbar.rateLimiter = new RateLimiter(defaultOptions); -Rollbar.prototype.global = function(options) { +Rollbar.prototype.global = function (options) { Rollbar.rateLimiter.configureGlobal(options); return this; }; -Rollbar.prototype.configure = function(options, payloadData) { +Rollbar.prototype.configure = function (options, payloadData) { var oldOptions = this.options; var payload = {}; if (payloadData) { - payload = {payload: payloadData}; + payload = { payload: payloadData }; } + this.options = _.merge(oldOptions, options, payload); + + // This must happen before the Notifier is configured + var tracer = this.options.tracer || null; + if (validateTracer(tracer)) { + this.tracer = tracer; + // set to a string for api response serialization + this.options.tracer = 'opentracing-tracer-enabled'; + this.options._configuredOptions.tracer = 'opentracing-tracer-enabled'; + } else { + this.tracer = null; + } + this.notifier && this.notifier.configure(this.options); this.telemeter && this.telemeter.configure(this.options); setStackTraceLimit(options); this.global(this.options); + + if (validateTracer(options.tracer)) { + this.tracer = options.tracer + } + return this; }; -Rollbar.prototype.log = function(item) { +Rollbar.prototype.log = function (item) { var level = this._defaultLogLevel(); return this._log(level, item); }; -Rollbar.prototype.debug = function(item) { +Rollbar.prototype.debug = function (item) { this._log('debug', item); }; -Rollbar.prototype.info = function(item) { +Rollbar.prototype.info = function (item) { this._log('info', item); }; -Rollbar.prototype.warn = function(item) { +Rollbar.prototype.warn = function (item) { this._log('warning', item); }; -Rollbar.prototype.warning = function(item) { +Rollbar.prototype.warning = function (item) { this._log('warning', item); }; -Rollbar.prototype.error = function(item) { +Rollbar.prototype.error = function (item) { this._log('error', item); }; -Rollbar.prototype.critical = function(item) { +Rollbar.prototype.critical = function (item) { this._log('critical', item); }; -Rollbar.prototype.wait = function(callback) { +Rollbar.prototype.wait = function (callback) { this.queue.wait(callback); }; -Rollbar.prototype.captureEvent = function(type, metadata, level) { +Rollbar.prototype.captureEvent = function (type, metadata, level) { return this.telemeter.captureEvent(type, metadata, level); }; -Rollbar.prototype.captureDomContentLoaded = function(ts) { +Rollbar.prototype.captureDomContentLoaded = function (ts) { return this.telemeter.captureDomContentLoaded(ts); }; -Rollbar.prototype.captureLoad = function(ts) { +Rollbar.prototype.captureLoad = function (ts) { return this.telemeter.captureLoad(ts); }; -Rollbar.prototype.buildJsonPayload = function(item) { +Rollbar.prototype.buildJsonPayload = function (item) { return this.api.buildJsonPayload(item); }; -Rollbar.prototype.sendJsonPayload = function(jsonPayload) { +Rollbar.prototype.sendJsonPayload = function (jsonPayload) { this.api.postJsonPayload(jsonPayload); }; /* Internal */ -Rollbar.prototype._log = function(defaultLevel, item) { +Rollbar.prototype._log = function (defaultLevel, item) { var callback; if (item.callback) { callback = item.callback; @@ -121,6 +151,7 @@ Rollbar.prototype._log = function(defaultLevel, item) { return; } try { + this._addTracingInfo(item); item.level = item.level || defaultLevel; this.telemeter._captureRollbarItem(item); item.telemetryEvents = this.telemeter.copyEvents(); @@ -130,11 +161,11 @@ Rollbar.prototype._log = function(defaultLevel, item) { } }; -Rollbar.prototype._defaultLogLevel = function() { +Rollbar.prototype._defaultLogLevel = function () { return this.options.logLevel || 'debug'; }; -Rollbar.prototype._sameAsLastError = function(item) { +Rollbar.prototype._sameAsLastError = function (item) { if (!item._isUncaught) { return false; } @@ -147,6 +178,34 @@ Rollbar.prototype._sameAsLastError = function(item) { return false; }; +Rollbar.prototype._addTracingInfo = function (item) { + // Tracer validation occurs in the constructor + // or in the Rollbar.prototype.configure methods + if (this.tracer) { + // add rollbar occurrence uuid to span + var span = this.tracer.scope().active(); + + if (validateSpan(span)) { + span.setTag('rollbar_uuid', item.uuid); + span.setTag('has_rollbar_error', true); + + // add span ID & trace ID to occurrence + var opentracingSpanId = span.context().toSpanId(); + var opentracingTraceId = span.context().toTraceId(); + + if (item.custom) { + item.custom.opentracing_span_id = opentracingSpanId; + item.custom.opentracing_trace_id = opentracingTraceId; + } else { + item.custom = { + opentracing_span_id: opentracingSpanId, + opentracing_trace_id: opentracingTraceId + }; + } + } + } +} + function generateItemHash(item) { var message = item.message || ''; var stack = (item.err || {}).stack || String(item.err); @@ -162,4 +221,49 @@ function setStackTraceLimit(options) { } } +/** + * Validate the Tracer object provided to the Client + * is valid for our Opentracing use case. + * @param {opentracer.Tracer} tracer + */ +function validateTracer(tracer) { + if (!tracer) { + return false; + } + + if (!tracer.scope || typeof tracer.scope !== 'function') { + return false; + } + + const scope = tracer.scope(); + + if (!scope || !scope.active || typeof scope.active !== 'function') { + return false; + } + + return true; +} + +/** + * Validate the Span object provided + * @param {opentracer.Span} span + */ +function validateSpan(span) { + if (!span || !span.context || typeof span.context !== 'function') { + return false; + } + + const spanContext = span.context(); + + if (!spanContext + || !spanContext.toSpanId + || !spanContext.toTraceId + || typeof spanContext.toSpanId !== 'function' + || typeof spanContext.toTraceId !== 'function') { + return false + } + + return true; +} + module.exports = Rollbar; diff --git a/sdks/rollbar.js/src/server/rollbar.js b/sdks/rollbar.js/src/server/rollbar.js index 05304ff73..7912cba65 100644 --- a/sdks/rollbar.js/src/server/rollbar.js +++ b/sdks/rollbar.js/src/server/rollbar.js @@ -42,7 +42,7 @@ function Rollbar(options, client) { } var _instance = null; -Rollbar.init = function(options, client) { +Rollbar.init = function (options, client) { if (_instance) { return _instance.global(options).configure(options); } @@ -58,14 +58,14 @@ function handleUninitialized(maybeCallback) { } } -Rollbar.prototype.global = function(options) { +Rollbar.prototype.global = function (options) { options = _.handleOptions(options); // On the server we want to ignore any maxItems setting delete options.maxItems; this.client.global(options); return this; }; -Rollbar.global = function(options) { +Rollbar.global = function (options) { if (_instance) { return _instance.global(options); } else { @@ -73,11 +73,11 @@ Rollbar.global = function(options) { } }; -Rollbar.prototype.configure = function(options, payloadData) { +Rollbar.prototype.configure = function (options, payloadData) { var oldOptions = this.options; var payload = {}; if (payloadData) { - payload = {payload: payloadData}; + payload = { payload: payloadData }; } this.options = _.handleOptions(oldOptions, options, payload); this.options._configuredOptions = _.handleOptions(oldOptions._configuredOptions, options, payload); @@ -88,7 +88,7 @@ Rollbar.prototype.configure = function(options, payloadData) { this.setupUnhandledCapture(); return this; }; -Rollbar.configure = function(options, payloadData) { +Rollbar.configure = function (options, payloadData) { if (_instance) { return _instance.configure(options, payloadData); } else { @@ -96,10 +96,10 @@ Rollbar.configure = function(options, payloadData) { } }; -Rollbar.prototype.lastError = function() { +Rollbar.prototype.lastError = function () { return this.client.lastError; }; -Rollbar.lastError = function() { +Rollbar.lastError = function () { if (_instance) { return _instance.lastError(); } else { @@ -107,13 +107,13 @@ Rollbar.lastError = function() { } }; -Rollbar.prototype.log = function() { +Rollbar.prototype.log = function () { var item = this._createItem(arguments); var uuid = item.uuid; this.client.log(item); - return {uuid: uuid}; + return { uuid: uuid }; }; -Rollbar.log = function() { +Rollbar.log = function () { if (_instance) { return _instance.log.apply(_instance, arguments); } else { @@ -122,13 +122,13 @@ Rollbar.log = function() { } }; -Rollbar.prototype.debug = function() { +Rollbar.prototype.debug = function () { var item = this._createItem(arguments); var uuid = item.uuid; this.client.debug(item); - return {uuid: uuid}; + return { uuid: uuid }; }; -Rollbar.debug = function() { +Rollbar.debug = function () { if (_instance) { return _instance.debug.apply(_instance, arguments); } else { @@ -137,13 +137,13 @@ Rollbar.debug = function() { } }; -Rollbar.prototype.info = function() { +Rollbar.prototype.info = function () { var item = this._createItem(arguments); var uuid = item.uuid; this.client.info(item); - return {uuid: uuid}; + return { uuid: uuid }; }; -Rollbar.info = function() { +Rollbar.info = function () { if (_instance) { return _instance.info.apply(_instance, arguments); } else { @@ -152,13 +152,13 @@ Rollbar.info = function() { } }; -Rollbar.prototype.warn = function() { +Rollbar.prototype.warn = function () { var item = this._createItem(arguments); var uuid = item.uuid; this.client.warn(item); - return {uuid: uuid}; + return { uuid: uuid }; }; -Rollbar.warn = function() { +Rollbar.warn = function () { if (_instance) { return _instance.warn.apply(_instance, arguments); } else { @@ -168,13 +168,13 @@ Rollbar.warn = function() { }; -Rollbar.prototype.warning = function() { +Rollbar.prototype.warning = function () { var item = this._createItem(arguments); var uuid = item.uuid; this.client.warning(item); - return {uuid: uuid}; + return { uuid: uuid }; }; -Rollbar.warning = function() { +Rollbar.warning = function () { if (_instance) { return _instance.warning.apply(_instance, arguments); } else { @@ -184,13 +184,13 @@ Rollbar.warning = function() { }; -Rollbar.prototype.error = function() { +Rollbar.prototype.error = function () { var item = this._createItem(arguments); var uuid = item.uuid; this.client.error(item); - return {uuid: uuid}; + return { uuid: uuid }; }; -Rollbar.error = function() { +Rollbar.error = function () { if (_instance) { return _instance.error.apply(_instance, arguments); } else { @@ -198,21 +198,21 @@ Rollbar.error = function() { handleUninitialized(maybeCallback); } }; -Rollbar.prototype._uncaughtError = function() { +Rollbar.prototype._uncaughtError = function () { var item = this._createItem(arguments); item._isUncaught = true; var uuid = item.uuid; this.client.error(item); - return {uuid: uuid}; + return { uuid: uuid }; }; -Rollbar.prototype.critical = function() { +Rollbar.prototype.critical = function () { var item = this._createItem(arguments); var uuid = item.uuid; this.client.critical(item); - return {uuid: uuid}; + return { uuid: uuid }; }; -Rollbar.critical = function() { +Rollbar.critical = function () { if (_instance) { return _instance.critical.apply(_instance, arguments); } else { @@ -221,10 +221,10 @@ Rollbar.critical = function() { } }; -Rollbar.prototype.buildJsonPayload = function(item) { +Rollbar.prototype.buildJsonPayload = function (item) { return this.client.buildJsonPayload(item); }; -Rollbar.buildJsonPayload = function() { +Rollbar.buildJsonPayload = function () { if (_instance) { return _instance.buildJsonPayload.apply(_instance, arguments); } else { @@ -232,10 +232,10 @@ Rollbar.buildJsonPayload = function() { } }; -Rollbar.prototype.sendJsonPayload = function(jsonPayload) { +Rollbar.prototype.sendJsonPayload = function (jsonPayload) { return this.client.sendJsonPayload(jsonPayload); }; -Rollbar.sendJsonPayload = function() { +Rollbar.sendJsonPayload = function () { if (_instance) { return _instance.sendJsonPayload.apply(_instance, arguments); } else { @@ -243,10 +243,10 @@ Rollbar.sendJsonPayload = function() { } }; -Rollbar.prototype.wait = function(callback) { +Rollbar.prototype.wait = function (callback) { this.client.wait(callback); }; -Rollbar.wait = function(callback) { +Rollbar.wait = function (callback) { if (_instance) { return _instance.wait(callback) } else { @@ -255,9 +255,9 @@ Rollbar.wait = function(callback) { } }; -Rollbar.prototype.errorHandler = function() { - return function(err, request, response, next) { - var cb = function(rollbarError) { +Rollbar.prototype.errorHandler = function () { + return function (err, request, response, next) { + var cb = function (rollbarError) { if (rollbarError) { logger.error('Error reporting to rollbar, ignoring: ' + rollbarError); } @@ -273,7 +273,7 @@ Rollbar.prototype.errorHandler = function() { return this.error('Error: ' + err, request, cb); }.bind(this); }; -Rollbar.errorHandler = function() { +Rollbar.errorHandler = function () { if (_instance) { return _instance.errorHandler() } else { @@ -281,16 +281,16 @@ Rollbar.errorHandler = function() { } }; -Rollbar.prototype.lambdaHandler = function(handler, timeoutHandler) { +Rollbar.prototype.lambdaHandler = function (handler, timeoutHandler) { if (handler.length <= 2) { return this.asyncLambdaHandler(handler, timeoutHandler); } return this.syncLambdaHandler(handler, timeoutHandler); }; -Rollbar.prototype.asyncLambdaHandler = function(handler, timeoutHandler) { +Rollbar.prototype.asyncLambdaHandler = function (handler, timeoutHandler) { var self = this; - var _timeoutHandler = function(event, context) { + var _timeoutHandler = function (event, context) { var message = 'Function timed out'; var custom = { originalEvent: event, @@ -299,21 +299,21 @@ Rollbar.prototype.asyncLambdaHandler = function(handler, timeoutHandler) { self.error(message, custom); }; var shouldReportTimeouts = self.options.captureLambdaTimeouts; - return function(event, context) { - return new Promise(function(resolve, reject) { + return function (event, context) { + return new Promise(function (resolve, reject) { self.lambdaContext = context; if (shouldReportTimeouts) { var timeoutCb = (timeoutHandler || _timeoutHandler).bind(null, event, context); self.lambdaTimeoutHandle = setTimeout(timeoutCb, context.getRemainingTimeInMillis() - 1000); } handler(event, context) - .then(function(resp) { + .then(function (resp) { clearTimeout(self.lambdaTimeoutHandle); resolve(resp); }) - .catch(function(err) { + .catch(function (err) { self.error(err); - self.wait(function() { + self.wait(function () { clearTimeout(self.lambdaTimeoutHandle); reject(err); }); @@ -321,9 +321,9 @@ Rollbar.prototype.asyncLambdaHandler = function(handler, timeoutHandler) { }); }; }; -Rollbar.prototype.syncLambdaHandler = function(handler, timeoutHandler) { +Rollbar.prototype.syncLambdaHandler = function (handler, timeoutHandler) { var self = this; - var _timeoutHandler = function(event, context, _cb) { + var _timeoutHandler = function (event, context, _cb) { var message = 'Function timed out'; var custom = { originalEvent: event, @@ -332,32 +332,32 @@ Rollbar.prototype.syncLambdaHandler = function(handler, timeoutHandler) { self.error(message, custom); }; var shouldReportTimeouts = self.options.captureLambdaTimeouts; - return function(event, context, callback) { + return function (event, context, callback) { self.lambdaContext = context; if (shouldReportTimeouts) { var timeoutCb = (timeoutHandler || _timeoutHandler).bind(null, event, context, callback); self.lambdaTimeoutHandle = setTimeout(timeoutCb, context.getRemainingTimeInMillis() - 1000); } try { - handler(event, context, function(err, resp) { + handler(event, context, function (err, resp) { if (err) { self.error(err); } - self.wait(function() { + self.wait(function () { clearTimeout(self.lambdaTimeoutHandle); callback(err, resp); }); }); } catch (err) { self.error(err); - self.wait(function() { + self.wait(function () { clearTimeout(self.lambdaTimeoutHandle); throw err; }); } }; }; -Rollbar.lambdaHandler = function(handler) { +Rollbar.lambdaHandler = function (handler) { if (_instance) { return _instance.lambdaHandler(handler); } else { @@ -366,7 +366,7 @@ Rollbar.lambdaHandler = function(handler) { }; function wrapCallback(r, f) { - return function() { + return function () { var err = arguments[0]; if (err) { r.error(err); @@ -375,10 +375,10 @@ function wrapCallback(r, f) { }; } -Rollbar.prototype.wrapCallback = function(f) { +Rollbar.prototype.wrapCallback = function (f) { return wrapCallback(this, f); }; -Rollbar.wrapCallback = function(f) { +Rollbar.wrapCallback = function (f) { if (_instance) { return _instance.wrapCallback(f); } else { @@ -386,11 +386,11 @@ Rollbar.wrapCallback = function(f) { } }; -Rollbar.prototype.captureEvent = function() { +Rollbar.prototype.captureEvent = function () { var event = _.createTelemetryEvent(arguments); return this.client.captureEvent(event.type, event.metadata, event.level); }; -Rollbar.captureEvent = function() { +Rollbar.captureEvent = function () { if (_instance) { return _instance.captureEvent.apply(_instance, arguments); } else { @@ -400,7 +400,7 @@ Rollbar.captureEvent = function() { /** DEPRECATED **/ -Rollbar.prototype.reportMessage = function(message, level, request, callback) { +Rollbar.prototype.reportMessage = function (message, level, request, callback) { logger.log('reportMessage is deprecated'); if (_.isFunction(this[level])) { return this[level](message, request, callback); @@ -408,7 +408,7 @@ Rollbar.prototype.reportMessage = function(message, level, request, callback) { return this.error(message, request, callback); } }; -Rollbar.reportMessage = function(message, level, request, callback) { +Rollbar.reportMessage = function (message, level, request, callback) { if (_instance) { return _instance.reportMessage(message, level, request, callback); } else { @@ -416,11 +416,11 @@ Rollbar.reportMessage = function(message, level, request, callback) { } }; -Rollbar.prototype.reportMessageWithPayloadData = function(message, payloadData, request, callback) { +Rollbar.prototype.reportMessageWithPayloadData = function (message, payloadData, request, callback) { logger.log('reportMessageWithPayloadData is deprecated'); return this.error(message, request, payloadData, callback); }; -Rollbar.reportMessageWithPayloadData = function(message, payloadData, request, callback) { +Rollbar.reportMessageWithPayloadData = function (message, payloadData, request, callback) { if (_instance) { return _instance.reportMessageWithPayloadData(message, payloadData, request, callback); } else { @@ -429,11 +429,11 @@ Rollbar.reportMessageWithPayloadData = function(message, payloadData, request, c }; -Rollbar.prototype.handleError = function(err, request, callback) { +Rollbar.prototype.handleError = function (err, request, callback) { logger.log('handleError is deprecated'); return this.error(err, request, callback); }; -Rollbar.handleError = function(err, request, callback) { +Rollbar.handleError = function (err, request, callback) { if (_instance) { return _instance.handleError(err, request, callback); } else { @@ -442,11 +442,11 @@ Rollbar.handleError = function(err, request, callback) { }; -Rollbar.prototype.handleErrorWithPayloadData = function(err, payloadData, request, callback) { +Rollbar.prototype.handleErrorWithPayloadData = function (err, payloadData, request, callback) { logger.log('handleErrorWithPayloadData is deprecated'); return this.error(err, request, payloadData, callback); }; -Rollbar.handleErrorWithPayloadData = function(err, payloadData, request, callback) { +Rollbar.handleErrorWithPayloadData = function (err, payloadData, request, callback) { if (_instance) { return _instance.handleErrorWithPayloadData(err, payloadData, request, callback); } else { @@ -454,7 +454,7 @@ Rollbar.handleErrorWithPayloadData = function(err, payloadData, request, callbac } }; -Rollbar.handleUncaughtExceptions = function(accessToken, options) { +Rollbar.handleUncaughtExceptions = function (accessToken, options) { if (_instance) { options = options || {}; options.accessToken = accessToken; @@ -464,7 +464,7 @@ Rollbar.handleUncaughtExceptions = function(accessToken, options) { } }; -Rollbar.handleUnhandledRejections = function(accessToken, options) { +Rollbar.handleUnhandledRejections = function (accessToken, options) { if (_instance) { options = options || {}; options.accessToken = accessToken; @@ -474,7 +474,7 @@ Rollbar.handleUnhandledRejections = function(accessToken, options) { } }; -Rollbar.handleUncaughtExceptionsAndRejections = function(accessToken, options) { +Rollbar.handleUncaughtExceptionsAndRejections = function (accessToken, options) { if (_instance) { options = options || {}; options.accessToken = accessToken; @@ -512,7 +512,7 @@ function addPredicatesToQueue(queue) { .addPredicate(sharedPredicates.messageIsIgnored(logger)); } -Rollbar.prototype._createItem = function(args) { +Rollbar.prototype._createItem = function (args) { var requestKeys = ['headers', 'protocol', 'url', 'method', 'body', 'route']; return _.createItem(args, logger, this, requestKeys, this.lambdaContext); }; @@ -526,7 +526,7 @@ function _getFirstFunction(args) { return undefined; } -Rollbar.prototype.setupUnhandledCapture = function() { +Rollbar.prototype.setupUnhandledCapture = function () { if (this.options.captureUncaught || this.options.handleUncaughtExceptions) { this.handleUncaughtExceptions(); } @@ -535,24 +535,24 @@ Rollbar.prototype.setupUnhandledCapture = function() { } }; -Rollbar.prototype.handleUncaughtExceptions = function() { +Rollbar.prototype.handleUncaughtExceptions = function () { var exitOnUncaught = !!this.options.exitOnUncaughtException; delete this.options.exitOnUncaughtException; - addOrReplaceRollbarHandler('uncaughtException', function(err) { + addOrReplaceRollbarHandler('uncaughtException', function (err) { if (!this.options.captureUncaught && !this.options.handleUncaughtExceptions) { return; } - this._uncaughtError(err, function(err) { + this._uncaughtError(err, function (err) { if (err) { logger.error('Encountered error while handling an uncaught exception.'); logger.error(err); } }); if (exitOnUncaught) { - setImmediate(function() { - this.wait(function() { + setImmediate(function () { + this.wait(function () { process.exit(1); }); }.bind(this)); @@ -560,13 +560,13 @@ Rollbar.prototype.handleUncaughtExceptions = function() { }.bind(this)); }; -Rollbar.prototype.handleUnhandledRejections = function() { - addOrReplaceRollbarHandler('unhandledRejection', function(reason) { +Rollbar.prototype.handleUnhandledRejections = function () { + addOrReplaceRollbarHandler('unhandledRejection', function (reason) { if (!this.options.captureUnhandledRejections && !this.options.handleUnhandledRejections) { return; } - this._uncaughtError(reason, function(err) { + this._uncaughtError(reason, function (err) { if (err) { logger.error('Encountered error while handling an uncaught exception.'); logger.error(err); @@ -578,7 +578,7 @@ Rollbar.prototype.handleUnhandledRejections = function() { function addOrReplaceRollbarHandler(event, action) { // We only support up to two arguments which is enough for how this is used // rather than dealing with `arguments` and `apply` - var fn = function(a, b) { + var fn = function (a, b) { action(a, b); }; fn._rollbarHandler = true; diff --git a/sdks/rollbar.js/src/server/transforms.js b/sdks/rollbar.js/src/server/transforms.js index 740430980..13ae85cb6 100644 --- a/sdks/rollbar.js/src/server/transforms.js +++ b/sdks/rollbar.js/src/server/transforms.js @@ -13,7 +13,8 @@ function baseData(item, options, callback) { language: 'javascript', framework: item.framework || options.framework, uuid: item.uuid, - notifier: JSON.parse(JSON.stringify(options.notifier)) + notifier: JSON.parse(JSON.stringify(options.notifier)), + custom: item.custom }; if (options.codeVersion) { diff --git a/sdks/rollbar.js/test/browser.rollbar.test.js b/sdks/rollbar.js/test/browser.rollbar.test.js index 790f3fa08..50db1bc9d 100644 --- a/sdks/rollbar.js/test/browser.rollbar.test.js +++ b/sdks/rollbar.js/test/browser.rollbar.test.js @@ -5,6 +5,31 @@ var Rollbar = require('../src/browser/rollbar'); +const DUMMY_TRACE_ID = 'some-trace-id'; +const DUMMY_SPAN_ID = 'some-span-id'; + +const ValidOpenTracingTracerStub = { + scope: () => { + return { + active: () => { + return { + setTag: () => { }, + context: () => { + return { + toTraceId: () => DUMMY_TRACE_ID, + toSpanId: () => DUMMY_SPAN_ID + } + } + } + } + } + } +}; + +const InvalidOpenTracingTracerStub = { + foo: () => { } +}; + function TestClientGen() { var TestClient = function() { this.transforms = []; @@ -35,6 +60,7 @@ function TestClientGen() { this.options = o; this.payloadData = payloadData; }; + this.tracer = ValidOpenTracingTracerStub; }; return TestClient; @@ -153,6 +179,24 @@ describe('Rollbar()', function() { done(); }); + + it('should have a tracer if valid tracer is provided', function(done) { + var options = { tracer: ValidOpenTracingTracerStub }; + var rollbar = window.rollbar = new Rollbar(options); + + expect(rollbar.client.tracer).to.eql(ValidOpenTracingTracerStub); + + done(); + }); + + it('should not have a tracer if invalid tracer is provided', function(done) { + var options = { tracer: InvalidOpenTracingTracerStub }; + var rollbar = window.rollbar = new Rollbar(options); + + expect(rollbar.client.tracer).to.eql(null); + + done(); + }); }); describe('configure', function() { diff --git a/sdks/rollbar.js/test/react-native.rollbar.test.js b/sdks/rollbar.js/test/react-native.rollbar.test.js index 6da191fea..dbdfc61d9 100644 --- a/sdks/rollbar.js/test/react-native.rollbar.test.js +++ b/sdks/rollbar.js/test/react-native.rollbar.test.js @@ -45,6 +45,30 @@ describe('sendJsonPayload', function() { }); }); +const DUMMY_TRACE_ID = 'some-trace-id'; +const DUMMY_SPAN_ID = 'some-span-id'; + +const ValidOpenTracingTracerStub = { + scope: () => { + return { + active: () => { + return { + setTag: () => { }, + context: () => { + return { + toTraceId: () => DUMMY_TRACE_ID, + toSpanId: () => DUMMY_SPAN_ID + } + } + } + } + } + } +}; + +const InvalidOpenTracingTracerStub = { + foo: () => { } +}; function TestClientGen() { var TestClient = function() { @@ -190,6 +214,24 @@ describe('Rollbar()', function() { done(); }); + + it('should have a tracer if valid tracer is provided', function (done) { + var options = { tracer: ValidOpenTracingTracerStub }; + var rollbar = new Rollbar(options); + + expect(rollbar.client.tracer).to.eql(ValidOpenTracingTracerStub); + + done(); + }); + + it('should not have a tracer if invalid tracer is provided', function (done) { + var options = { tracer: InvalidOpenTracingTracerStub }; + var rollbar = new Rollbar(options); + + expect(rollbar.client.tracer).to.eql(null); + + done(); + }); }); describe('configure', function() { diff --git a/sdks/rollbar.js/test/server.rollbar.test.js b/sdks/rollbar.js/test/server.rollbar.test.js index bc5f464a2..cbbf86efd 100644 --- a/sdks/rollbar.js/test/server.rollbar.test.js +++ b/sdks/rollbar.js/test/server.rollbar.test.js @@ -7,8 +7,33 @@ var sinon = require('sinon'); process.env.NODE_ENV = process.env.NODE_ENV || 'test-node-env'; var Rollbar = require('../src/server/rollbar'); +const DUMMY_TRACE_ID = 'some-trace-id'; +const DUMMY_SPAN_ID = 'some-span-id'; + +const ValidOpenTracingTracerStub = { + scope: () => { + return { + active: () => { + return { + setTag: () => {}, + context: () => { + return { + toTraceId: () => DUMMY_TRACE_ID, + toSpanId: () => DUMMY_SPAN_ID + } + } + } + } + } + } +}; + +const InvalidOpenTracingTracerStub = { + foo: () => {} +}; + function TestClientGen() { - var TestClient = function() { + var TestClient = function () { this.notifier = { addTransform: function() { return this; } }; @@ -16,7 +41,7 @@ function TestClientGen() { addPredicate: function() { return this; } }; this.logCalls = []; - var logs = 'log,debug,info,warn,wanring,error,critical'.split(','); + var logs = 'log,debug,info,warn,warning,error,critical'.split(','); for(var i=0, len=logs.length; i < len; i++) { var fn = logs[i].slice(0); this[fn] = function(fn, item) { @@ -32,6 +57,7 @@ function TestClientGen() { this.clearLogCalls = function() { this.logCalls = []; }; + this.tracer = ValidOpenTracingTracerStub; }; return TestClient; @@ -116,6 +142,24 @@ vows.describe('rollbar') 'should set configured options': function(r) { assert.equal('fake-env', r.options._configuredOptions.environment); } + }, + 'with valid tracer': { + topic: function () { + var rollbar = new Rollbar({ captureUncaught: true, environment: 'fake-env', tracer: ValidOpenTracingTracerStub }); + return rollbar; + }, + 'should configure tracer': function (r) { + assert.ok(typeof r.client.tracer === 'object'); + } + }, + 'with invalid tracer': { + topic: function () { + var rollbar = new Rollbar({ captureUncaught: true, environment: 'fake-env', tracer: InvalidOpenTracingTracerStub }); + return rollbar; + }, + 'should configure tracer': function (r) { + assert.equal(null, r.client.tracer); + } } }, 'configure': { @@ -129,12 +173,65 @@ vows.describe('rollbar') assert.equal('new-env', r.options._configuredOptions.environment); assert.equal(false, r.options._configuredOptions.captureUncaught); } + }, + 'with valid tracer': { + topic: function() { + var rollbar = new Rollbar({ captureUncaught: true, environment: 'fake-env' }); + rollbar.configure({ tracer: ValidOpenTracingTracerStub }); + return rollbar; + }, + 'should configure tracer': function(r) { + assert.equal(ValidOpenTracingTracerStub, r.client.tracer); + } + }, + 'with invalid tracer': { + topic: function() { + var rollbar = new Rollbar({ captureUncaught: true, environment: 'fake-env' }); + rollbar.configure({ tracer: InvalidOpenTracingTracerStub }); + return rollbar; + }, + 'should configure tracer': function(r) { + assert.equal(null, r.client.tracer); + } } }, + 'addTracingInfo': { + 'with valid tracer': { + topic: function() { + var rollbar = new Rollbar({ captureUncaught: true, environment: 'fake-env', tracer: ValidOpenTracingTracerStub }); + return rollbar; + }, + 'should add trace ID and span ID to custom field on item if no custom field set': function (r) { + const item = { uuid: 'some-uuid', custom: null }; + r.client._addTracingInfo(item); + assert.equal(DUMMY_TRACE_ID, item.custom.opentracing_trace_id); + assert.equal(DUMMY_SPAN_ID, item.custom.opentracing_span_id); + }, + 'should add trace ID and span ID to custom field on item even if already has some custom fields': function(r) { + const item = { uuid: 'some-uuid', custom: { foo: 'foo' } }; + r.client._addTracingInfo(item); + assert.equal(DUMMY_TRACE_ID, item.custom.opentracing_trace_id); + assert.equal(DUMMY_SPAN_ID, item.custom.opentracing_span_id); + assert.equal('foo', item.custom.foo); + } + }, + 'with invalid tracer': { + topic: function() { + var rollbar = new Rollbar({ captureUncaught: true, environment: 'fake-env', tracer: InvalidOpenTracingTracerStub }); + return rollbar; + }, + 'should add trace ID and span ID to custom field on item': function (r) { + const item = { uuid: 'some-uuid', custom: {} }; + r.client._addTracingInfo(item); + assert.equal(undefined, item.custom.opentracing_trace_id); + assert.equal(undefined, item.custom.opentracing_span_id); + } + }, + }, 'log': { topic: function() { var client = new (TestClientGen())(); - var rollbar = new Rollbar({accessToken: 'abc123'}, client); + var rollbar = new Rollbar({ accessToken: 'abc123' }, client); return rollbar; }, 'message': {