dd-trace 2.4.2 → 2.5.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 +1 -0
- package/ci/init.js +6 -0
- package/ci/jest/env.js +16 -3
- package/ext/exporters.d.ts +2 -1
- package/ext/exporters.js +2 -1
- package/package.json +3 -2
- package/packages/datadog-instrumentations/src/cypress.js +8 -0
- package/packages/datadog-plugin-aws-sdk/src/helpers.js +4 -4
- package/packages/datadog-plugin-aws-sdk/src/index.js +1 -1
- package/packages/datadog-plugin-cucumber/src/index.js +24 -12
- package/packages/datadog-plugin-cypress/src/index.js +10 -5
- package/packages/datadog-plugin-cypress/src/plugin.js +13 -1
- package/packages/datadog-plugin-mocha/src/index.js +10 -1
- package/packages/dd-trace/lib/version.js +1 -1
- package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +3 -1
- package/packages/dd-trace/src/appsec/recommended.json +15 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +32 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +51 -0
- package/packages/dd-trace/src/config.js +8 -1
- package/packages/dd-trace/src/encode/0.4.js +0 -1
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +193 -0
- package/packages/dd-trace/src/encode/tags-processors.js +116 -0
- package/packages/dd-trace/src/exporter.js +3 -0
- package/packages/dd-trace/src/exporters/agent/index.js +1 -1
- package/packages/dd-trace/src/exporters/agent/writer.js +7 -32
- package/packages/dd-trace/src/exporters/{agent → common}/docker.js +0 -0
- package/packages/dd-trace/src/exporters/common/request.js +83 -0
- package/packages/dd-trace/src/exporters/common/writer.js +36 -0
- package/packages/dd-trace/src/exporters/{agent/scheduler.js → scheduler.js} +0 -0
- package/packages/dd-trace/src/instrumenter.js +3 -0
- package/packages/dd-trace/src/pkg.js +11 -6
- package/packages/dd-trace/src/plugins/util/test.js +60 -1
- package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
- package/packages/dd-trace/src/proxy.js +2 -0
- package/packages/dd-trace/src/telemetry.js +187 -0
- package/packages/dd-trace/src/exporters/agent/request.js +0 -86
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const { truncateSpan, normalizeSpan } = require('./tags-processors')
|
|
3
|
+
const Chunk = require('./chunk')
|
|
4
|
+
const { AgentEncoder } = require('./0.4')
|
|
5
|
+
|
|
6
|
+
const ENCODING_VERSION = 1
|
|
7
|
+
|
|
8
|
+
function formatSpan (span) {
|
|
9
|
+
return {
|
|
10
|
+
type: span.type === 'test' ? 'test' : 'span',
|
|
11
|
+
version: ENCODING_VERSION,
|
|
12
|
+
content: normalizeSpan(truncateSpan(span))
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class AgentlessCiVisibilityEncoder extends AgentEncoder {
|
|
17
|
+
constructor ({ runtimeId, service, env }) {
|
|
18
|
+
super(...arguments)
|
|
19
|
+
this._events = []
|
|
20
|
+
this.runtimeId = runtimeId
|
|
21
|
+
this.service = service
|
|
22
|
+
this.env = env
|
|
23
|
+
this._traceBytes = new Chunk()
|
|
24
|
+
this._stringBytes = new Chunk()
|
|
25
|
+
this._stringCount = 0
|
|
26
|
+
this._stringMap = {}
|
|
27
|
+
|
|
28
|
+
// Used to keep track of the number of encoded events to update the
|
|
29
|
+
// length of `payload.events` when calling `makePayload`
|
|
30
|
+
this._eventCount = 0
|
|
31
|
+
|
|
32
|
+
this.reset()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
_encodeEventContent (bytes, content) {
|
|
36
|
+
this._encodeMapPrefix(bytes, content)
|
|
37
|
+
if (content.type) {
|
|
38
|
+
this._encodeString(bytes, 'type')
|
|
39
|
+
this._encodeString(bytes, content.type)
|
|
40
|
+
}
|
|
41
|
+
this._encodeString(bytes, 'trace_id')
|
|
42
|
+
this._encodeId(bytes, content.trace_id)
|
|
43
|
+
this._encodeString(bytes, 'span_id')
|
|
44
|
+
this._encodeId(bytes, content.span_id)
|
|
45
|
+
this._encodeString(bytes, 'parent_id')
|
|
46
|
+
this._encodeId(bytes, content.parent_id)
|
|
47
|
+
this._encodeString(bytes, 'name')
|
|
48
|
+
this._encodeString(bytes, content.name)
|
|
49
|
+
this._encodeString(bytes, 'resource')
|
|
50
|
+
this._encodeString(bytes, content.resource)
|
|
51
|
+
this._encodeString(bytes, 'service')
|
|
52
|
+
this._encodeString(bytes, content.service)
|
|
53
|
+
this._encodeString(bytes, 'error')
|
|
54
|
+
this._encodeNumber(bytes, content.error)
|
|
55
|
+
this._encodeString(bytes, 'start')
|
|
56
|
+
this._encodeNumber(bytes, content.start)
|
|
57
|
+
this._encodeString(bytes, 'duration')
|
|
58
|
+
this._encodeNumber(bytes, content.duration)
|
|
59
|
+
this._encodeString(bytes, 'meta')
|
|
60
|
+
this._encodeMap(bytes, content.meta)
|
|
61
|
+
this._encodeString(bytes, 'metrics')
|
|
62
|
+
this._encodeMap(bytes, content.metrics)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
_encodeEvent (bytes, event) {
|
|
66
|
+
this._encodeMapPrefix(bytes, event)
|
|
67
|
+
this._encodeString(bytes, 'type')
|
|
68
|
+
this._encodeString(bytes, event.type)
|
|
69
|
+
|
|
70
|
+
this._encodeString(bytes, 'version')
|
|
71
|
+
this._encodeNumber(bytes, event.version)
|
|
72
|
+
|
|
73
|
+
this._encodeString(bytes, 'content')
|
|
74
|
+
this._encodeEventContent(bytes, event.content)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
_encodeNumber (bytes, value) {
|
|
78
|
+
if (Math.floor(value) !== value) { // float 64
|
|
79
|
+
return this._encodeFloat(bytes, value)
|
|
80
|
+
}
|
|
81
|
+
return this._encodeLong(bytes, value)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
_encodeLong (bytes, value) {
|
|
85
|
+
const isPositive = value >= 0
|
|
86
|
+
|
|
87
|
+
const hi = isPositive ? (value / Math.pow(2, 32)) >> 0 : Math.floor(value / Math.pow(2, 32))
|
|
88
|
+
const lo = value >>> 0
|
|
89
|
+
const flag = isPositive ? 0xcf : 0xd3
|
|
90
|
+
|
|
91
|
+
const buffer = bytes.buffer
|
|
92
|
+
const offset = bytes.length
|
|
93
|
+
|
|
94
|
+
// int 64
|
|
95
|
+
bytes.reserve(9)
|
|
96
|
+
bytes.length += 9
|
|
97
|
+
|
|
98
|
+
buffer[offset] = flag
|
|
99
|
+
buffer[offset + 1] = hi >> 24
|
|
100
|
+
buffer[offset + 2] = hi >> 16
|
|
101
|
+
buffer[offset + 3] = hi >> 8
|
|
102
|
+
buffer[offset + 4] = hi
|
|
103
|
+
buffer[offset + 5] = lo >> 24
|
|
104
|
+
buffer[offset + 6] = lo >> 16
|
|
105
|
+
buffer[offset + 7] = lo >> 8
|
|
106
|
+
buffer[offset + 8] = lo
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
_encodeMapPrefix (bytes, map) {
|
|
110
|
+
const keys = Object.keys(map)
|
|
111
|
+
const buffer = bytes.buffer
|
|
112
|
+
const offset = bytes.length
|
|
113
|
+
|
|
114
|
+
bytes.reserve(5)
|
|
115
|
+
bytes.length += 5
|
|
116
|
+
buffer[offset] = 0xdf
|
|
117
|
+
buffer[offset + 1] = keys.length >> 24
|
|
118
|
+
buffer[offset + 2] = keys.length >> 16
|
|
119
|
+
buffer[offset + 3] = keys.length >> 8
|
|
120
|
+
buffer[offset + 4] = keys.length
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
_encode (bytes, trace) {
|
|
124
|
+
this._eventCount += trace.length
|
|
125
|
+
const events = trace.map(formatSpan)
|
|
126
|
+
|
|
127
|
+
for (const event of events) {
|
|
128
|
+
this._encodeEvent(bytes, event)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
makePayload () {
|
|
133
|
+
const bytes = this._traceBytes
|
|
134
|
+
const eventsOffset = this._eventsOffset
|
|
135
|
+
const eventsCount = this._eventCount
|
|
136
|
+
|
|
137
|
+
bytes.buffer[eventsOffset] = 0xdd
|
|
138
|
+
bytes.buffer[eventsOffset + 1] = eventsCount >> 24
|
|
139
|
+
bytes.buffer[eventsOffset + 2] = eventsCount >> 16
|
|
140
|
+
bytes.buffer[eventsOffset + 3] = eventsCount >> 8
|
|
141
|
+
bytes.buffer[eventsOffset + 4] = eventsCount
|
|
142
|
+
|
|
143
|
+
const traceSize = bytes.length
|
|
144
|
+
const buffer = Buffer.allocUnsafe(traceSize)
|
|
145
|
+
|
|
146
|
+
bytes.buffer.copy(buffer, 0, 0, bytes.length)
|
|
147
|
+
|
|
148
|
+
this.reset()
|
|
149
|
+
|
|
150
|
+
return buffer
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
_encodePayloadStart (bytes) {
|
|
154
|
+
// encodes the payload up to `events`. `events` will be encoded via _encode
|
|
155
|
+
const payload = {
|
|
156
|
+
version: ENCODING_VERSION,
|
|
157
|
+
metadata: {
|
|
158
|
+
'*': {
|
|
159
|
+
'language': 'javascript'
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
events: []
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (this.env) {
|
|
166
|
+
payload.metadata['*'].env = this.env
|
|
167
|
+
}
|
|
168
|
+
if (this.runtimeId) {
|
|
169
|
+
payload.metadata['*']['runtime-id'] = this.runtimeId
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
this._encodeMapPrefix(bytes, payload)
|
|
173
|
+
this._encodeString(bytes, 'version')
|
|
174
|
+
this._encodeNumber(bytes, payload.version)
|
|
175
|
+
this._encodeString(bytes, 'metadata')
|
|
176
|
+
this._encodeMapPrefix(bytes, payload.metadata)
|
|
177
|
+
this._encodeString(bytes, '*')
|
|
178
|
+
this._encodeMap(bytes, payload.metadata['*'])
|
|
179
|
+
this._encodeString(bytes, 'events')
|
|
180
|
+
// Get offset of the events list to update the length of the array when calling `makePayload`
|
|
181
|
+
this._eventsOffset = bytes.length
|
|
182
|
+
bytes.reserve(5)
|
|
183
|
+
bytes.length += 5
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
reset () {
|
|
187
|
+
this._reset()
|
|
188
|
+
this._eventCount = 0
|
|
189
|
+
this._encodePayloadStart(this._traceBytes)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
module.exports = { AgentlessCiVisibilityEncoder }
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// From agent truncators: https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/agent/truncator.go
|
|
2
|
+
|
|
3
|
+
// Values from: https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/traceutil/truncate.go#L22-L27
|
|
4
|
+
// MAX_RESOURCE_NAME_LENGTH the maximum length a span resource can have
|
|
5
|
+
const MAX_RESOURCE_NAME_LENGTH = 5000
|
|
6
|
+
// MAX_META_KEY_LENGTH the maximum length of metadata key
|
|
7
|
+
const MAX_META_KEY_LENGTH = 200
|
|
8
|
+
// MAX_META_VALUE_LENGTH the maximum length of metadata value
|
|
9
|
+
const MAX_META_VALUE_LENGTH = 25000
|
|
10
|
+
// MAX_METRIC_KEY_LENGTH the maximum length of a metric name key
|
|
11
|
+
const MAX_METRIC_KEY_LENGTH = MAX_META_KEY_LENGTH
|
|
12
|
+
// MAX_METRIC_VALUE_LENGTH the maximum length of a metric name value
|
|
13
|
+
const MAX_METRIC_VALUE_LENGTH = MAX_META_VALUE_LENGTH
|
|
14
|
+
|
|
15
|
+
// From agent normalizer:
|
|
16
|
+
// https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/traceutil/normalize.go
|
|
17
|
+
// DEFAULT_SPAN_NAME is the default name we assign a span if it's missing and we have no reasonable fallback
|
|
18
|
+
const DEFAULT_SPAN_NAME = 'unnamed_operation'
|
|
19
|
+
// DEFAULT_SERVICE_NAME is the default name we assign a service if it's missing and we have no reasonable fallback
|
|
20
|
+
const DEFAULT_SERVICE_NAME = 'unnamed-service'
|
|
21
|
+
// MAX_NAME_LENGTH the maximum length a name can have
|
|
22
|
+
const MAX_NAME_LENGTH = 100
|
|
23
|
+
// MAX_SERVICE_LENGTH the maximum length a service can have
|
|
24
|
+
const MAX_SERVICE_LENGTH = 100
|
|
25
|
+
// MAX_TYPE_LENGTH the maximum length a span type can have
|
|
26
|
+
const MAX_TYPE_LENGTH = 100
|
|
27
|
+
|
|
28
|
+
const fromEntries = Object.fromEntries || (entries =>
|
|
29
|
+
entries.reduce((obj, [k, v]) => Object.assign(obj, { [k]: v }), {}))
|
|
30
|
+
|
|
31
|
+
function truncateToLength (value, maxLength) {
|
|
32
|
+
if (!value) {
|
|
33
|
+
return value
|
|
34
|
+
}
|
|
35
|
+
if (value.length > maxLength) {
|
|
36
|
+
return `${value.slice(0, maxLength)}...`
|
|
37
|
+
}
|
|
38
|
+
return value
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function truncateSpan (span) {
|
|
42
|
+
return fromEntries(Object.entries(span).map(([key, value]) => {
|
|
43
|
+
switch (key) {
|
|
44
|
+
case 'resource':
|
|
45
|
+
return ['resource', truncateToLength(value, MAX_RESOURCE_NAME_LENGTH)]
|
|
46
|
+
case 'meta':
|
|
47
|
+
return ['meta', fromEntries(Object.entries(value).map(([metaKey, metaValue]) =>
|
|
48
|
+
[truncateToLength(metaKey, MAX_META_KEY_LENGTH), truncateToLength(metaValue, MAX_META_VALUE_LENGTH)]
|
|
49
|
+
))]
|
|
50
|
+
case 'metrics':
|
|
51
|
+
return ['metrics', fromEntries(Object.entries(value).map(([metricsKey, metricsValue]) =>
|
|
52
|
+
[truncateToLength(metricsKey, MAX_METRIC_KEY_LENGTH), truncateToLength(metricsValue, MAX_METRIC_VALUE_LENGTH)]
|
|
53
|
+
))]
|
|
54
|
+
default:
|
|
55
|
+
return [key, value]
|
|
56
|
+
}
|
|
57
|
+
}))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function normalizeSpan (span) {
|
|
61
|
+
const normalizedSpan = fromEntries(Object.entries(span).map(([key, value]) => {
|
|
62
|
+
switch (key) {
|
|
63
|
+
case 'service':
|
|
64
|
+
if (!value) {
|
|
65
|
+
return [key, DEFAULT_SERVICE_NAME]
|
|
66
|
+
}
|
|
67
|
+
if (value.length > MAX_SERVICE_LENGTH) {
|
|
68
|
+
return [key, value.slice(0, MAX_SERVICE_LENGTH)]
|
|
69
|
+
}
|
|
70
|
+
break
|
|
71
|
+
case 'name':
|
|
72
|
+
if (!value) {
|
|
73
|
+
return [key, DEFAULT_SPAN_NAME]
|
|
74
|
+
}
|
|
75
|
+
if (value.length > MAX_NAME_LENGTH) {
|
|
76
|
+
return [key, value.slice(0, MAX_NAME_LENGTH)]
|
|
77
|
+
}
|
|
78
|
+
break
|
|
79
|
+
case 'resource':
|
|
80
|
+
if (!value) {
|
|
81
|
+
return [key, span.name || DEFAULT_SPAN_NAME]
|
|
82
|
+
}
|
|
83
|
+
break
|
|
84
|
+
case 'type':
|
|
85
|
+
if (!value) {
|
|
86
|
+
return [key, value]
|
|
87
|
+
}
|
|
88
|
+
if (value.length > MAX_TYPE_LENGTH) {
|
|
89
|
+
return [key, value.slice(0, MAX_TYPE_LENGTH)]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return [key, value]
|
|
93
|
+
}))
|
|
94
|
+
if (!normalizedSpan.service) {
|
|
95
|
+
normalizedSpan.service = DEFAULT_SERVICE_NAME
|
|
96
|
+
}
|
|
97
|
+
if (!normalizedSpan.name) {
|
|
98
|
+
normalizedSpan.name = DEFAULT_SPAN_NAME
|
|
99
|
+
}
|
|
100
|
+
return normalizedSpan
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = {
|
|
104
|
+
truncateSpan,
|
|
105
|
+
normalizeSpan,
|
|
106
|
+
MAX_META_KEY_LENGTH,
|
|
107
|
+
MAX_META_VALUE_LENGTH,
|
|
108
|
+
MAX_METRIC_KEY_LENGTH,
|
|
109
|
+
MAX_METRIC_VALUE_LENGTH,
|
|
110
|
+
MAX_NAME_LENGTH,
|
|
111
|
+
MAX_SERVICE_LENGTH,
|
|
112
|
+
MAX_TYPE_LENGTH,
|
|
113
|
+
MAX_RESOURCE_NAME_LENGTH,
|
|
114
|
+
DEFAULT_SPAN_NAME,
|
|
115
|
+
DEFAULT_SERVICE_NAME
|
|
116
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const AgentExporter = require('./exporters/agent')
|
|
4
4
|
const LogExporter = require('./exporters/log')
|
|
5
|
+
const AgentlessCiVisibilityExporter = require('./ci-visibility/exporters/agentless')
|
|
5
6
|
const exporters = require('../../../ext/exporters')
|
|
6
7
|
const fs = require('fs')
|
|
7
8
|
const constants = require('./constants')
|
|
@@ -15,6 +16,8 @@ module.exports = name => {
|
|
|
15
16
|
return LogExporter
|
|
16
17
|
case exporters.AGENT:
|
|
17
18
|
return AgentExporter
|
|
19
|
+
case exporters.DATADOG:
|
|
20
|
+
return AgentlessCiVisibilityExporter
|
|
18
21
|
default:
|
|
19
22
|
return inAWSLambda && !usingLambdaExtension ? LogExporter : AgentExporter
|
|
20
23
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const URL = require('url').URL
|
|
4
4
|
const log = require('../../log')
|
|
5
5
|
const Writer = require('./writer')
|
|
6
|
-
const Scheduler = require('
|
|
6
|
+
const Scheduler = require('../scheduler')
|
|
7
7
|
|
|
8
8
|
class AgentExporter {
|
|
9
9
|
constructor ({ url, hostname, port, flushInterval, lookup, protocolVersion }, prioritySampler) {
|
|
@@ -1,28 +1,23 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const request = require('
|
|
3
|
+
const request = require('../common/request')
|
|
4
4
|
const { startupLog } = require('../../startup-log')
|
|
5
5
|
const metrics = require('../../metrics')
|
|
6
6
|
const log = require('../../log')
|
|
7
7
|
const tracerVersion = require('../../../lib/version')
|
|
8
|
+
const BaseWriter = require('../common/writer')
|
|
8
9
|
|
|
9
10
|
const METRIC_PREFIX = 'datadog.tracer.node.exporter.agent'
|
|
10
11
|
|
|
11
|
-
class Writer {
|
|
12
|
-
constructor ({
|
|
12
|
+
class Writer extends BaseWriter {
|
|
13
|
+
constructor ({ prioritySampler, lookup, protocolVersion }) {
|
|
14
|
+
super(...arguments)
|
|
13
15
|
const AgentEncoder = getEncoder(protocolVersion)
|
|
14
16
|
|
|
15
|
-
this._url = url
|
|
16
17
|
this._prioritySampler = prioritySampler
|
|
17
18
|
this._lookup = lookup
|
|
18
19
|
this._protocolVersion = protocolVersion
|
|
19
|
-
this.
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
append (spans) {
|
|
23
|
-
log.debug(() => `Encoding trace: ${JSON.stringify(spans)}`)
|
|
24
|
-
|
|
25
|
-
this._encode(spans)
|
|
20
|
+
this._encoder = new AgentEncoder(this)
|
|
26
21
|
}
|
|
27
22
|
|
|
28
23
|
_sendPayload (data, count, done) {
|
|
@@ -62,26 +57,6 @@ class Writer {
|
|
|
62
57
|
done()
|
|
63
58
|
})
|
|
64
59
|
}
|
|
65
|
-
|
|
66
|
-
setUrl (url) {
|
|
67
|
-
this._url = url
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
_encode (trace) {
|
|
71
|
-
this._encoderForVersion.encode(trace)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
flush (done = () => {}) {
|
|
75
|
-
const count = this._encoderForVersion.count()
|
|
76
|
-
|
|
77
|
-
if (count > 0) {
|
|
78
|
-
const payload = this._encoderForVersion.makePayload()
|
|
79
|
-
|
|
80
|
-
this._sendPayload(payload, count, done)
|
|
81
|
-
} else {
|
|
82
|
-
done()
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
60
|
}
|
|
86
61
|
|
|
87
62
|
function setHeader (headers, key, value) {
|
|
@@ -124,7 +99,7 @@ function makeRequest (version, data, count, url, lookup, needsStartupLog, cb) {
|
|
|
124
99
|
|
|
125
100
|
log.debug(() => `Request to the agent: ${JSON.stringify(options)}`)
|
|
126
101
|
|
|
127
|
-
request(
|
|
102
|
+
request(data, options, true, (err, res, status) => {
|
|
128
103
|
if (needsStartupLog) {
|
|
129
104
|
// Note that logging will only happen once, regardless of how many times this is called.
|
|
130
105
|
startupLog({
|
|
File without changes
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const http = require('http')
|
|
4
|
+
const https = require('https')
|
|
5
|
+
const log = require('../../log')
|
|
6
|
+
const docker = require('./docker')
|
|
7
|
+
const { storage } = require('../../../../datadog-core')
|
|
8
|
+
|
|
9
|
+
const httpAgent = new http.Agent({ keepAlive: true })
|
|
10
|
+
const httpsAgent = new https.Agent({ keepAlive: true })
|
|
11
|
+
const containerId = docker.id()
|
|
12
|
+
|
|
13
|
+
function request (data, options, keepAlive, callback) {
|
|
14
|
+
if (!options.headers) {
|
|
15
|
+
options.headers = {}
|
|
16
|
+
}
|
|
17
|
+
const isSecure = options.protocol === 'https:'
|
|
18
|
+
const client = isSecure ? https : http
|
|
19
|
+
const dataArray = [].concat(data)
|
|
20
|
+
options.headers['Content-Length'] = byteLength(dataArray)
|
|
21
|
+
|
|
22
|
+
if (containerId) {
|
|
23
|
+
options.headers['Datadog-Container-ID'] = containerId
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (keepAlive) {
|
|
27
|
+
options.agent = isSecure ? httpsAgent : httpAgent
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const firstRequest = retriableRequest(options, client, callback)
|
|
31
|
+
dataArray.forEach(buffer => firstRequest.write(buffer))
|
|
32
|
+
|
|
33
|
+
// The first request will be retried
|
|
34
|
+
const firstRequestErrorHandler = () => {
|
|
35
|
+
log.debug('Retrying request to the intake')
|
|
36
|
+
const retriedReq = retriableRequest(options, client, callback)
|
|
37
|
+
dataArray.forEach(buffer => retriedReq.write(buffer))
|
|
38
|
+
// The retried request will fail normally
|
|
39
|
+
retriedReq.on('error', e => callback(new Error(`Network error trying to reach the intake: ${e.message}`)))
|
|
40
|
+
retriedReq.end()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
firstRequest.on('error', firstRequestErrorHandler)
|
|
44
|
+
firstRequest.end()
|
|
45
|
+
|
|
46
|
+
return firstRequest
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function retriableRequest (options, client, callback) {
|
|
50
|
+
const store = storage.getStore()
|
|
51
|
+
|
|
52
|
+
storage.enterWith({ noop: true })
|
|
53
|
+
|
|
54
|
+
const timeout = options.timeout || 15000
|
|
55
|
+
|
|
56
|
+
const request = client.request(options, res => {
|
|
57
|
+
let responseData = ''
|
|
58
|
+
|
|
59
|
+
res.setTimeout(timeout)
|
|
60
|
+
|
|
61
|
+
res.on('data', chunk => { responseData += chunk })
|
|
62
|
+
res.on('end', () => {
|
|
63
|
+
if (res.statusCode >= 200 && res.statusCode <= 299) {
|
|
64
|
+
callback(null, responseData, res.statusCode)
|
|
65
|
+
} else {
|
|
66
|
+
const error = new Error(`Error from the endpoint: ${res.statusCode} ${http.STATUS_CODES[res.statusCode]}`)
|
|
67
|
+
error.status = res.statusCode
|
|
68
|
+
|
|
69
|
+
callback(error, null, res.statusCode)
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
request.setTimeout(timeout, request.abort)
|
|
74
|
+
storage.enterWith(store)
|
|
75
|
+
|
|
76
|
+
return request
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function byteLength (data) {
|
|
80
|
+
return data.length > 0 ? data.reduce((prev, next) => prev + next.length, 0) : 0
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module.exports = request
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const log = require('../../log')
|
|
3
|
+
|
|
4
|
+
class Writer {
|
|
5
|
+
constructor ({ url }) {
|
|
6
|
+
this._url = url
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
flush (done = () => {}) {
|
|
10
|
+
const count = this._encoder.count()
|
|
11
|
+
|
|
12
|
+
if (count > 0) {
|
|
13
|
+
const payload = this._encoder.makePayload()
|
|
14
|
+
|
|
15
|
+
this._sendPayload(payload, count, done)
|
|
16
|
+
} else {
|
|
17
|
+
done()
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
append (spans) {
|
|
22
|
+
log.debug(() => `Encoding trace: ${JSON.stringify(spans)}`)
|
|
23
|
+
|
|
24
|
+
this._encode(spans)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
_encode (trace) {
|
|
28
|
+
this._encoder.encode(trace)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
setUrl (url) {
|
|
32
|
+
this._url = url
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = Writer
|
|
File without changes
|
|
@@ -7,6 +7,7 @@ const Loader = require('./loader')
|
|
|
7
7
|
const { isTrue } = require('./util')
|
|
8
8
|
const plugins = require('./plugins')
|
|
9
9
|
const Plugin = require('./plugins/plugin')
|
|
10
|
+
const telemetry = require('./telemetry')
|
|
10
11
|
|
|
11
12
|
const disabledPlugins = process.env.DD_TRACE_DISABLED_PLUGINS
|
|
12
13
|
|
|
@@ -54,6 +55,7 @@ class Instrumenter {
|
|
|
54
55
|
|
|
55
56
|
try {
|
|
56
57
|
this._set(plugin, { name, config })
|
|
58
|
+
telemetry.updateIntegrations()
|
|
57
59
|
} catch (e) {
|
|
58
60
|
log.debug(`Could not find a plugin named "${name}".`)
|
|
59
61
|
}
|
|
@@ -154,6 +156,7 @@ class Instrumenter {
|
|
|
154
156
|
|
|
155
157
|
if (!instrumented) {
|
|
156
158
|
this._instrumented.set(instrumentation, instrumented = new Set())
|
|
159
|
+
telemetry.updateIntegrations()
|
|
157
160
|
}
|
|
158
161
|
|
|
159
162
|
if (!instrumented.has(this._defaultExport(moduleExports))) {
|
|
@@ -11,7 +11,14 @@ function findRoot () {
|
|
|
11
11
|
|
|
12
12
|
function findPkg () {
|
|
13
13
|
const cwd = findRoot()
|
|
14
|
-
const
|
|
14
|
+
const directory = path.resolve(cwd)
|
|
15
|
+
const res = path.parse(directory)
|
|
16
|
+
|
|
17
|
+
if (!res) return {}
|
|
18
|
+
|
|
19
|
+
const { root } = res
|
|
20
|
+
|
|
21
|
+
const filePath = findUp('package.json', root, directory)
|
|
15
22
|
|
|
16
23
|
try {
|
|
17
24
|
return JSON.parse(fs.readFileSync(filePath, 'utf8'))
|
|
@@ -20,18 +27,16 @@ function findPkg () {
|
|
|
20
27
|
}
|
|
21
28
|
}
|
|
22
29
|
|
|
23
|
-
function findUp (name,
|
|
24
|
-
let directory = path.resolve(cwd)
|
|
25
|
-
const { root } = path.parse(directory)
|
|
26
|
-
|
|
30
|
+
function findUp (name, root, directory) {
|
|
27
31
|
while (true) {
|
|
28
32
|
const current = path.resolve(directory, name)
|
|
29
33
|
|
|
30
34
|
if (fs.existsSync(current)) return current
|
|
35
|
+
|
|
31
36
|
if (directory === root) return
|
|
32
37
|
|
|
33
38
|
directory = path.dirname(directory)
|
|
34
39
|
}
|
|
35
40
|
}
|
|
36
41
|
|
|
37
|
-
module.exports = findPkg()
|
|
42
|
+
module.exports = Object.assign(findPkg(), { findRoot, findUp })
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
|
|
4
|
+
const ignore = require('ignore')
|
|
2
5
|
|
|
3
6
|
const { getGitMetadata } = require('./git')
|
|
4
7
|
const { getUserProviderGitMetadata } = require('./user-provided-git')
|
|
@@ -25,6 +28,7 @@ const TEST_STATUS = 'test.status'
|
|
|
25
28
|
const TEST_PARAMETERS = 'test.parameters'
|
|
26
29
|
const TEST_SKIP_REASON = 'test.skip_reason'
|
|
27
30
|
const TEST_IS_RUM_ACTIVE = 'test.is_rum_active'
|
|
31
|
+
const TEST_CODE_OWNERS = 'test.codeowners'
|
|
28
32
|
|
|
29
33
|
const ERROR_TYPE = 'error.type'
|
|
30
34
|
const ERROR_MESSAGE = 'error.msg'
|
|
@@ -35,6 +39,7 @@ const CI_APP_ORIGIN = 'ciapp-test'
|
|
|
35
39
|
const JEST_TEST_RUNNER = 'test.jest.test_runner'
|
|
36
40
|
|
|
37
41
|
module.exports = {
|
|
42
|
+
TEST_CODE_OWNERS,
|
|
38
43
|
TEST_FRAMEWORK,
|
|
39
44
|
TEST_FRAMEWORK_VERSION,
|
|
40
45
|
JEST_TEST_RUNNER,
|
|
@@ -53,7 +58,9 @@ module.exports = {
|
|
|
53
58
|
getTestParametersString,
|
|
54
59
|
finishAllTraceSpans,
|
|
55
60
|
getTestParentSpan,
|
|
56
|
-
getTestSuitePath
|
|
61
|
+
getTestSuitePath,
|
|
62
|
+
getCodeOwnersFileEntries,
|
|
63
|
+
getCodeOwnersForFilename
|
|
57
64
|
}
|
|
58
65
|
|
|
59
66
|
function getTestEnvironmentMetadata (testFramework, config) {
|
|
@@ -140,3 +147,55 @@ function getTestSuitePath (testSuiteAbsolutePath, sourceRoot) {
|
|
|
140
147
|
|
|
141
148
|
return testSuitePath.replace(path.sep, '/')
|
|
142
149
|
}
|
|
150
|
+
|
|
151
|
+
const POSSIBLE_CODEOWNERS_LOCATIONS = [
|
|
152
|
+
'CODEOWNERS',
|
|
153
|
+
'.github/CODEOWNERS',
|
|
154
|
+
'docs/CODEOWNERS',
|
|
155
|
+
'.gitlab/CODEOWNERS'
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
function getCodeOwnersFileEntries (rootDir = process.cwd()) {
|
|
159
|
+
let codeOwnersContent
|
|
160
|
+
|
|
161
|
+
POSSIBLE_CODEOWNERS_LOCATIONS.forEach(location => {
|
|
162
|
+
try {
|
|
163
|
+
codeOwnersContent = fs.readFileSync(`${rootDir}/${location}`).toString()
|
|
164
|
+
} catch (e) {
|
|
165
|
+
// retry with next path
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
if (!codeOwnersContent) {
|
|
169
|
+
return null
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const entries = []
|
|
173
|
+
const lines = codeOwnersContent.split('\n')
|
|
174
|
+
|
|
175
|
+
for (const line of lines) {
|
|
176
|
+
const [content] = line.split('#')
|
|
177
|
+
const trimmed = content.trim()
|
|
178
|
+
if (trimmed === '') continue
|
|
179
|
+
const [pattern, ...owners] = trimmed.split(/\s+/)
|
|
180
|
+
entries.push({ pattern, owners })
|
|
181
|
+
}
|
|
182
|
+
// Reverse because rules defined last take precedence
|
|
183
|
+
return entries.reverse()
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function getCodeOwnersForFilename (filename, entries) {
|
|
187
|
+
if (!entries) {
|
|
188
|
+
return null
|
|
189
|
+
}
|
|
190
|
+
for (const entry of entries) {
|
|
191
|
+
try {
|
|
192
|
+
const isResponsible = ignore().add(entry.pattern).ignores(filename)
|
|
193
|
+
if (isResponsible) {
|
|
194
|
+
return JSON.stringify(entry.owners)
|
|
195
|
+
}
|
|
196
|
+
} catch (e) {
|
|
197
|
+
return null
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return null
|
|
201
|
+
}
|
|
@@ -5,7 +5,7 @@ const { request } = require('http')
|
|
|
5
5
|
const FormData = require('form-data')
|
|
6
6
|
|
|
7
7
|
// TODO: avoid using dd-trace internals. Make this a separate module?
|
|
8
|
-
const docker = require('../../exporters/
|
|
8
|
+
const docker = require('../../exporters/common/docker')
|
|
9
9
|
const version = require('../../../lib/version')
|
|
10
10
|
|
|
11
11
|
const containerId = docker.id()
|