UNPKG

dd-trace

Version:

Datadog APM tracing client for JavaScript

121 lines (101 loc) 3.27 kB
'use strict' // TODO: move anything related to tracing to TracingPlugin instead const dc = require('dc-polyfill') const logger = require('../log') const { storage } = require('../../../datadog-core') class Subscription { constructor (event, handler) { this._channel = dc.channel(event) this._handler = (message, name) => { const store = storage('legacy').getStore() if (!store || !store.noop) { handler(message, name) } } } enable () { // TODO: Once Node.js v18.6.0 is no longer supported, we should use `dc.subscribe(event, handler)` instead this._channel.subscribe(this._handler) } disable () { // TODO: Once Node.js v18.6.0 is no longer supported, we should use `dc.unsubscribe(event, handler)` instead this._channel.unsubscribe(this._handler) } } class StoreBinding { constructor (event, transform) { this._channel = dc.channel(event) this._transform = data => { const store = storage('legacy').getStore() return !store || !store.noop || (data && Object.hasOwn(data, 'currentStore')) ? transform(data) : store } } enable () { this._channel.bindStore(storage('legacy'), this._transform) } disable () { this._channel.unbindStore(storage('legacy')) } } module.exports = class Plugin { constructor (tracer, tracerConfig) { this._subscriptions = [] this._bindings = [] this._enabled = false this._tracer = tracer this.config = {} // plugin-specific configuration, unset until .configure() is called this._tracerConfig = tracerConfig // global tracer configuration } get tracer () { return this._tracer?._tracer || this._tracer } enter (span, store) { store = store || storage('legacy').getStore() storage('legacy').enterWith({ ...store, span }) } // TODO: Implement filters on resource name for all plugins. /** Prevents creation of spans here and for all async descendants. */ skip () { storage('legacy').enterWith({ noop: true }) } addSub (channelName, handler) { const plugin = this const wrappedHandler = function () { try { return handler.apply(this, arguments) } catch (e) { logger.error('Error in plugin handler:', e) logger.info('Disabling plugin: %s', plugin.id) plugin.configure(false) } } this._subscriptions.push(new Subscription(channelName, wrappedHandler)) } addBind (channelName, transform) { this._bindings.push(new StoreBinding(channelName, transform)) } addError (error) { const store = storage('legacy').getStore() if (!store || !store.span) return if (!store.span._spanContext._tags.error) { store.span.setTag('error', error || 1) } } configure (config) { if (typeof config === 'boolean') { config = { enabled: config } } this.config = config if (config.enabled && !this._enabled) { this._enabled = true this._subscriptions.forEach(sub => sub.enable()) this._bindings.forEach(sub => sub.enable()) } else if (!config.enabled && this._enabled) { this._enabled = false this._subscriptions.forEach(sub => sub.disable()) this._bindings.forEach(sub => sub.disable()) } } }