dd-trace 4.18.0 → 4.19.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 -2
- package/README.md +3 -3
- package/ext/kinds.d.ts +1 -0
- package/ext/kinds.js +2 -1
- package/ext/tags.d.ts +2 -1
- package/ext/tags.js +6 -1
- package/package.json +6 -6
- package/packages/datadog-core/src/storage/async_resource.js +1 -1
- package/packages/datadog-esbuild/index.js +1 -20
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +1 -2
- package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
- package/packages/datadog-instrumentations/src/restify.js +14 -1
- package/packages/datadog-plugin-kafkajs/src/consumer.js +8 -6
- package/packages/datadog-plugin-kafkajs/src/producer.js +9 -6
- package/packages/dd-trace/src/appsec/channels.js +1 -1
- package/packages/dd-trace/src/appsec/iast/iast-log.js +1 -1
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +1 -1
- package/packages/dd-trace/src/appsec/iast/index.js +1 -1
- package/packages/dd-trace/src/appsec/iast/path-line.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +1 -1
- package/packages/dd-trace/src/appsec/recommended.json +272 -48
- package/packages/dd-trace/src/appsec/reporter.js +31 -34
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +17 -7
- package/packages/dd-trace/src/config.js +6 -4
- package/packages/dd-trace/src/datastreams/processor.js +60 -15
- package/packages/dd-trace/src/format.js +6 -1
- package/packages/dd-trace/src/iitm.js +1 -1
- package/packages/dd-trace/src/log/channels.js +1 -1
- package/packages/dd-trace/src/opentelemetry/span.js +95 -2
- package/packages/dd-trace/src/opentelemetry/tracer.js +9 -10
- package/packages/dd-trace/src/opentracing/span.js +4 -0
- package/packages/dd-trace/src/opentracing/span_context.js +5 -2
- package/packages/dd-trace/src/plugin_manager.js +1 -1
- package/packages/dd-trace/src/plugins/database.js +1 -1
- package/packages/dd-trace/src/plugins/plugin.js +1 -1
- package/packages/dd-trace/src/plugins/util/ci.js +6 -19
- package/packages/dd-trace/src/plugins/util/git.js +2 -1
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +7 -6
- package/packages/dd-trace/src/plugins/util/url.js +26 -0
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +1 -14
- package/packages/dd-trace/src/profiling/config.js +19 -2
- package/packages/dd-trace/src/profiling/profilers/events.js +161 -0
- package/packages/dd-trace/src/profiling/profilers/shared.js +9 -0
- package/packages/dd-trace/src/profiling/profilers/wall.js +61 -29
- package/packages/dd-trace/src/ritm.js +1 -1
- package/packages/dd-trace/src/span_processor.js +4 -0
- package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
- package/packages/dd-trace/src/telemetry/index.js +1 -1
- package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
- package/packages/dd-trace/src/tracer.js +4 -2
- package/packages/diagnostics_channel/index.js +0 -3
- package/packages/diagnostics_channel/src/index.js +0 -121
|
@@ -4,12 +4,16 @@ const pkg = require('../../../../package.json')
|
|
|
4
4
|
const Uint64 = require('int64-buffer').Uint64BE
|
|
5
5
|
|
|
6
6
|
const { LogCollapsingLowestDenseDDSketch } = require('@datadog/sketches-js')
|
|
7
|
-
|
|
7
|
+
const { encodePathwayContext } = require('./pathway')
|
|
8
8
|
const { DataStreamsWriter } = require('./writer')
|
|
9
9
|
const { computePathwayHash } = require('./pathway')
|
|
10
|
+
const { types } = require('util')
|
|
11
|
+
const { PATHWAY_HASH } = require('../../../../ext/tags')
|
|
12
|
+
|
|
10
13
|
const ENTRY_PARENT_HASH = Buffer.from('0000000000000000', 'hex')
|
|
11
14
|
|
|
12
15
|
const HIGH_ACCURACY_DISTRIBUTION = 0.0075
|
|
16
|
+
const CONTEXT_PROPAGATION_KEY = 'dd-pathway-ctx'
|
|
13
17
|
|
|
14
18
|
class StatsPoint {
|
|
15
19
|
constructor (hash, parentHash, edgeTags) {
|
|
@@ -18,6 +22,7 @@ class StatsPoint {
|
|
|
18
22
|
this.edgeTags = edgeTags
|
|
19
23
|
this.edgeLatency = new LogCollapsingLowestDenseDDSketch(HIGH_ACCURACY_DISTRIBUTION)
|
|
20
24
|
this.pathwayLatency = new LogCollapsingLowestDenseDDSketch(HIGH_ACCURACY_DISTRIBUTION)
|
|
25
|
+
this.payloadSize = new LogCollapsingLowestDenseDDSketch(HIGH_ACCURACY_DISTRIBUTION)
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
addLatencies (checkpoint) {
|
|
@@ -25,6 +30,7 @@ class StatsPoint {
|
|
|
25
30
|
const pathwayLatencySec = checkpoint.pathwayLatencyNs / 1e9
|
|
26
31
|
this.edgeLatency.accept(edgeLatencySec)
|
|
27
32
|
this.pathwayLatency.accept(pathwayLatencySec)
|
|
33
|
+
this.payloadSize.accept(checkpoint.payloadSize)
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
encode () {
|
|
@@ -33,7 +39,8 @@ class StatsPoint {
|
|
|
33
39
|
ParentHash: this.parentHash,
|
|
34
40
|
EdgeTags: this.edgeTags,
|
|
35
41
|
EdgeLatency: this.edgeLatency.toProto(),
|
|
36
|
-
PathwayLatency: this.pathwayLatency.toProto()
|
|
42
|
+
PathwayLatency: this.pathwayLatency.toProto(),
|
|
43
|
+
PayloadSize: this.payloadSize.toProto()
|
|
37
44
|
}
|
|
38
45
|
}
|
|
39
46
|
}
|
|
@@ -49,6 +56,29 @@ class StatsBucket extends Map {
|
|
|
49
56
|
}
|
|
50
57
|
}
|
|
51
58
|
|
|
59
|
+
function getSizeOrZero (obj) {
|
|
60
|
+
if (typeof obj === 'string') {
|
|
61
|
+
return Buffer.from(obj, 'utf-8').length
|
|
62
|
+
}
|
|
63
|
+
if (types.isArrayBuffer(obj)) {
|
|
64
|
+
return obj.byteLength
|
|
65
|
+
}
|
|
66
|
+
if (Buffer.isBuffer(obj)) {
|
|
67
|
+
return obj.length
|
|
68
|
+
}
|
|
69
|
+
return 0
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function getHeadersSize (headers) {
|
|
73
|
+
if (headers === undefined) return 0
|
|
74
|
+
return Object.entries(headers).reduce((prev, [key, val]) => getSizeOrZero(key) + getSizeOrZero(val) + prev, 0)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getMessageSize (message) {
|
|
78
|
+
const { key, value, headers } = message
|
|
79
|
+
return getSizeOrZero(key) + getSizeOrZero(value) + getHeadersSize(headers)
|
|
80
|
+
}
|
|
81
|
+
|
|
52
82
|
class TimeBuckets extends Map {
|
|
53
83
|
forTime (time) {
|
|
54
84
|
if (!this.has(time)) {
|
|
@@ -105,15 +135,19 @@ class DataStreamsProcessor {
|
|
|
105
135
|
this.writer.flush(payload)
|
|
106
136
|
}
|
|
107
137
|
|
|
108
|
-
recordCheckpoint (checkpoint) {
|
|
138
|
+
recordCheckpoint (checkpoint, span = null) {
|
|
109
139
|
if (!this.enabled) return
|
|
110
140
|
const bucketTime = Math.round(checkpoint.currentTimestamp - (checkpoint.currentTimestamp % this.bucketSizeNs))
|
|
111
141
|
this.buckets.forTime(bucketTime)
|
|
112
142
|
.forCheckpoint(checkpoint)
|
|
113
143
|
.addLatencies(checkpoint)
|
|
144
|
+
// set DSM pathway hash on span to enable related traces feature on DSM tab, convert from buffer to uint64
|
|
145
|
+
if (span) {
|
|
146
|
+
span.setTag(PATHWAY_HASH, checkpoint.hash.readBigUInt64BE(0).toString())
|
|
147
|
+
}
|
|
114
148
|
}
|
|
115
149
|
|
|
116
|
-
setCheckpoint (edgeTags, ctx = null) {
|
|
150
|
+
setCheckpoint (edgeTags, span, ctx = null, payloadSize = 0) {
|
|
117
151
|
if (!this.enabled) return null
|
|
118
152
|
const nowNs = Date.now() * 1e6
|
|
119
153
|
const direction = edgeTags.find(t => t.startsWith('direction:'))
|
|
@@ -147,16 +181,7 @@ class DataStreamsProcessor {
|
|
|
147
181
|
const hash = computePathwayHash(this.service, this.env, edgeTags, parentHash)
|
|
148
182
|
const edgeLatencyNs = nowNs - edgeStartNs
|
|
149
183
|
const pathwayLatencyNs = nowNs - pathwayStartNs
|
|
150
|
-
const
|
|
151
|
-
currentTimestamp: nowNs,
|
|
152
|
-
parentHash: parentHash,
|
|
153
|
-
hash: hash,
|
|
154
|
-
edgeTags: edgeTags,
|
|
155
|
-
edgeLatencyNs: edgeLatencyNs,
|
|
156
|
-
pathwayLatencyNs: pathwayLatencyNs
|
|
157
|
-
}
|
|
158
|
-
this.recordCheckpoint(checkpoint)
|
|
159
|
-
return {
|
|
184
|
+
const dataStreamsContext = {
|
|
160
185
|
hash: hash,
|
|
161
186
|
edgeStartNs: edgeStartNs,
|
|
162
187
|
pathwayStartNs: pathwayStartNs,
|
|
@@ -164,6 +189,22 @@ class DataStreamsProcessor {
|
|
|
164
189
|
closestOppositeDirectionHash: closestOppositeDirectionHash,
|
|
165
190
|
closestOppositeDirectionEdgeStart: closestOppositeDirectionEdgeStart
|
|
166
191
|
}
|
|
192
|
+
if (direction === 'direction:out') {
|
|
193
|
+
// Add the header for this now, as the callee doesn't have access to context when producing
|
|
194
|
+
payloadSize += getSizeOrZero(encodePathwayContext(dataStreamsContext))
|
|
195
|
+
payloadSize += CONTEXT_PROPAGATION_KEY.length
|
|
196
|
+
}
|
|
197
|
+
const checkpoint = {
|
|
198
|
+
currentTimestamp: nowNs,
|
|
199
|
+
parentHash: parentHash,
|
|
200
|
+
hash: hash,
|
|
201
|
+
edgeTags: edgeTags,
|
|
202
|
+
edgeLatencyNs: edgeLatencyNs,
|
|
203
|
+
pathwayLatencyNs: pathwayLatencyNs,
|
|
204
|
+
payloadSize: payloadSize
|
|
205
|
+
}
|
|
206
|
+
this.recordCheckpoint(checkpoint, span)
|
|
207
|
+
return dataStreamsContext
|
|
167
208
|
}
|
|
168
209
|
|
|
169
210
|
_serializeBuckets () {
|
|
@@ -194,5 +235,9 @@ module.exports = {
|
|
|
194
235
|
StatsPoint: StatsPoint,
|
|
195
236
|
StatsBucket: StatsBucket,
|
|
196
237
|
TimeBuckets,
|
|
197
|
-
|
|
238
|
+
getMessageSize,
|
|
239
|
+
getHeadersSize,
|
|
240
|
+
getSizeOrZero,
|
|
241
|
+
ENTRY_PARENT_HASH,
|
|
242
|
+
CONTEXT_PROPAGATION_KEY
|
|
198
243
|
}
|
|
@@ -14,7 +14,7 @@ const SPAN_SAMPLING_MECHANISM = constants.SPAN_SAMPLING_MECHANISM
|
|
|
14
14
|
const SPAN_SAMPLING_RULE_RATE = constants.SPAN_SAMPLING_RULE_RATE
|
|
15
15
|
const SPAN_SAMPLING_MAX_PER_SECOND = constants.SPAN_SAMPLING_MAX_PER_SECOND
|
|
16
16
|
const SAMPLING_MECHANISM_SPAN = constants.SAMPLING_MECHANISM_SPAN
|
|
17
|
-
const { MEASURED, BASE_SERVICE } = tags
|
|
17
|
+
const { MEASURED, BASE_SERVICE, ANALYTICS } = tags
|
|
18
18
|
const ORIGIN_KEY = constants.ORIGIN_KEY
|
|
19
19
|
const HOSTNAME_KEY = constants.HOSTNAME_KEY
|
|
20
20
|
const TOP_LEVEL_KEY = constants.TOP_LEVEL_KEY
|
|
@@ -24,6 +24,7 @@ const ERROR_STACK = constants.ERROR_STACK
|
|
|
24
24
|
const ERROR_TYPE = constants.ERROR_TYPE
|
|
25
25
|
|
|
26
26
|
const map = {
|
|
27
|
+
'operation.name': 'name',
|
|
27
28
|
'service.name': 'service',
|
|
28
29
|
'span.type': 'type',
|
|
29
30
|
'resource.name': 'resource'
|
|
@@ -83,6 +84,7 @@ function extractTags (trace, span) {
|
|
|
83
84
|
|
|
84
85
|
for (const tag in tags) {
|
|
85
86
|
switch (tag) {
|
|
87
|
+
case 'operation.name':
|
|
86
88
|
case 'service.name':
|
|
87
89
|
case 'span.type':
|
|
88
90
|
case 'resource.name':
|
|
@@ -92,6 +94,9 @@ function extractTags (trace, span) {
|
|
|
92
94
|
case 'http.status_code':
|
|
93
95
|
addTag(trace.meta, {}, tag, tags[tag] && String(tags[tag]))
|
|
94
96
|
break
|
|
97
|
+
case 'analytics.event':
|
|
98
|
+
addTag({}, trace.metrics, ANALYTICS, tags[tag] === undefined || tags[tag] ? 1 : 0)
|
|
99
|
+
break
|
|
95
100
|
case HOSTNAME_KEY:
|
|
96
101
|
case MEASURED:
|
|
97
102
|
addTag({}, trace.metrics, tag, tags[tag] === undefined || tags[tag] ? 1 : 0)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const semver = require('semver')
|
|
4
4
|
const logger = require('./log')
|
|
5
5
|
const { addHook } = require('import-in-the-middle')
|
|
6
|
-
const dc = require('
|
|
6
|
+
const dc = require('dc-polyfill')
|
|
7
7
|
|
|
8
8
|
if (semver.satisfies(process.versions.node, '>=14.13.1')) {
|
|
9
9
|
const moduleLoadStartChannel = dc.channel('dd-trace:moduleLoadStart')
|
|
@@ -11,6 +11,7 @@ const tracer = require('../../')
|
|
|
11
11
|
const DatadogSpan = require('../opentracing/span')
|
|
12
12
|
const { ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK } = require('../constants')
|
|
13
13
|
const { SERVICE_NAME, RESOURCE_NAME } = require('../../../../ext/tags')
|
|
14
|
+
const kinds = require('../../../../ext/kinds')
|
|
14
15
|
|
|
15
16
|
const SpanContext = require('./span_context')
|
|
16
17
|
|
|
@@ -19,6 +20,93 @@ function hrTimeToMilliseconds (time) {
|
|
|
19
20
|
return time[0] * 1e3 + time[1] / 1e6
|
|
20
21
|
}
|
|
21
22
|
|
|
23
|
+
const spanKindNames = {
|
|
24
|
+
[api.SpanKind.INTERNAL]: kinds.INTERNAL,
|
|
25
|
+
[api.SpanKind.SERVER]: kinds.SERVER,
|
|
26
|
+
[api.SpanKind.CLIENT]: kinds.CLIENT,
|
|
27
|
+
[api.SpanKind.PRODUCER]: kinds.PRODUCER,
|
|
28
|
+
[api.SpanKind.CONSUMER]: kinds.CONSUMER
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Several of these attributes are not yet supported by the Node.js OTel API.
|
|
33
|
+
* We check for old equivalents where we can, but not all had equivalents.
|
|
34
|
+
*/
|
|
35
|
+
function spanNameMapper (spanName, kind, attributes) {
|
|
36
|
+
if (spanName) return spanName
|
|
37
|
+
|
|
38
|
+
const opName = attributes['operation.name']
|
|
39
|
+
if (opName) return opName
|
|
40
|
+
|
|
41
|
+
const { INTERNAL, SERVER, CLIENT } = api.SpanKind
|
|
42
|
+
|
|
43
|
+
// HTTP server and client requests
|
|
44
|
+
// TODO: Drop http.method when http.request.method is supported.
|
|
45
|
+
for (const key of ['http.method', 'http.request.method']) {
|
|
46
|
+
if (key in attributes) {
|
|
47
|
+
if (kind === SERVER) {
|
|
48
|
+
return 'http.server.request'
|
|
49
|
+
}
|
|
50
|
+
if (kind === CLIENT) {
|
|
51
|
+
return 'http.client.request'
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Databases
|
|
57
|
+
const dbSystem = attributes['db.system']
|
|
58
|
+
if (dbSystem && kind === CLIENT) {
|
|
59
|
+
return `${dbSystem}.query`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Messaging
|
|
63
|
+
const msgSys = attributes['messaging.system']
|
|
64
|
+
const msgOp = attributes['messaging.operation']
|
|
65
|
+
if (msgSys && msgOp && kind !== INTERNAL) {
|
|
66
|
+
return `${msgSys}.${msgOp}`
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// RPC (and AWS)
|
|
70
|
+
const rpcSystem = attributes['rpc.system']
|
|
71
|
+
if (rpcSystem) {
|
|
72
|
+
if (kind === CLIENT) {
|
|
73
|
+
return rpcSystem === 'aws-api'
|
|
74
|
+
? `aws.${attributes['rpc.service'] || 'client'}.request`
|
|
75
|
+
: `${rpcSystem}.client.request`
|
|
76
|
+
}
|
|
77
|
+
if (kind === SERVER) {
|
|
78
|
+
return `${rpcSystem}.server.request`
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// FaaS
|
|
83
|
+
const faasProvider = attributes['faas.invoked_provider']
|
|
84
|
+
const faasName = attributes['faas.invoked_name']
|
|
85
|
+
const faasTrigger = attributes['faas.trigger']
|
|
86
|
+
if (kind === CLIENT && faasProvider && faasName) {
|
|
87
|
+
return `${faasProvider}.${faasName}.invoke`
|
|
88
|
+
}
|
|
89
|
+
if (kind === SERVER && faasTrigger) {
|
|
90
|
+
return `${faasTrigger}.invoke`
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// GraphQL
|
|
94
|
+
// NOTE: Not part of Semantic Convention spec yet, but is used in the GraphQL
|
|
95
|
+
// integration.
|
|
96
|
+
const isGraphQL = 'graphql.operation.type' in attributes
|
|
97
|
+
if (isGraphQL) return 'graphql.server.request'
|
|
98
|
+
|
|
99
|
+
// Network
|
|
100
|
+
// TODO: Doesn't exist yet. No equivalent.
|
|
101
|
+
const protocol = attributes['network.protocol.name']
|
|
102
|
+
const protocolPrefix = protocol ? `${protocol}.` : ''
|
|
103
|
+
if (kind === SERVER) return `${protocolPrefix}server.request`
|
|
104
|
+
if (kind === CLIENT) return `${protocolPrefix}client.request`
|
|
105
|
+
|
|
106
|
+
// If all else fails, default to stringified span.kind.
|
|
107
|
+
return spanKindNames[kind]
|
|
108
|
+
}
|
|
109
|
+
|
|
22
110
|
class Span {
|
|
23
111
|
constructor (
|
|
24
112
|
parentTracer,
|
|
@@ -27,7 +115,8 @@ class Span {
|
|
|
27
115
|
spanContext,
|
|
28
116
|
kind,
|
|
29
117
|
links = [],
|
|
30
|
-
timeInput
|
|
118
|
+
timeInput,
|
|
119
|
+
attributes
|
|
31
120
|
) {
|
|
32
121
|
const { _tracer } = tracer
|
|
33
122
|
|
|
@@ -35,7 +124,7 @@ class Span {
|
|
|
35
124
|
const startTime = hrTimeToMilliseconds(hrStartTime)
|
|
36
125
|
|
|
37
126
|
this._ddSpan = new DatadogSpan(_tracer, _tracer._processor, _tracer._prioritySampler, {
|
|
38
|
-
operationName: spanName,
|
|
127
|
+
operationName: spanNameMapper(spanName, kind, attributes),
|
|
39
128
|
context: spanContext._ddContext,
|
|
40
129
|
startTime,
|
|
41
130
|
hostname: _tracer._hostname,
|
|
@@ -46,6 +135,10 @@ class Span {
|
|
|
46
135
|
}
|
|
47
136
|
}, _tracer._debug)
|
|
48
137
|
|
|
138
|
+
if (attributes) {
|
|
139
|
+
this.setAttributes(attributes)
|
|
140
|
+
}
|
|
141
|
+
|
|
49
142
|
this._parentTracer = parentTracer
|
|
50
143
|
this._context = context
|
|
51
144
|
|
|
@@ -78,23 +78,22 @@ class Tracer {
|
|
|
78
78
|
// return api.trace.wrapSpanContext(spanContext)
|
|
79
79
|
// }
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
return new Span(
|
|
82
82
|
this,
|
|
83
83
|
context,
|
|
84
84
|
name,
|
|
85
85
|
spanContext,
|
|
86
86
|
spanKind,
|
|
87
87
|
links,
|
|
88
|
-
options.startTime
|
|
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
|
+
sanitizeAttributes(
|
|
93
|
+
// Object.assign(attributes, samplingResult.attributes)
|
|
94
|
+
attributes
|
|
95
|
+
)
|
|
89
96
|
)
|
|
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
97
|
}
|
|
99
98
|
|
|
100
99
|
startActiveSpan (name, options, context, fn) {
|
|
@@ -12,6 +12,7 @@ const runtimeMetrics = require('../runtime_metrics')
|
|
|
12
12
|
const log = require('../log')
|
|
13
13
|
const { storage } = require('../../../datadog-core')
|
|
14
14
|
const telemetryMetrics = require('../telemetry/metrics')
|
|
15
|
+
const { channel } = require('dc-polyfill')
|
|
15
16
|
|
|
16
17
|
const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
|
|
17
18
|
|
|
@@ -30,6 +31,8 @@ const integrationCounters = {
|
|
|
30
31
|
span_finished: {}
|
|
31
32
|
}
|
|
32
33
|
|
|
34
|
+
const finishCh = channel('dd-trace:span:finish')
|
|
35
|
+
|
|
33
36
|
function getIntegrationCounter (event, integration) {
|
|
34
37
|
const counters = integrationCounters[event]
|
|
35
38
|
|
|
@@ -176,6 +179,7 @@ class DatadogSpan {
|
|
|
176
179
|
this._duration = finishTime - this._startTime
|
|
177
180
|
this._spanContext._trace.finished.push(this)
|
|
178
181
|
this._spanContext._isFinished = true
|
|
182
|
+
finishCh.publish(this)
|
|
179
183
|
this._processor.process(this)
|
|
180
184
|
}
|
|
181
185
|
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
const { AUTO_KEEP } = require('../../../../ext/priority')
|
|
4
4
|
|
|
5
|
+
// the lowercase, hex encoded upper 64 bits of a 128-bit trace id, if present
|
|
6
|
+
const TRACE_ID_128 = '_dd.p.tid'
|
|
7
|
+
|
|
5
8
|
class DatadogSpanContext {
|
|
6
9
|
constructor (props) {
|
|
7
10
|
props = props || {}
|
|
@@ -35,8 +38,8 @@ class DatadogSpanContext {
|
|
|
35
38
|
|
|
36
39
|
toTraceparent () {
|
|
37
40
|
const flags = this._sampling.priority >= AUTO_KEEP ? '01' : '00'
|
|
38
|
-
const traceId = this._traceId.toBuffer().length <= 8 && this._trace.tags[
|
|
39
|
-
? this._trace.tags[
|
|
41
|
+
const traceId = this._traceId.toBuffer().length <= 8 && this._trace.tags[TRACE_ID_128]
|
|
42
|
+
? this._trace.tags[TRACE_ID_128] + this._traceId.toString(16).padStart(16, '0')
|
|
40
43
|
: this._traceId.toString(16).padStart(32, '0')
|
|
41
44
|
const spanId = this._spanId.toString(16).padStart(16, '0')
|
|
42
45
|
const version = (this._traceparent && this._traceparent.version) || '00'
|
|
@@ -36,7 +36,7 @@ class DatabasePlugin extends StoragePlugin {
|
|
|
36
36
|
const { encodedDddbs, encodedDde, encodedDdps, encodedDdpv } = this.serviceTags
|
|
37
37
|
|
|
38
38
|
return `dddbs='${encodedDddbs}',dde='${encodedDde}',` +
|
|
39
|
-
|
|
39
|
+
`ddps='${encodedDdps}',ddpv='${encodedDdpv}'`
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
getDbmServiceName (span, tracerService) {
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const URL = require('url').URL
|
|
2
|
-
|
|
3
1
|
const {
|
|
4
2
|
GIT_BRANCH,
|
|
5
3
|
GIT_COMMIT_SHA,
|
|
@@ -24,6 +22,7 @@ const {
|
|
|
24
22
|
CI_NODE_LABELS,
|
|
25
23
|
CI_NODE_NAME
|
|
26
24
|
} = require('./tags')
|
|
25
|
+
const { filterSensitiveInfoFromRepository } = require('./url')
|
|
27
26
|
|
|
28
27
|
// Receives a string with the form 'John Doe <john.doe@gmail.com>'
|
|
29
28
|
// and returns { name: 'John Doe', email: 'john.doe@gmail.com' }
|
|
@@ -67,20 +66,6 @@ function normalizeRef (ref) {
|
|
|
67
66
|
return ref.replace(/origin\/|refs\/heads\/|tags\//gm, '')
|
|
68
67
|
}
|
|
69
68
|
|
|
70
|
-
function filterSensitiveInfoFromRepository (repositoryUrl) {
|
|
71
|
-
if (repositoryUrl.startsWith('git@')) {
|
|
72
|
-
return repositoryUrl
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
try {
|
|
76
|
-
const { protocol, hostname, pathname } = new URL(repositoryUrl)
|
|
77
|
-
|
|
78
|
-
return `${protocol}//${hostname}${pathname}`
|
|
79
|
-
} catch (e) {
|
|
80
|
-
return ''
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
69
|
function resolveTilde (filePath) {
|
|
85
70
|
if (!filePath || typeof filePath !== 'string') {
|
|
86
71
|
return ''
|
|
@@ -271,20 +256,22 @@ module.exports = {
|
|
|
271
256
|
const ref = GITHUB_HEAD_REF || GITHUB_REF || ''
|
|
272
257
|
const refKey = ref.includes('tags/') ? GIT_TAG : GIT_BRANCH
|
|
273
258
|
|
|
259
|
+
// Both pipeline URL and job URL include GITHUB_SERVER_URL, which can include user credentials,
|
|
260
|
+
// so we pass them through `filterSensitiveInfoFromRepository`.
|
|
274
261
|
tags = {
|
|
275
262
|
[CI_PIPELINE_ID]: GITHUB_RUN_ID,
|
|
276
263
|
[CI_PIPELINE_NAME]: GITHUB_WORKFLOW,
|
|
277
264
|
[CI_PIPELINE_NUMBER]: GITHUB_RUN_NUMBER,
|
|
278
|
-
[CI_PIPELINE_URL]: pipelineURL,
|
|
265
|
+
[CI_PIPELINE_URL]: filterSensitiveInfoFromRepository(pipelineURL),
|
|
279
266
|
[CI_PROVIDER_NAME]: 'github',
|
|
280
267
|
[GIT_COMMIT_SHA]: GITHUB_SHA,
|
|
281
268
|
[GIT_REPOSITORY_URL]: repositoryURL,
|
|
282
|
-
[CI_JOB_URL]: jobUrl,
|
|
269
|
+
[CI_JOB_URL]: filterSensitiveInfoFromRepository(jobUrl),
|
|
283
270
|
[CI_JOB_NAME]: GITHUB_JOB,
|
|
284
271
|
[CI_WORKSPACE_PATH]: GITHUB_WORKSPACE,
|
|
285
272
|
[refKey]: ref,
|
|
286
273
|
[CI_ENV_VARS]: JSON.stringify({
|
|
287
|
-
GITHUB_SERVER_URL,
|
|
274
|
+
GITHUB_SERVER_URL: filterSensitiveInfoFromRepository(GITHUB_SERVER_URL),
|
|
288
275
|
GITHUB_REPOSITORY,
|
|
289
276
|
GITHUB_RUN_ID,
|
|
290
277
|
GITHUB_RUN_ATTEMPT
|
|
@@ -19,6 +19,7 @@ const {
|
|
|
19
19
|
GIT_COMMIT_AUTHOR_NAME,
|
|
20
20
|
CI_WORKSPACE_PATH
|
|
21
21
|
} = require('./tags')
|
|
22
|
+
const { filterSensitiveInfoFromRepository } = require('./url')
|
|
22
23
|
|
|
23
24
|
const GIT_REV_LIST_MAX_BUFFER = 8 * 1024 * 1024 // 8MB
|
|
24
25
|
|
|
@@ -214,7 +215,7 @@ function getGitMetadata (ciMetadata) {
|
|
|
214
215
|
|
|
215
216
|
return {
|
|
216
217
|
[GIT_REPOSITORY_URL]:
|
|
217
|
-
repositoryUrl || sanitizedExec('git', ['ls-remote', '--get-url']),
|
|
218
|
+
filterSensitiveInfoFromRepository(repositoryUrl || sanitizedExec('git', ['ls-remote', '--get-url'])),
|
|
218
219
|
[GIT_COMMIT_MESSAGE]:
|
|
219
220
|
commitMessage || sanitizedExec('git', ['show', '-s', '--format=%s']),
|
|
220
221
|
[GIT_COMMIT_AUTHOR_DATE]: authorDate,
|
|
@@ -48,8 +48,8 @@ function extractIp (config, req) {
|
|
|
48
48
|
|
|
49
49
|
let firstPrivateIp
|
|
50
50
|
if (headers) {
|
|
51
|
-
for (
|
|
52
|
-
const firstIp = findFirstIp(headers[
|
|
51
|
+
for (const ipHeaderName of ipHeaderList) {
|
|
52
|
+
const firstIp = findFirstIp(headers[ipHeaderName])
|
|
53
53
|
|
|
54
54
|
if (firstIp.public) {
|
|
55
55
|
return firstIp.public
|
|
@@ -59,7 +59,7 @@ function extractIp (config, req) {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
return firstPrivateIp ||
|
|
62
|
+
return firstPrivateIp || req.socket?.remoteAddress
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
function findFirstIp (str) {
|
|
@@ -68,8 +68,8 @@ function findFirstIp (str) {
|
|
|
68
68
|
|
|
69
69
|
const splitted = str.split(',')
|
|
70
70
|
|
|
71
|
-
for (
|
|
72
|
-
const chunk =
|
|
71
|
+
for (const part of splitted) {
|
|
72
|
+
const chunk = part.trim()
|
|
73
73
|
|
|
74
74
|
// TODO: strip port and interface data ?
|
|
75
75
|
|
|
@@ -90,5 +90,6 @@ function findFirstIp (str) {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
module.exports = {
|
|
93
|
-
extractIp
|
|
93
|
+
extractIp,
|
|
94
|
+
ipHeaderList
|
|
94
95
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const { URL } = require('url')
|
|
2
|
+
|
|
3
|
+
function filterSensitiveInfoFromRepository (repositoryUrl) {
|
|
4
|
+
if (!repositoryUrl) {
|
|
5
|
+
return ''
|
|
6
|
+
}
|
|
7
|
+
if (repositoryUrl.startsWith('git@')) {
|
|
8
|
+
return repositoryUrl
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Remove the username from ssh URLs
|
|
12
|
+
if (repositoryUrl.startsWith('ssh://')) {
|
|
13
|
+
const sshRegex = /^(ssh:\/\/)[^@/]*@/
|
|
14
|
+
return repositoryUrl.replace(sshRegex, '$1')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const { protocol, host, pathname } = new URL(repositoryUrl)
|
|
19
|
+
|
|
20
|
+
return `${protocol}//${host}${pathname === '/' ? '' : pathname}`
|
|
21
|
+
} catch (e) {
|
|
22
|
+
return ''
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = { filterSensitiveInfoFromRepository }
|
|
@@ -13,7 +13,7 @@ const {
|
|
|
13
13
|
} = require('./tags')
|
|
14
14
|
|
|
15
15
|
const { normalizeRef } = require('./ci')
|
|
16
|
-
const {
|
|
16
|
+
const { filterSensitiveInfoFromRepository } = require('./url')
|
|
17
17
|
|
|
18
18
|
function removeEmptyValues (tags) {
|
|
19
19
|
return Object.keys(tags).reduce((filteredTags, tag) => {
|
|
@@ -27,19 +27,6 @@ function removeEmptyValues (tags) {
|
|
|
27
27
|
}, {})
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
function filterSensitiveInfoFromRepository (repositoryUrl) {
|
|
31
|
-
try {
|
|
32
|
-
if (repositoryUrl.startsWith('git@')) {
|
|
33
|
-
return repositoryUrl
|
|
34
|
-
}
|
|
35
|
-
const { protocol, hostname, pathname } = new URL(repositoryUrl)
|
|
36
|
-
|
|
37
|
-
return `${protocol}//${hostname}${pathname}`
|
|
38
|
-
} catch (e) {
|
|
39
|
-
return repositoryUrl
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
30
|
// The regex is extracted from
|
|
44
31
|
// https://github.com/jonschlinkert/is-git-url/blob/396965ffabf2f46656c8af4c47bef1d69f09292e/index.js#L9C15-L9C87
|
|
45
32
|
function validateGitRepositoryUrl (repoUrl) {
|
|
@@ -9,6 +9,7 @@ const { FileExporter } = require('./exporters/file')
|
|
|
9
9
|
const { ConsoleLogger } = require('./loggers/console')
|
|
10
10
|
const WallProfiler = require('./profilers/wall')
|
|
11
11
|
const SpaceProfiler = require('./profilers/space')
|
|
12
|
+
const EventsProfiler = require('./profilers/events')
|
|
12
13
|
const { oomExportStrategies, snapshotKinds } = require('./constants')
|
|
13
14
|
const { tagger } = require('./tagger')
|
|
14
15
|
const { isFalse, isTrue } = require('../util')
|
|
@@ -37,6 +38,7 @@ class Config {
|
|
|
37
38
|
DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE,
|
|
38
39
|
DD_PROFILING_EXPERIMENTAL_OOM_MAX_HEAP_EXTENSION_COUNT,
|
|
39
40
|
DD_PROFILING_EXPERIMENTAL_OOM_EXPORT_STRATEGIES,
|
|
41
|
+
DD_PROFILING_EXPERIMENTAL_TIMELINE_ENABLED,
|
|
40
42
|
DD_PROFILING_CODEHOTSPOTS_ENABLED,
|
|
41
43
|
DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
|
|
42
44
|
DD_PROFILING_EXPERIMENTAL_CODEHOTSPOTS_ENABLED,
|
|
@@ -126,7 +128,12 @@ class Config {
|
|
|
126
128
|
|
|
127
129
|
const profilers = options.profilers
|
|
128
130
|
? options.profilers
|
|
129
|
-
: getProfilers({
|
|
131
|
+
: getProfilers({
|
|
132
|
+
DD_PROFILING_HEAP_ENABLED,
|
|
133
|
+
DD_PROFILING_WALLTIME_ENABLED,
|
|
134
|
+
DD_PROFILING_EXPERIMENTAL_TIMELINE_ENABLED,
|
|
135
|
+
DD_PROFILING_PROFILERS
|
|
136
|
+
})
|
|
130
137
|
|
|
131
138
|
this.codeHotspotsEnabled = isTrue(coalesce(options.codeHotspotsEnabled,
|
|
132
139
|
DD_PROFILING_CODEHOTSPOTS_ENABLED,
|
|
@@ -139,7 +146,10 @@ class Config {
|
|
|
139
146
|
|
|
140
147
|
module.exports = { Config }
|
|
141
148
|
|
|
142
|
-
function getProfilers ({
|
|
149
|
+
function getProfilers ({
|
|
150
|
+
DD_PROFILING_HEAP_ENABLED, DD_PROFILING_WALLTIME_ENABLED,
|
|
151
|
+
DD_PROFILING_PROFILERS, DD_PROFILING_EXPERIMENTAL_TIMELINE_ENABLED
|
|
152
|
+
}) {
|
|
143
153
|
// First consider "legacy" DD_PROFILING_PROFILERS env variable, defaulting to wall + space
|
|
144
154
|
// Use a Set to avoid duplicates
|
|
145
155
|
const profilers = new Set(coalesce(DD_PROFILING_PROFILERS, 'wall,space').split(','))
|
|
@@ -162,6 +172,11 @@ function getProfilers ({ DD_PROFILING_HEAP_ENABLED, DD_PROFILING_WALLTIME_ENABLE
|
|
|
162
172
|
}
|
|
163
173
|
}
|
|
164
174
|
|
|
175
|
+
// Events profiler is a profiler for timeline events that goes with the wall
|
|
176
|
+
// profiler
|
|
177
|
+
if (profilers.has('wall') && DD_PROFILING_EXPERIMENTAL_TIMELINE_ENABLED) {
|
|
178
|
+
profilers.add('events')
|
|
179
|
+
}
|
|
165
180
|
return [...profilers]
|
|
166
181
|
}
|
|
167
182
|
|
|
@@ -223,6 +238,8 @@ function getProfiler (name, options) {
|
|
|
223
238
|
return new WallProfiler(options)
|
|
224
239
|
case 'space':
|
|
225
240
|
return new SpaceProfiler(options)
|
|
241
|
+
case 'events':
|
|
242
|
+
return new EventsProfiler(options)
|
|
226
243
|
default:
|
|
227
244
|
options.logger.error(`Unknown profiler "${name}"`)
|
|
228
245
|
}
|