dd-trace 4.1.1 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/LICENSE-3rdparty.csv +2 -0
  2. package/index.d.ts +281 -0
  3. package/package.json +6 -4
  4. package/packages/datadog-instrumentations/src/cookie.js +21 -0
  5. package/packages/datadog-instrumentations/src/fetch.js +48 -0
  6. package/packages/datadog-instrumentations/src/grpc/server.js +1 -1
  7. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  8. package/packages/datadog-instrumentations/src/helpers/register.js +10 -0
  9. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +18 -0
  10. package/packages/datadog-plugin-cypress/src/plugin.js +109 -47
  11. package/packages/datadog-plugin-cypress/src/support.js +3 -2
  12. package/packages/datadog-plugin-fetch/src/index.js +36 -0
  13. package/packages/datadog-plugin-http/src/client.js +24 -8
  14. package/packages/datadog-plugin-mysql/src/index.js +2 -11
  15. package/packages/datadog-plugin-tedious/src/index.js +2 -2
  16. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +3 -0
  17. package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +52 -0
  18. package/packages/dd-trace/src/appsec/iast/analyzers/insecure-cookie-analyzer.js +3 -22
  19. package/packages/dd-trace/src/appsec/iast/analyzers/no-httponly-cookie-analyzer.js +12 -0
  20. package/packages/dd-trace/src/appsec/iast/analyzers/no-samesite-cookie-analyzer.js +12 -0
  21. package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +7 -3
  22. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +3 -3
  23. package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +48 -0
  24. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +3 -3
  25. package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +24 -0
  26. package/packages/dd-trace/src/appsec/iast/index.js +9 -2
  27. package/packages/dd-trace/src/appsec/iast/path-line.js +13 -0
  28. package/packages/dd-trace/src/appsec/iast/tags.js +6 -0
  29. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +2 -1
  30. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +13 -4
  31. package/packages/dd-trace/src/appsec/iast/taint-tracking/origin-types.js +5 -1
  32. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +24 -4
  33. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +3 -1
  34. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +3 -0
  35. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +7 -1
  36. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -3
  37. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +5 -2
  38. package/packages/dd-trace/src/config.js +13 -0
  39. package/packages/dd-trace/src/external-logger/src/index.js +126 -0
  40. package/packages/dd-trace/src/external-logger/test/index.spec.js +147 -0
  41. package/packages/dd-trace/src/lambda/handler.js +3 -15
  42. package/packages/dd-trace/src/noop/proxy.js +4 -0
  43. package/packages/dd-trace/src/opentelemetry/context_manager.js +74 -0
  44. package/packages/dd-trace/src/opentelemetry/sampler.js +18 -0
  45. package/packages/dd-trace/src/opentelemetry/span.js +151 -0
  46. package/packages/dd-trace/src/opentelemetry/span_context.js +44 -0
  47. package/packages/dd-trace/src/opentelemetry/span_processor.js +50 -0
  48. package/packages/dd-trace/src/opentelemetry/tracer.js +124 -0
  49. package/packages/dd-trace/src/opentelemetry/tracer_provider.js +72 -0
  50. package/packages/dd-trace/src/opentracing/span.js +14 -4
  51. package/packages/dd-trace/src/plugin_manager.js +10 -7
  52. package/packages/dd-trace/src/plugins/database.js +7 -3
  53. package/packages/dd-trace/src/plugins/plugin.js +3 -1
  54. package/packages/dd-trace/src/plugins/util/exec.js +2 -2
  55. package/packages/dd-trace/src/plugins/util/git.js +51 -24
  56. package/packages/dd-trace/src/profiling/config.js +2 -0
  57. package/packages/dd-trace/src/profiling/profiler.js +13 -4
  58. package/packages/dd-trace/src/proxy.js +4 -0
  59. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +24 -1
  60. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +18 -1
  61. package/packages/dd-trace/src/util.js +1 -1
