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
|
@@ -10,6 +10,7 @@ const metrics = require('./metrics')
|
|
|
10
10
|
const log = require('./log')
|
|
11
11
|
const { isFalse } = require('./util')
|
|
12
12
|
const { setStartupLogInstrumenter } = require('./startup-log')
|
|
13
|
+
const telemetry = require('./telemetry')
|
|
13
14
|
|
|
14
15
|
const noop = new NoopTracer()
|
|
15
16
|
|
|
@@ -63,6 +64,7 @@ class Tracer extends BaseTracer {
|
|
|
63
64
|
this._instrumenter.enable(config)
|
|
64
65
|
this._pluginManager.configure(config)
|
|
65
66
|
setStartupLogInstrumenter(this._instrumenter)
|
|
67
|
+
telemetry.start(config, this._instrumenter, this._pluginManager)
|
|
66
68
|
}
|
|
67
69
|
} catch (e) {
|
|
68
70
|
log.error(e)
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const tracerVersion = require('../lib/version')
|
|
4
|
+
const pkg = require('./pkg')
|
|
5
|
+
const containerId = require('./exporters/common/docker').id()
|
|
6
|
+
const requirePackageJson = require('./require-package-json')
|
|
7
|
+
const path = require('path')
|
|
8
|
+
const os = require('os')
|
|
9
|
+
const request = require('./exporters/common/request')
|
|
10
|
+
|
|
11
|
+
let config
|
|
12
|
+
let instrumenter
|
|
13
|
+
let pluginManager
|
|
14
|
+
|
|
15
|
+
let seqId = 0
|
|
16
|
+
let application
|
|
17
|
+
let host
|
|
18
|
+
let interval
|
|
19
|
+
const sentIntegrations = new Set()
|
|
20
|
+
|
|
21
|
+
function getIntegrations () {
|
|
22
|
+
const newIntegrations = []
|
|
23
|
+
for (const plugin of instrumenter._instrumented.keys()) {
|
|
24
|
+
if (sentIntegrations.has(plugin.name)) {
|
|
25
|
+
continue
|
|
26
|
+
}
|
|
27
|
+
newIntegrations.push({
|
|
28
|
+
name: plugin.name,
|
|
29
|
+
enabled: true,
|
|
30
|
+
auto_enabled: true
|
|
31
|
+
})
|
|
32
|
+
sentIntegrations.add(plugin.name)
|
|
33
|
+
}
|
|
34
|
+
for (const pluginName in pluginManager._pluginsByName) {
|
|
35
|
+
if (sentIntegrations.has(pluginName)) {
|
|
36
|
+
continue
|
|
37
|
+
}
|
|
38
|
+
newIntegrations.push({
|
|
39
|
+
name: pluginName,
|
|
40
|
+
enabled: pluginManager._pluginsByName[pluginName]._enabled,
|
|
41
|
+
auto_enabled: true
|
|
42
|
+
})
|
|
43
|
+
sentIntegrations.add(pluginName)
|
|
44
|
+
}
|
|
45
|
+
return newIntegrations
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function getDependencies () {
|
|
49
|
+
const deps = []
|
|
50
|
+
const { dependencies } = pkg
|
|
51
|
+
if (!dependencies) {
|
|
52
|
+
return deps
|
|
53
|
+
}
|
|
54
|
+
const rootDir = pkg.findRoot()
|
|
55
|
+
for (const [name, version] of Object.entries(dependencies)) {
|
|
56
|
+
const dep = { name }
|
|
57
|
+
try {
|
|
58
|
+
dep.version = requirePackageJson(
|
|
59
|
+
path.join(rootDir, 'node_modules', name.replace('/', path.sep))
|
|
60
|
+
).version
|
|
61
|
+
} catch (e) {
|
|
62
|
+
dep.version = version
|
|
63
|
+
}
|
|
64
|
+
deps.push(dep)
|
|
65
|
+
}
|
|
66
|
+
return deps
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function flatten (input, result = [], prefix = [], traversedObjects = null) {
|
|
70
|
+
traversedObjects = traversedObjects || new WeakSet()
|
|
71
|
+
if (traversedObjects.has(input)) {
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
traversedObjects.add(input)
|
|
75
|
+
for (const [key, value] of Object.entries(input)) {
|
|
76
|
+
if (typeof value === 'object' && value !== null) {
|
|
77
|
+
flatten(value, result, [...prefix, key], traversedObjects)
|
|
78
|
+
} else {
|
|
79
|
+
result.push({ name: [...prefix, key].join('.'), value })
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return result
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function appStarted () {
|
|
86
|
+
return {
|
|
87
|
+
integrations: getIntegrations(),
|
|
88
|
+
dependencies: getDependencies(),
|
|
89
|
+
configuration: flatten(config),
|
|
90
|
+
additional_payload: []
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function onBeforeExit () {
|
|
95
|
+
process.removeListener('beforeExit', onBeforeExit)
|
|
96
|
+
sendData('app-closing')
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function createAppObject () {
|
|
100
|
+
return {
|
|
101
|
+
service_name: config.service,
|
|
102
|
+
env: config.env,
|
|
103
|
+
service_version: config.version,
|
|
104
|
+
tracer_version: tracerVersion,
|
|
105
|
+
language_name: 'nodejs',
|
|
106
|
+
language_version: process.versions.node
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function createHostObject () {
|
|
111
|
+
return {
|
|
112
|
+
hostname: os.hostname(), // TODO is this enough?
|
|
113
|
+
container_id: containerId
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function sendData (reqType, payload = {}) {
|
|
118
|
+
const {
|
|
119
|
+
hostname,
|
|
120
|
+
port
|
|
121
|
+
} = config
|
|
122
|
+
const options = {
|
|
123
|
+
hostname,
|
|
124
|
+
port,
|
|
125
|
+
method: 'POST',
|
|
126
|
+
path: '/telemetry/proxy/api/v2/apmtelemetry',
|
|
127
|
+
headers: {
|
|
128
|
+
'content-type': 'application/json',
|
|
129
|
+
'dd-telemetry-api-version': 'v1',
|
|
130
|
+
'dd-telemetry-request-type': reqType
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const data = JSON.stringify({
|
|
134
|
+
api_version: 'v1',
|
|
135
|
+
request_type: reqType,
|
|
136
|
+
tracer_time: Math.floor(Date.now() / 1000),
|
|
137
|
+
runtime_id: config.tags['runtime-id'],
|
|
138
|
+
seq_id: ++seqId,
|
|
139
|
+
payload,
|
|
140
|
+
application,
|
|
141
|
+
host
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
request(data, options, true, () => {
|
|
145
|
+
// ignore errors
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function start (aConfig, theInstrumenter, thePluginManager) {
|
|
150
|
+
if (!aConfig.telemetryEnabled) {
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
config = aConfig
|
|
154
|
+
instrumenter = theInstrumenter
|
|
155
|
+
pluginManager = thePluginManager
|
|
156
|
+
application = createAppObject()
|
|
157
|
+
host = createHostObject()
|
|
158
|
+
sendData('app-started', appStarted())
|
|
159
|
+
interval = setInterval(() => sendData('app-heartbeat'), 60000)
|
|
160
|
+
interval.unref()
|
|
161
|
+
process.on('beforeExit', onBeforeExit)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function stop () {
|
|
165
|
+
if (!config) {
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
clearInterval(interval)
|
|
169
|
+
process.removeListener('beforeExit', onBeforeExit)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function updateIntegrations () {
|
|
173
|
+
if (!config || !config.telemetryEnabled) {
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
const integrations = getIntegrations()
|
|
177
|
+
if (integrations.length === 0) {
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
sendData('app-integrations-change', { integrations })
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
module.exports = {
|
|
184
|
+
start,
|
|
185
|
+
stop,
|
|
186
|
+
updateIntegrations
|
|
187
|
+
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const http = require('http')
|
|
4
|
-
const https = require('https')
|
|
5
|
-
const docker = require('./docker')
|
|
6
|
-
const log = require('../../log')
|
|
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 retriableRequest (options, callback, client, data) {
|
|
14
|
-
const store = storage.getStore()
|
|
15
|
-
|
|
16
|
-
storage.enterWith({ noop: true })
|
|
17
|
-
|
|
18
|
-
const req = client.request(options, res => {
|
|
19
|
-
let data = ''
|
|
20
|
-
|
|
21
|
-
res.setTimeout(options.timeout)
|
|
22
|
-
|
|
23
|
-
res.on('data', chunk => { data += chunk })
|
|
24
|
-
res.on('end', () => {
|
|
25
|
-
if (res.statusCode >= 200 && res.statusCode <= 299) {
|
|
26
|
-
callback(null, data, res.statusCode)
|
|
27
|
-
} else {
|
|
28
|
-
const error = new Error(`Error from the agent: ${res.statusCode} ${http.STATUS_CODES[res.statusCode]}`)
|
|
29
|
-
error.status = res.statusCode
|
|
30
|
-
|
|
31
|
-
callback(error, null, res.statusCode)
|
|
32
|
-
}
|
|
33
|
-
})
|
|
34
|
-
})
|
|
35
|
-
req.setTimeout(options.timeout, req.abort)
|
|
36
|
-
data.forEach(buffer => req.write(buffer))
|
|
37
|
-
|
|
38
|
-
storage.enterWith(store)
|
|
39
|
-
|
|
40
|
-
return req
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function request (options, callback) {
|
|
44
|
-
options = Object.assign({
|
|
45
|
-
headers: {},
|
|
46
|
-
data: [],
|
|
47
|
-
timeout: 2000
|
|
48
|
-
}, options)
|
|
49
|
-
|
|
50
|
-
const data = [].concat(options.data)
|
|
51
|
-
const isSecure = options.protocol === 'https:'
|
|
52
|
-
const client = isSecure ? https : http
|
|
53
|
-
const agent = isSecure ? httpsAgent : httpAgent
|
|
54
|
-
|
|
55
|
-
options.agent = agent
|
|
56
|
-
options.headers['Content-Length'] = byteLength(data)
|
|
57
|
-
|
|
58
|
-
if (containerId) {
|
|
59
|
-
options.headers['Datadog-Container-ID'] = containerId
|
|
60
|
-
}
|
|
61
|
-
const firstRequest = retriableRequest(options, callback, client, data)
|
|
62
|
-
|
|
63
|
-
// The first request will be retried if it fails due to a socket connection close
|
|
64
|
-
const firstRequestErrorHandler = error => {
|
|
65
|
-
if (firstRequest.reusedSocket && (error.code === 'ECONNRESET' || error.code === 'EPIPE')) {
|
|
66
|
-
log.debug('Retrying request due to socket connection error')
|
|
67
|
-
const retriedReq = retriableRequest(options, callback, client, data)
|
|
68
|
-
// The retried request will fail normally
|
|
69
|
-
retriedReq.on('error', e => callback(new Error(`Network error trying to reach the agent: ${e.message}`)))
|
|
70
|
-
retriedReq.end()
|
|
71
|
-
} else {
|
|
72
|
-
callback(new Error(`Network error trying to reach the agent: ${error.message}`))
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
firstRequest.on('error', firstRequestErrorHandler)
|
|
77
|
-
firstRequest.end()
|
|
78
|
-
|
|
79
|
-
return firstRequest
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function byteLength (data) {
|
|
83
|
-
return data.length > 0 ? data.reduce((prev, next) => prev + next.length, 0) : 0
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
module.exports = request
|