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.
- package/LICENSE-3rdparty.csv +2 -0
- package/index.d.ts +281 -0
- package/package.json +6 -4
- package/packages/datadog-instrumentations/src/cookie.js +21 -0
- package/packages/datadog-instrumentations/src/fetch.js +48 -0
- package/packages/datadog-instrumentations/src/grpc/server.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +10 -0
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +18 -0
- package/packages/datadog-plugin-cypress/src/plugin.js +109 -47
- package/packages/datadog-plugin-cypress/src/support.js +3 -2
- package/packages/datadog-plugin-fetch/src/index.js +36 -0
- package/packages/datadog-plugin-http/src/client.js +24 -8
- package/packages/datadog-plugin-mysql/src/index.js +2 -11
- package/packages/datadog-plugin-tedious/src/index.js +2 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +3 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +52 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/insecure-cookie-analyzer.js +3 -22
- package/packages/dd-trace/src/appsec/iast/analyzers/no-httponly-cookie-analyzer.js +12 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/no-samesite-cookie-analyzer.js +12 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +7 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +3 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +48 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +3 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +24 -0
- package/packages/dd-trace/src/appsec/iast/index.js +9 -2
- package/packages/dd-trace/src/appsec/iast/path-line.js +13 -0
- package/packages/dd-trace/src/appsec/iast/tags.js +6 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +2 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +13 -4
- package/packages/dd-trace/src/appsec/iast/taint-tracking/origin-types.js +5 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +24 -4
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +3 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +3 -0
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +7 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -3
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +5 -2
- package/packages/dd-trace/src/config.js +13 -0
- package/packages/dd-trace/src/external-logger/src/index.js +126 -0
- package/packages/dd-trace/src/external-logger/test/index.spec.js +147 -0
- package/packages/dd-trace/src/lambda/handler.js +3 -15
- package/packages/dd-trace/src/noop/proxy.js +4 -0
- package/packages/dd-trace/src/opentelemetry/context_manager.js +74 -0
- package/packages/dd-trace/src/opentelemetry/sampler.js +18 -0
- package/packages/dd-trace/src/opentelemetry/span.js +151 -0
- package/packages/dd-trace/src/opentelemetry/span_context.js +44 -0
- package/packages/dd-trace/src/opentelemetry/span_processor.js +50 -0
- package/packages/dd-trace/src/opentelemetry/tracer.js +124 -0
- package/packages/dd-trace/src/opentelemetry/tracer_provider.js +72 -0
- package/packages/dd-trace/src/opentracing/span.js +14 -4
- package/packages/dd-trace/src/plugin_manager.js +10 -7
- package/packages/dd-trace/src/plugins/database.js +7 -3
- package/packages/dd-trace/src/plugins/plugin.js +3 -1
- package/packages/dd-trace/src/plugins/util/exec.js +2 -2
- package/packages/dd-trace/src/plugins/util/git.js +51 -24
- package/packages/dd-trace/src/profiling/config.js +2 -0
- package/packages/dd-trace/src/profiling/profiler.js +13 -4
- package/packages/dd-trace/src/proxy.js +4 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +24 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +18 -1
- 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 (
|
|
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
|
-
|
|
171
|
+
startTime = dateNow()
|
|
164
172
|
}
|
|
165
173
|
} else {
|
|
166
174
|
const spanId = id()
|
|
167
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
48
|
+
|
|
49
|
+
if (isPreparedStatement || mode === 'service') {
|
|
46
50
|
return `/*${servicePropagation}*/ ${query}`
|
|
47
|
-
} else if (
|
|
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.
|
|
5
|
+
return cp.execFileSync(cmd, flags, options).toString().replace(/(\r\n|\n|\r)/gm, '')
|
|
6
6
|
} catch (e) {
|
|
7
7
|
return ''
|
|
8
8
|
}
|