@@ -0,0 +1,74 @@
1
+ 'use strict'
2
+
3
+ const { AsyncLocalStorage } = require('async_hooks')
4
+ const { trace, ROOT_CONTEXT } = require('@opentelemetry/api')
5
+
6
+ const SpanContext = require('./span_context')
7
+ const tracer = require('../../')
8
+
9
+ // Horrible hack to acquire the otherwise inaccessible SPAN_KEY so we can redirect it...
10
+ // This is used for getting the current span context in OpenTelemetry, but the SPAN_KEY value is
11
+ // not exposed as it's meant to be read-only from outside the module. We want to hijack this logic
12
+ // so we can instead get the span context from the datadog context manager instead.
13
+ let SPAN_KEY
14
+ trace.getSpan({
15
+ getValue (key) {
16
+ SPAN_KEY = key
17
+ }
18
+ })
19
+
20
+ // Whenever a value is acquired from the context map we should mostly delegate to the real getter,
21
+ // but when accessing the current span we should hijack that access to instead provide a fake span
22
+ // which we can use to get an OTel span context wrapping the datadog active scope span context.
23
+ function wrappedGetValue (target) {
24
+ return (key) => {
25
+ if (key === SPAN_KEY) {
26
+ return {
27
+ spanContext () {
28
+ const activeSpan = tracer.scope().active()
29
+ const context = activeSpan && activeSpan.context()
30
+ return new SpanContext(context)
31
+ }
32
+ }
33
+ }
34
+ return target.getValue(key)
35
+ }
36
+ }
37
+
38
+ class ContextManager {
39
+ constructor () {
40
+ this._store = new AsyncLocalStorage()
41
+ }
42
+
43
+ active () {
44
+ const active = this._store.getStore() || ROOT_CONTEXT
45
+
46
+ return new Proxy(active, {
47
+ get (target, key) {
48
+ return key === 'getValue' ? wrappedGetValue(target) : target[key]
49
+ }
50
+ })
51
+ }
52
+
53
+ with (context, fn, thisArg, ...args) {
54
+ const span = trace.getSpan(context)
55
+ const ddScope = tracer.scope()
56
+ return ddScope.activate(span._ddSpan, () => {
57
+ const cb = thisArg == null ? fn : fn.bind(thisArg)
58
+ return this._store.run(context, cb, ...args)
59
+ })
60
+ }
61
+
62
+ bind (context, target) {
63
+ const self = this
64
+ return function (...args) {
65
+ return self.with(context, target, this, ...args)
66
+ }
67
+ }
68
+
69
+ // Not part of the spec but the Node.js API expects these
70
+ enable () {}
71
+ disable () {}
72
+ }
73
+
74
+ module.exports = ContextManager
@@ -0,0 +1,18 @@
1
+ 'use strict'
2
+
3
+ // This isn't used yet. We currently delegate to dd-trace core for sampling decisions.
4
+ // Leaving here for future use.
5
+ class Sampler {
6
+ shouldSample (context, traceId, spanName, spanKind, attributes, links) {
7
+ // 0 = no, 1 = record, 2 = record and sample
8
+ // TODO: Make this actually do sampling...
9
+ return { decision: 2 }
10
+ }
11
+
12
+ /** Returns the sampler name or short description with the configuration. */
13
+ toString () {
14
+ return 'DatadogSampler'
15
+ }
16
+ }
17
+
18
+ module.exports = Sampler
@@ -0,0 +1,151 @@
1
+ 'use strict'
2
+
3
+ const api = require('@opentelemetry/api')
4
+
5
+ const { performance } = require('perf_hooks')
6
+ const { timeOrigin } = performance
7
+
8
+ const { timeInputToHrTime } = require('@opentelemetry/core')
9
+
10
+ const tracer = require('../../')
11
+ const DatadogSpan = require('../opentracing/span')
12
+ const { ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK } = require('../constants')
13
+
14
+ const SpanContext = require('./span_context')
15
+
16
+ // The one built into OTel rounds so we lose sub-millisecond precision.
17
+ function hrTimeToMilliseconds (time) {
18
+ return time[0] * 1e3 + time[1] / 1e6
19
+ }
20
+
21
+ class Span {
22
+ constructor (
23
+ parentTracer,
24
+ context,
25
+ spanName,
26
+ spanContext,
27
+ kind,
28
+ links = [],
29
+ timeInput
30
+ ) {
31
+ const { _tracer } = tracer
32
+
33
+ const hrStartTime = timeInputToHrTime(timeInput || (performance.now() + timeOrigin))
34
+ const startTime = hrTimeToMilliseconds(hrStartTime)
35
+
36
+ this._ddSpan = new DatadogSpan(_tracer, _tracer._processor, _tracer._prioritySampler, {
37
+ operationName: spanName,
38
+ context: spanContext._ddContext,
39
+ startTime,
40
+ hostname: _tracer._hostname,
41
+ tags: {
42
+ 'service.name': _tracer._service
43
+ }
44
+ }, _tracer._debug)
45
+
46
+ this._parentTracer = parentTracer
47
+ this._context = context
48
+
49
+ this._hasStatus = false
50
+
51
+ // NOTE: Need to grab the value before setting it on the span because the
52
+ // math for computing opentracing timestamps is apparently lossy...
53
+ this.startTime = hrStartTime
54
+ this.kind = kind
55
+ this.links = links
56
+ this._spanProcessor.onStart(this, context)
57
+ }
58
+
59
+ get parentSpanId () {
60
+ const { _parentId } = this._ddSpan.context()
61
+ return _parentId && _parentId.toString(16)
62
+ }
63
+
64
+ // Expected by OTel
65
+ get resource () {
66
+ return this._parentTracer.resource
67
+ }
68
+ get instrumentationLibrary () {
69
+ return this._parentTracer.instrumentationLibrary
70
+ }
71
+ get _spanProcessor () {
72
+ return this._parentTracer.getActiveSpanProcessor()
73
+ }
74
+
75
+ get name () {
76
+ return this._ddSpan.context()._name
77
+ }
78
+
79
+ spanContext () {
80
+ return new SpanContext(this._ddSpan.context())
81
+ }
82
+
83
+ setAttribute (key, value) {
84
+ this._ddSpan.setTag(key, value)
85
+ return this
86
+ }
87
+
88
+ setAttributes (attributes) {
89
+ this._ddSpan.addTags(attributes)
90
+ return this
91
+ }
92
+
93
+ addEvent (name, attributesOrStartTime, startTime) {
94
+ api.diag.warn('Events not supported')
95
+ return this
96
+ }
97
+
98
+ setStatus ({ code, message }) {
99
+ if (!this.ended && !this._hasStatus && code) {
100
+ this._hasStatus = true
101
+ if (code === 2) {
102
+ this._ddSpan.addTags({
103
+ [ERROR_MESSAGE]: message
104
+ })
105
+ }
106
+ }
107
+ return this
108
+ }
109
+
110
+ updateName (name) {
111
+ if (!this.ended) {
112
+ this._ddSpan.setOperationName(name)
113
+ }
114
+ return this
115
+ }
116
+
117
+ end (timeInput) {
118
+ if (this.ended) {
119
+ api.diag.error('You can only call end() on a span once.')
120
+ return
121
+ }
122
+
123
+ const hrEndTime = timeInputToHrTime(timeInput || (performance.now() + timeOrigin))
124
+ const endTime = hrTimeToMilliseconds(hrEndTime)
125
+
126
+ this._ddSpan.finish(endTime)
127
+ this._spanProcessor.onEnd(this)
128
+ }
129
+
130
+ isRecording () {
131
+ return this.ended === false
132
+ }
133
+
134
+ recordException (exception) {
135
+ this._ddSpan.addTags({
136
+ [ERROR_TYPE]: exception.name,
137
+ [ERROR_MESSAGE]: exception.message,
138
+ [ERROR_STACK]: exception.stack
139
+ })
140
+ }
141
+
142
+ get duration () {
143
+ return this._ddSpan._duration
144
+ }
145
+
146
+ get ended () {
147
+ return typeof this.duration !== 'undefined'
148
+ }
149
+ }
150
+
151
+ module.exports = Span
@@ -0,0 +1,44 @@
1
+ 'use strict'
2
+
3
+ const api = require('@opentelemetry/api')
4
+ const { AUTO_KEEP } = require('../../../../ext/priority')
5
+ const DatadogSpanContext = require('../opentracing/span_context')
6
+ const id = require('../id')
7
+
8
+ function newContext () {
9
+ const spanId = id()
10
+ return new DatadogSpanContext({
11
+ traceId: spanId,
12
+ spanId
13
+ })
14
+ }
15
+
16
+ class SpanContext {
17
+ constructor (context) {
18
+ if (!(context instanceof DatadogSpanContext)) {
19
+ context = context
20
+ ? new DatadogSpanContext(context)
21
+ : newContext()
22
+ }
23
+ this._ddContext = context
24
+ }
25
+
26
+ get traceId () {
27
+ return this._ddContext._traceId.toString(16)
28
+ }
29
+
30
+ get spanId () {
31
+ return this._ddContext._spanId.toString(16)
32
+ }
33
+
34
+ get traceFlags () {
35
+ return this._ddContext._sampling.priority >= AUTO_KEEP ? 1 : 0
36
+ }
37
+
38
+ get traceState () {
39
+ const ts = this._ddContext._tracestate
40
+ return api.createTraceState(ts ? ts.toString() : '')
41
+ }
42
+ }
43
+
44
+ module.exports = SpanContext
@@ -0,0 +1,50 @@
1
+ 'use strict'
2
+
3
+ class NoopSpanProcessor {
4
+ forceFlush () {
5
+ return Promise.resolve()
6
+ }
7
+
8
+ onStart (span, context) { }
9
+ onEnd (span) { }
10
+
11
+ shutdown () {
12
+ return Promise.resolve()
13
+ }
14
+ }
15
+
16
+ class MultiSpanProcessor extends NoopSpanProcessor {
17
+ constructor (spanProcessors) {
18
+ super()
19
+ this._processors = spanProcessors
20
+ }
21
+
22
+ forceFlush () {
23
+ return Promise.all(
24
+ this._processors.map(p => p.forceFlush())
25
+ )
26
+ }
27
+
28
+ onStart (span, context) {
29
+ for (const processor of this._processors) {
30
+ processor.onStart(span, context)
31
+ }
32
+ }
33
+
34
+ onEnd (span) {
35
+ for (const processor of this._processors) {
36
+ processor.onEnd(span)
37
+ }
38
+ }
39
+
40
+ shutdown () {
41
+ return Promise.all(
42
+ this._processors.map(p => p.shutdown())
43
+ )
44
+ }
45
+ }
46
+
47
+ module.exports = {
48
+ MultiSpanProcessor,
49
+ NoopSpanProcessor
50
+ }
@@ -0,0 +1,124 @@
1
+ 'use strict'
2
+
3
+ const api = require('@opentelemetry/api')
4
+ const { sanitizeAttributes } = require('@opentelemetry/core')
5
+
6
+ const Sampler = require('./sampler')
7
+ const Span = require('./span')
8
+ const id = require('../id')
9
+ const SpanContext = require('./span_context')
10
+
11
+ class Tracer {
12
+ constructor (library, config, tracerProvider) {
13
+ this._sampler = new Sampler()
14
+ this._config = config
15
+ this._tracerProvider = tracerProvider
16
+ // Is there a reason this is public?
17
+ this.instrumentationLibrary = library
18
+ }
19
+
20
+ get resource () {
21
+ return this._tracerProvider.resource
22
+ }
23
+
24
+ startSpan (name, options = {}, context = api.context.active()) {
25
+ // remove span from context in case a root span is requested via options
26
+ if (options.root) {
27
+ context = api.trace.deleteSpan(context)
28
+ }
29
+ const parentSpan = api.trace.getSpan(context)
30
+ const parentSpanContext = parentSpan && parentSpan.spanContext()
31
+
32
+ let spanContext
33
+ // TODO: Need a way to get 128-bit trace IDs for the validity check API to work...
34
+ // if (parent && api.trace.isSpanContextValid(parent)) {
35
+ if (parentSpanContext && parentSpanContext.traceId) {
36
+ const parent = parentSpanContext._ddContext
37
+ spanContext = new SpanContext({
38
+ traceId: parent._traceId,
39
+ spanId: id(),
40
+ parentId: parent._spanId,
41
+ sampling: parent._sampling,
42
+ baggageItems: Object.assign({}, parent._baggageItems),
43
+ trace: parent._trace,
44
+ tracestate: parent._tracestate
45
+ })
46
+ } else {
47
+ spanContext = new SpanContext()
48
+ }
49
+
50
+ const spanKind = options.kind || api.SpanKind.INTERNAL
51
+ const links = (options.links || []).map(link => {
52
+ return {
53
+ context: link.context,
54
+ attributes: sanitizeAttributes(link.attributes)
55
+ }
56
+ })
57
+ const attributes = sanitizeAttributes(options.attributes)
58
+
59
+ // TODO: sampling API is not yet supported
60
+ // // make sampling decision
61
+ // const samplingResult = this._sampler.shouldSample(
62
+ // context,
63
+ // spanContext.traceId,
64
+ // name,
65
+ // spanKind,
66
+ // attributes,
67
+ // links
68
+ // )
69
+
70
+ // // Should use new span context
71
+ // spanContext._ddContext._sampling.priority =
72
+ // samplingResult.decision === api.SamplingDecision.RECORD_AND_SAMPLED
73
+ // ? AUTO_KEEP
74
+ // : AUTO_REJECT
75
+
76
+ // if (samplingResult.decision === api.SamplingDecision.NOT_RECORD) {
77
+ // api.diag.debug('Recording is off, propagating context in a non-recording span')
78
+ // return api.trace.wrapSpanContext(spanContext)
79
+ // }
80
+
81
+ const span = new Span(
82
+ this,
83
+ context,
84
+ name,
85
+ spanContext,
86
+ spanKind,
87
+ links,
88
+ options.startTime
89
+ )
90
+ // Set initial span attributes. The attributes object may have been mutated
91
+ // by the sampler, so we sanitize the merged attributes before setting them.
92
+ const initAttributes = sanitizeAttributes(
93
+ // Object.assign(attributes, samplingResult.attributes)
94
+ attributes
95
+ )
96
+ span.setAttributes(initAttributes)
97
+ return span
98
+ }
99
+
100
+ startActiveSpan (name, options, context, fn) {
101
+ if (arguments.length === 2) {
102
+ fn = options
103
+ context = undefined
104
+ options = undefined
105
+ } else if (arguments.length === 3) {
106
+ fn = context
107
+ context = undefined
108
+ } else if (arguments.length !== 4) {
109
+ return
110
+ }
111
+
112
+ const parentContext = context || api.context.active()
113
+ const span = this.startSpan(name, options, parentContext)
114
+ const contextWithSpanSet = api.trace.setSpan(parentContext, span)
115
+
116
+ return api.context.with(contextWithSpanSet, fn, undefined, span)
117
+ }
118
+
119
+ getActiveSpanProcessor () {
120
+ return this._tracerProvider.getActiveSpanProcessor()
121
+ }
122
+ }
123
+
124
+ module.exports = Tracer
@@ -0,0 +1,72 @@
1
+ 'use strict'
2
+
3
+ const { trace, context } = require('@opentelemetry/api')
4
+
5
+ const tracer = require('../../')
6
+
7
+ const ContextManager = require('./context_manager')
8
+ const { MultiSpanProcessor, NoopSpanProcessor } = require('./span_processor')
9
+ const Tracer = require('./tracer')
10
+
11
+ class TracerProvider {
12
+ constructor (config = {}) {
13
+ this.config = config
14
+ this.resource = config.resource
15
+
16
+ this._processors = []
17
+ this._tracers = new Map()
18
+ this._activeProcessor = new NoopSpanProcessor()
19
+ this._contextManager = new ContextManager()
20
+ }
21
+
22
+ getTracer (name = 'opentelemetry', version = '0.0.0', options) {
23
+ const key = `${name}@${version}`
24
+ if (!this._tracers.has(key)) {
25
+ this._tracers.set(key, new Tracer(
26
+ { ...options, name, version },
27
+ this.config,
28
+ this
29
+ ))
30
+ }
31
+ return this._tracers.get(key)
32
+ }
33
+
34
+ addSpanProcessor (spanProcessor) {
35
+ if (!this._processors.length) {
36
+ this._activeProcessor.shutdown()
37
+ }
38
+ this._processors.push(spanProcessor)
39
+ this._activeProcessor = new MultiSpanProcessor(
40
+ this._processors
41
+ )
42
+ }
43
+
44
+ getActiveSpanProcessor () {
45
+ return this._activeProcessor
46
+ }
47
+
48
+ // Not actually required by the SDK spec, but the official Node.js SDK does
49
+ // this and the docs reflect that so we should do this too for familiarity.
50
+ register (config = {}) {
51
+ context.setGlobalContextManager(this._contextManager)
52
+ if (!trace.setGlobalTracerProvider(this)) {
53
+ trace.getTracerProvider().setDelegate(this)
54
+ }
55
+ }
56
+
57
+ forceFlush () {
58
+ const exporter = tracer._tracer._exporter
59
+ if (!exporter) {
60
+ return Promise.reject(new Error('Not started'))
61
+ }
62
+
63
+ exporter._writer.flush()
64
+ return this._activeProcessor.forceFlush()
65
+ }
66
+
67
+ shutdown () {
68
+ return this._activeProcessor.shutdown()
69
+ }
70
+ }
71
+
72
+ module.exports = TracerProvider
@@ -44,6 +44,8 @@ class DatadogSpan {
44
44
  this._spanContext._tags = tags
45
45
  this._spanContext._hostname = hostname
46
46
 
47
+ this._spanContext._trace.started.push(this)
48
+
47
49
  this._startTime = fields.startTime || this._getTime()
48
50
 
49
51
  if (DD_TRACE_EXPERIMENTAL_SPAN_COUNTS && finishedRegistry) {
@@ -147,8 +149,14 @@ class DatadogSpan {
147
149
 
148
150
  _createContext (parent, fields) {
149
151
  let spanContext
152
+ let startTime
150
153
 
151
- if (parent) {
154
+ if (fields.context) {
155
+ spanContext = fields.context
156
+ if (!spanContext._trace.startTime) {
157
+ startTime = dateNow()
158
+ }
159
+ } else if (parent) {
152
160
  spanContext = new SpanContext({
153
161
  traceId: parent._traceId,
154
162
  spanId: id(),
@@ -160,11 +168,11 @@ class DatadogSpan {
160
168
  })
161
169
 
162
170
  if (!spanContext._trace.startTime) {
163
- spanContext._trace.startTime = dateNow()
171
+ startTime = dateNow()
164
172
  }
165
173
  } else {
166
174
  const spanId = id()
167
- const startTime = dateNow()
175
+ startTime = dateNow()
168
176
  spanContext = new SpanContext({
169
177
  traceId: spanId,
170
178
  spanId
@@ -178,8 +186,10 @@ class DatadogSpan {
178
186
  }
179
187
  }
180
188
 
181
- spanContext._trace.started.push(this)
182
189
  spanContext._trace.ticks = spanContext._trace.ticks || now()
190
+ if (startTime) {
191
+ spanContext._trace.startTime = startTime
192
+ }
183
193
 
184
194
  return spanContext
185
195
  }
@@ -26,8 +26,13 @@ const disabledPlugins = new Set(
26
26
  const pluginClasses = {}
27
27
 
28
28
  loadChannel.subscribe(({ name }) => {
29
- const Plugin = plugins[name]
29
+ maybeEnable(plugins[name])
30
+ })
31
+
32
+ // Globals
33
+ maybeEnable(require('../../datadog-plugin-fetch/src'))
30
34
 
35
+ function maybeEnable (Plugin) {
31
36
  if (!Plugin || typeof Plugin !== 'function') return
32
37
  if (!pluginClasses[Plugin.id]) {
33
38
  const envName = `DD_TRACE_${Plugin.id.toUpperCase()}_ENABLED`
@@ -42,7 +47,7 @@ loadChannel.subscribe(({ name }) => {
42
47
  pluginClasses[Plugin.id] = Plugin
43
48
  }
44
49
  }
45
- })
50
+ }
46
51
 
47
52
  // TODO this must always be a singleton.
48
53
  module.exports = class PluginManager {
@@ -68,7 +73,7 @@ module.exports = class PluginManager {
68
73
 
69
74
  if (!Plugin) return
70
75
  if (!this._pluginsByName[name]) {
71
- this._pluginsByName[name] = new Plugin(this._tracer)
76
+ this._pluginsByName[name] = new Plugin(this._tracer, this._tracerConfig)
72
77
  }
73
78
  if (!this._tracerConfig) return // TODO: don't wait for tracer to be initialized
74
79
 
@@ -76,6 +81,7 @@ module.exports = class PluginManager {
76
81
  enabled: this._tracerConfig.plugins !== false
77
82
  }
78
83
 
84
+ // extracts predetermined configuration from tracer and combines it with plugin-specific config
79
85
  this._pluginsByName[name].configure({
80
86
  ...this._getSharedConfig(name),
81
87
  ...pluginConfig
@@ -127,8 +133,7 @@ module.exports = class PluginManager {
127
133
  serviceMapping,
128
134
  queryStringObfuscation,
129
135
  site,
130
- url,
131
- dbmPropagationMode
136
+ url
132
137
  } = this._tracerConfig
133
138
 
134
139
  const sharedConfig = {}
@@ -141,8 +146,6 @@ module.exports = class PluginManager {
141
146
  sharedConfig.queryStringObfuscation = queryStringObfuscation
142
147
  }
143
148
 
144
- sharedConfig.dbmPropagationMode = dbmPropagationMode
145
-
146
149
  if (serviceMapping && serviceMapping[name]) {
147
150
  sharedConfig.service = serviceMapping[name]
148
151
  }
@@ -38,13 +38,17 @@ class DatabasePlugin extends StoragePlugin {
38
38
  }
39
39
 
40
40
  injectDbmQuery (query, serviceName, isPreparedStatement = false) {
41
- if (this.config.dbmPropagationMode === 'disabled') {
41
+ const mode = this.config.dbmPropagationMode || this._tracerConfig.dbmPropagationMode
42
+
43
+ if (mode === 'disabled') {
42
44
  return query
43
45
  }
46
+
44
47
  const servicePropagation = this.createDBMPropagationCommentService(serviceName)
45
- if (isPreparedStatement || this.config.dbmPropagationMode === 'service') {
48
+
49
+ if (isPreparedStatement || mode === 'service') {
46
50
  return `/*${servicePropagation}*/ ${query}`
47
- } else if (this.config.dbmPropagationMode === 'full') {
51
+ } else if (mode === 'full') {
48
52
  this.activeSpan.setTag('_dd.dbm_trace_injected', 'true')
49
53
  const traceparent = this.activeSpan._spanContext.toTraceparent()
50
54
  return `/*${servicePropagation},traceparent='${traceparent}'*/ ${query}`
@@ -26,10 +26,12 @@ class Subscription {
26
26
  }
27
27
 
28
28
  module.exports = class Plugin {
29
- constructor (tracer) {
29
+ constructor (tracer, tracerConfig) {
30
30
  this._subscriptions = []
31
31
  this._enabled = false
32
32
  this._tracer = tracer
33
+ this.config = {} // plugin-specific configuration, unset until .configure() is called
34
+ this._tracerConfig = tracerConfig // global tracer configuration
33
35
  }
34
36
 
35
37
  get tracer () {
@@ -1,8 +1,8 @@
1
1
  const cp = require('child_process')
2
2
 
3
- const sanitizedExec = (cmd, options = {}) => {
3
+ const sanitizedExec = (cmd, flags, options = { stdio: 'pipe' }) => {
4
4
  try {
5
- return cp.execSync(cmd, options).toString().replace(/(\r\n|\n|\r)/gm, '')
5
+ return cp.execFileSync(cmd, flags, options).toString().replace(/(\r\n|\n|\r)/gm, '')
6
6
  } catch (e) {
7
7
  return ''
8
8
  }