UNPKG

signalfx-tracing

Version:

Provides auto-instrumentation for JavaScript libraries and frameworks

175 lines (128 loc) 4.07 kB
'use strict' let id = 0 class Scope { active () { return this._active() || null } activate (span, callback) { if (typeof callback !== 'function') return callback return this._activate(span, callback) } bind (target, span) { if (target === undefined || target === null) return target if (this._isEmitter(target)) { target = this._bindEmitter(target, span) } if (this._isPromise(target)) { target = this._bindPromise(target, span) } if (typeof target === 'function') { target = this._bindFn(target, span) } return target } _active () { return null } _activate (span, callback) { return typeof callback === 'function' && callback() } _bindFn (fn, span) { const scope = this const spanOrActive = this._spanOrActive(span) return function () { return scope.activate(spanOrActive, () => { return fn.apply(this, arguments) }) } } _bindEmitter (emitter, span) { if (emitter._datadog_events) return emitter emitter._datadog_events = {} if (emitter.addListener) { emitter.addListener = wrapAddListener(emitter.addListener, this, span) } if (emitter.prependListener) { emitter.prependListener = wrapAddListener(emitter.prependListener, this, span) } if (emitter.on) { emitter.on = wrapAddListener(emitter.on, this, span) } if (emitter.removeListener) { emitter.removeListener = wrapRemoveListener(emitter.removeListener) } if (emitter.off) { emitter.off = wrapRemoveListener(emitter.off) } if (emitter.removeAllListeners) { emitter.removeAllListeners = wrapRemoveAllListeners(emitter.removeAllListeners) } return emitter } _bindPromise (promise, span) { const scope = this const then = promise.then promise.then = function thenWithTrace (onFulfilled, onRejected) { const args = new Array(arguments.length) for (let i = 0, l = args.length; i < l; i++) { args[i] = scope.bind(arguments[i], span) } return then.apply(this, args) } return promise } _spanOrActive (span) { return span !== undefined ? span : this.active() } _isEmitter (emitter) { return emitter && typeof emitter.emit === 'function' && typeof emitter.on === 'function' && typeof emitter.addListener === 'function' && typeof emitter.removeListener === 'function' } _isPromise (promise) { return promise && typeof promise.then === 'function' } } function wrapAddListener (addListener, scope, span) { return function addListenerWithTrace (eventName, listener) { if (!listener || listener._datadog_bound) return addListener.apply(this, arguments) const bound = scope.bind(listener, scope._spanOrActive(span)) bound._datadog_bound = true listener._datadog_id = listener._datadog_id || ++id if (!this._datadog_events[eventName]) { this._datadog_events[eventName] = {} } const events = this._datadog_events[eventName] if (!events[listener._datadog_id]) { events[listener._datadog_id] = [] } events[listener._datadog_id].push(bound) return addListener.call(this, eventName, bound) } } function wrapRemoveListener (removeListener) { return function removeListenerWithTrace (eventName, listener) { const listeners = this._datadog_events[eventName] if (!listener || !listeners || !listeners[listener._datadog_id]) { return removeListener.apply(this, arguments) } let bound while ((bound = listeners[listener._datadog_id].pop())) { removeListener.call(this, eventName, bound) } return removeListener.call(this, eventName, listener) } } function wrapRemoveAllListeners (removeAllListeners) { return function removeAllListenersWithTrace (eventName) { if (eventName) { this._datadog_events[eventName] = {} } else { this._datadog_events = {} } return removeAllListeners.call(this, eventName) } } module.exports = Scope