dd-trace 2.0.0-appsec-beta.4 → 2.0.0-appsec-beta.5
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/ci/init.js +5 -1
- package/ci/jest/env.js +5 -1
- package/index.d.ts +1 -1
- package/package.json +2 -1
- package/packages/datadog-instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/mysql.js +69 -0
- package/packages/datadog-plugin-cucumber/src/index.js +4 -4
- package/packages/datadog-plugin-cypress/src/plugin.js +6 -2
- package/packages/datadog-plugin-cypress/src/support.js +21 -6
- package/packages/datadog-plugin-dns/src/index.js +1 -1
- package/packages/datadog-plugin-jest/src/jest-environment.js +4 -4
- package/packages/datadog-plugin-jest/src/jest-jasmine2.js +2 -2
- package/packages/datadog-plugin-mocha/src/index.js +2 -2
- package/packages/datadog-plugin-mysql/src/index.js +37 -89
- package/packages/dd-trace/lib/version.js +1 -1
- package/packages/dd-trace/src/appsec/gateway/engine/engine.js +19 -30
- package/packages/dd-trace/src/appsec/gateway/engine/runner.js +2 -0
- package/packages/dd-trace/src/appsec/index.js +2 -5
- package/packages/dd-trace/src/appsec/reporter.js +2 -2
- package/packages/dd-trace/src/plugins/util/test.js +8 -2
- package/packages/dd-trace/src/profiling/exporters/agent.js +33 -32
package/ci/init.js
CHANGED
package/ci/jest/env.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
const tracer = require('../../packages/dd-trace')
|
|
2
|
+
const { ORIGIN_KEY } = require('../../packages/dd-trace/src/constants')
|
|
2
3
|
|
|
3
4
|
tracer.init({
|
|
4
5
|
startupLogs: false,
|
|
5
|
-
flushInterval: 400000
|
|
6
|
+
flushInterval: 400000,
|
|
7
|
+
tags: {
|
|
8
|
+
[ORIGIN_KEY]: 'ciapp-test'
|
|
9
|
+
}
|
|
6
10
|
})
|
|
7
11
|
|
|
8
12
|
tracer.use('fs', false)
|
package/index.d.ts
CHANGED
|
@@ -786,7 +786,7 @@ declare namespace plugins {
|
|
|
786
786
|
* This plugin automatically instruments the
|
|
787
787
|
* [cucumber](https://www.npmjs.com/package/@cucumber/cucumber) module.
|
|
788
788
|
*/
|
|
789
|
-
interface cucumber extends
|
|
789
|
+
interface cucumber extends Integration {}
|
|
790
790
|
|
|
791
791
|
/**
|
|
792
792
|
* This plugin automatically instruments the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "2.0.0-appsec-beta.
|
|
3
|
+
"version": "2.0.0-appsec-beta.5",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"bench": "node benchmark",
|
|
11
11
|
"bench:profiler": "node benchmark/profiler",
|
|
12
12
|
"bench:e2e": "SERVICES=mongo yarn services && cd benchmark/e2e && node benchmark-run.js --duration=30",
|
|
13
|
+
"bench:e2e:ci-visibility": "node benchmark/e2e-ci/benchmark-run.js",
|
|
13
14
|
"type:doc": "cd docs && yarn && yarn build",
|
|
14
15
|
"type:test": "cd docs && yarn && yarn test",
|
|
15
16
|
"lint": "node scripts/check_licenses.js && eslint . && yarn audit --groups dependencies",
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { AsyncResource } = require('async_hooks')
|
|
4
|
+
const {
|
|
5
|
+
channel,
|
|
6
|
+
addHook,
|
|
7
|
+
bind,
|
|
8
|
+
bindAsyncResource
|
|
9
|
+
} = require('./helpers/instrument')
|
|
10
|
+
const shimmer = require('../../datadog-shimmer')
|
|
11
|
+
|
|
12
|
+
addHook({ name: 'mysql', file: 'lib/Connection.js', versions: ['>=2'] }, Connection => {
|
|
13
|
+
const startCh = channel('apm:mysql:query:start')
|
|
14
|
+
const asyncEndCh = channel('apm:mysql:query:async-end')
|
|
15
|
+
const endCh = channel('apm:mysql:query:end')
|
|
16
|
+
const errorCh = channel('apm:mysql:query:error')
|
|
17
|
+
|
|
18
|
+
shimmer.wrap(Connection.prototype, 'query', query => function () {
|
|
19
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
20
|
+
if (!startCh.hasSubscribers) {
|
|
21
|
+
return query.apply(this, arguments)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const sql = arguments[0].sql ? arguments[0].sql : arguments[0]
|
|
25
|
+
const startArgs = [sql, this.config]
|
|
26
|
+
|
|
27
|
+
startCh.publish(startArgs)
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const res = query.apply(this, arguments)
|
|
31
|
+
|
|
32
|
+
if (res._callback) {
|
|
33
|
+
const cb = bindAsyncResource.call(asyncResource, res._callback)
|
|
34
|
+
res._callback = bind(function (error, result) {
|
|
35
|
+
if (error) {
|
|
36
|
+
errorCh.publish(error)
|
|
37
|
+
}
|
|
38
|
+
asyncEndCh.publish(result)
|
|
39
|
+
|
|
40
|
+
return cb.apply(this, arguments)
|
|
41
|
+
})
|
|
42
|
+
} else {
|
|
43
|
+
const cb = bind(function () {
|
|
44
|
+
asyncEndCh.publish(undefined)
|
|
45
|
+
})
|
|
46
|
+
res.on('end', cb)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return res
|
|
50
|
+
} catch (err) {
|
|
51
|
+
err.stack // trigger getting the stack at the original throwing point
|
|
52
|
+
errorCh.publish(err)
|
|
53
|
+
|
|
54
|
+
throw err
|
|
55
|
+
} finally {
|
|
56
|
+
endCh.publish(undefined)
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
return Connection
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
addHook({ name: 'mysql', file: 'lib/Pool.js', versions: ['>=2'] }, Pool => {
|
|
64
|
+
shimmer.wrap(Pool.prototype, 'getConnection', getConnection => function (cb) {
|
|
65
|
+
arguments[0] = bind(cb)
|
|
66
|
+
return getConnection.apply(this, arguments)
|
|
67
|
+
})
|
|
68
|
+
return Pool
|
|
69
|
+
})
|
|
@@ -103,8 +103,8 @@ module.exports = [
|
|
|
103
103
|
name: '@cucumber/cucumber',
|
|
104
104
|
versions: ['7.0.0 - 7.2.1'],
|
|
105
105
|
file: 'lib/runtime/pickle_runner.js',
|
|
106
|
-
patch (PickleRunner, tracer) {
|
|
107
|
-
const testEnvironmentMetadata = getTestEnvironmentMetadata('cucumber')
|
|
106
|
+
patch (PickleRunner, tracer, config) {
|
|
107
|
+
const testEnvironmentMetadata = getTestEnvironmentMetadata('cucumber', config)
|
|
108
108
|
const sourceRoot = process.cwd()
|
|
109
109
|
const pl = PickleRunner.default
|
|
110
110
|
this.wrap(
|
|
@@ -127,8 +127,8 @@ module.exports = [
|
|
|
127
127
|
name: '@cucumber/cucumber',
|
|
128
128
|
versions: ['>=7.3.0'],
|
|
129
129
|
file: 'lib/runtime/test_case_runner.js',
|
|
130
|
-
patch (TestCaseRunner, tracer) {
|
|
131
|
-
const testEnvironmentMetadata = getTestEnvironmentMetadata('cucumber')
|
|
130
|
+
patch (TestCaseRunner, tracer, config) {
|
|
131
|
+
const testEnvironmentMetadata = getTestEnvironmentMetadata('cucumber', config)
|
|
132
132
|
const sourceRoot = process.cwd()
|
|
133
133
|
const pl = TestCaseRunner.default
|
|
134
134
|
this.wrap(
|
|
@@ -4,6 +4,7 @@ const {
|
|
|
4
4
|
TEST_SUITE,
|
|
5
5
|
TEST_STATUS,
|
|
6
6
|
TEST_FRAMEWORK_VERSION,
|
|
7
|
+
TEST_IS_RUM_ACTIVE,
|
|
7
8
|
getTestEnvironmentMetadata,
|
|
8
9
|
CI_APP_ORIGIN,
|
|
9
10
|
getTestParentSpan
|
|
@@ -66,15 +67,18 @@ module.exports = (on, config) => {
|
|
|
66
67
|
}
|
|
67
68
|
})
|
|
68
69
|
}
|
|
69
|
-
return null
|
|
70
|
+
return activeSpan ? activeSpan._spanContext._traceId.toString(10) : null
|
|
70
71
|
},
|
|
71
72
|
'dd:afterEach': (test) => {
|
|
72
|
-
const { state, error } = test
|
|
73
|
+
const { state, error, isRUMActive } = test
|
|
73
74
|
if (activeSpan) {
|
|
74
75
|
activeSpan.setTag(TEST_STATUS, CYPRESS_STATUS_TO_TEST_STATUS[state])
|
|
75
76
|
if (error) {
|
|
76
77
|
activeSpan.setTag('error', error)
|
|
77
78
|
}
|
|
79
|
+
if (isRUMActive) {
|
|
80
|
+
activeSpan.setTag(TEST_IS_RUM_ACTIVE, true)
|
|
81
|
+
}
|
|
78
82
|
activeSpan.finish()
|
|
79
83
|
}
|
|
80
84
|
activeSpan = null
|
|
@@ -3,15 +3,30 @@ beforeEach(() => {
|
|
|
3
3
|
cy.task('dd:beforeEach', {
|
|
4
4
|
testName: Cypress.mocha.getRunner().suite.ctx.currentTest.fullTitle(),
|
|
5
5
|
testSuite: Cypress.mocha.getRootSuite().file
|
|
6
|
+
}).then(traceId => {
|
|
7
|
+
Cypress.env('traceId', traceId)
|
|
6
8
|
})
|
|
7
9
|
})
|
|
8
10
|
|
|
11
|
+
after(() => {
|
|
12
|
+
cy.window().then(win => {
|
|
13
|
+
win.dispatchEvent(new Event('beforeunload'))
|
|
14
|
+
})
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
|
|
9
18
|
afterEach(() => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
cy.window().then(win => {
|
|
20
|
+
const currentTest = Cypress.mocha.getRunner().suite.ctx.currentTest
|
|
21
|
+
const testInfo = {
|
|
22
|
+
testName: currentTest.fullTitle(),
|
|
23
|
+
testSuite: Cypress.mocha.getRootSuite().file,
|
|
24
|
+
state: currentTest.state,
|
|
25
|
+
error: currentTest.err,
|
|
26
|
+
}
|
|
27
|
+
if (win.DD_RUM) {
|
|
28
|
+
testInfo.isRUMActive = true
|
|
29
|
+
}
|
|
30
|
+
cy.task('dd:afterEach', testInfo)
|
|
16
31
|
})
|
|
17
32
|
})
|
|
@@ -238,8 +238,8 @@ module.exports = [
|
|
|
238
238
|
{
|
|
239
239
|
name: 'jest-environment-node',
|
|
240
240
|
versions: ['>=24.8.0'],
|
|
241
|
-
patch: function (NodeEnvironment, tracer) {
|
|
242
|
-
const testEnvironmentMetadata = getTestEnvironmentMetadata('jest')
|
|
241
|
+
patch: function (NodeEnvironment, tracer, config) {
|
|
242
|
+
const testEnvironmentMetadata = getTestEnvironmentMetadata('jest', config)
|
|
243
243
|
|
|
244
244
|
this.wrap(NodeEnvironment.prototype, 'teardown', createWrapTeardown(tracer, this))
|
|
245
245
|
|
|
@@ -257,8 +257,8 @@ module.exports = [
|
|
|
257
257
|
{
|
|
258
258
|
name: 'jest-environment-jsdom',
|
|
259
259
|
versions: ['>=24.8.0'],
|
|
260
|
-
patch: function (JsdomEnvironment, tracer) {
|
|
261
|
-
const testEnvironmentMetadata = getTestEnvironmentMetadata('jest')
|
|
260
|
+
patch: function (JsdomEnvironment, tracer, config) {
|
|
261
|
+
const testEnvironmentMetadata = getTestEnvironmentMetadata('jest', config)
|
|
262
262
|
|
|
263
263
|
this.wrap(JsdomEnvironment.prototype, 'teardown', createWrapTeardown(tracer, this))
|
|
264
264
|
|
|
@@ -169,8 +169,8 @@ module.exports = [
|
|
|
169
169
|
name: 'jest-jasmine2',
|
|
170
170
|
versions: ['>=24.8.0'],
|
|
171
171
|
file: 'build/jasmineAsyncInstall.js',
|
|
172
|
-
patch: function (jasmineAsyncInstallExport, tracer) {
|
|
173
|
-
const testEnvironmentMetadata = getTestEnvironmentMetadata('jest')
|
|
172
|
+
patch: function (jasmineAsyncInstallExport, tracer, config) {
|
|
173
|
+
const testEnvironmentMetadata = getTestEnvironmentMetadata('jest', config)
|
|
174
174
|
return this.wrapExport(
|
|
175
175
|
jasmineAsyncInstallExport.default,
|
|
176
176
|
createWrapJasmineAsyncInstall(tracer, this, testEnvironmentMetadata)(jasmineAsyncInstallExport.default)
|
|
@@ -239,8 +239,8 @@ module.exports = [
|
|
|
239
239
|
name: 'mocha',
|
|
240
240
|
versions: ['>=5.2.0'],
|
|
241
241
|
file: 'lib/runner.js',
|
|
242
|
-
patch (Runner, tracer) {
|
|
243
|
-
const testEnvironmentMetadata = getTestEnvironmentMetadata('mocha')
|
|
242
|
+
patch (Runner, tracer, config) {
|
|
243
|
+
const testEnvironmentMetadata = getTestEnvironmentMetadata('mocha', config)
|
|
244
244
|
const sourceRoot = process.cwd()
|
|
245
245
|
this.wrap(Runner.prototype, 'runTests', createWrapRunTests(tracer, testEnvironmentMetadata, sourceRoot))
|
|
246
246
|
this.wrap(Runner.prototype, 'runTest', createWrapRunTest(tracer, testEnvironmentMetadata, sourceRoot))
|
|
@@ -1,110 +1,58 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const Plugin = require('../../dd-trace/src/plugins/plugin')
|
|
4
|
+
const { storage } = require('../../datadog-core')
|
|
4
5
|
const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
return
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
class MySQLPlugin extends Plugin {
|
|
8
|
+
static get name () {
|
|
9
|
+
return 'mysql'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
constructor (...args) {
|
|
13
|
+
super(...args)
|
|
14
|
+
|
|
15
|
+
this.addSub('apm:mysql:query:start', ([sql, conf]) => {
|
|
16
|
+
const store = storage.getStore()
|
|
17
|
+
const childOf = store ? store.span : store
|
|
18
|
+
const span = this.tracer.startSpan('mysql.query', {
|
|
12
19
|
childOf,
|
|
13
20
|
tags: {
|
|
14
|
-
|
|
15
|
-
'service.name': config.service || `${tracer._service}-mysql`,
|
|
21
|
+
'service.name': this.config.service || `${this.tracer._service}-mysql`,
|
|
16
22
|
'span.type': 'sql',
|
|
17
23
|
'span.kind': 'client',
|
|
18
24
|
'db.type': 'mysql',
|
|
19
|
-
'db.user':
|
|
20
|
-
'out.host':
|
|
21
|
-
'out.port':
|
|
25
|
+
'db.user': conf.user,
|
|
26
|
+
'out.host': conf.host,
|
|
27
|
+
'out.port': conf.port,
|
|
28
|
+
'resource.name': sql
|
|
22
29
|
}
|
|
23
30
|
})
|
|
24
31
|
|
|
25
|
-
if (
|
|
26
|
-
span.setTag('db.name',
|
|
32
|
+
if (conf.database) {
|
|
33
|
+
span.setTag('db.name', conf.database)
|
|
27
34
|
}
|
|
28
35
|
|
|
29
|
-
analyticsSampler.sample(span, config.measured)
|
|
36
|
+
analyticsSampler.sample(span, this.config.measured)
|
|
37
|
+
this.enter(span, store)
|
|
38
|
+
})
|
|
30
39
|
|
|
31
|
-
|
|
40
|
+
this.addSub('apm:mysql:query:end', () => {
|
|
41
|
+
this.exit()
|
|
42
|
+
})
|
|
32
43
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (sequence._callback) {
|
|
38
|
-
sequence._callback = wrapCallback(tracer, span, childOf, sequence._callback)
|
|
39
|
-
} else {
|
|
40
|
-
sequence.on('end', () => {
|
|
41
|
-
span.finish()
|
|
42
|
-
})
|
|
44
|
+
this.addSub('apm:mysql:query:error', err => {
|
|
45
|
+
if (err) {
|
|
46
|
+
const span = storage.getStore().span
|
|
47
|
+
span.setTag('error', err)
|
|
43
48
|
}
|
|
49
|
+
})
|
|
44
50
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function createWrapGetConnection (tracer, config) {
|
|
51
|
-
return function wrapGetConnection (getConnection) {
|
|
52
|
-
return function getConnectionWithTrace (cb) {
|
|
53
|
-
const scope = tracer.scope()
|
|
54
|
-
|
|
55
|
-
arguments[0] = scope.bind(cb)
|
|
56
|
-
|
|
57
|
-
return scope.bind(getConnection).apply(this, arguments)
|
|
58
|
-
}
|
|
51
|
+
this.addSub('apm:mysql:query:async-end', () => {
|
|
52
|
+
const span = storage.getStore().span
|
|
53
|
+
span.finish()
|
|
54
|
+
})
|
|
59
55
|
}
|
|
60
56
|
}
|
|
61
57
|
|
|
62
|
-
|
|
63
|
-
return tracer.scope().bind((...args) => {
|
|
64
|
-
const err = args[0]
|
|
65
|
-
if (err) {
|
|
66
|
-
span.addTags({
|
|
67
|
-
'error.type': err.name,
|
|
68
|
-
'error.msg': err.message,
|
|
69
|
-
'error.stack': err.stack
|
|
70
|
-
})
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
span.finish()
|
|
74
|
-
|
|
75
|
-
done(...args)
|
|
76
|
-
}, parent)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function patchConnection (Connection, tracer, config) {
|
|
80
|
-
this.wrap(Connection.prototype, 'query', createWrapQuery(tracer, config))
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function unpatchConnection (Connection) {
|
|
84
|
-
this.unwrap(Connection.prototype, 'query')
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function patchPool (Pool, tracer, config) {
|
|
88
|
-
this.wrap(Pool.prototype, 'getConnection', createWrapGetConnection(tracer, config))
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function unpatchPool (Pool) {
|
|
92
|
-
this.unwrap(Pool.prototype, 'getConnection')
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
module.exports = [
|
|
96
|
-
{
|
|
97
|
-
name: 'mysql',
|
|
98
|
-
file: 'lib/Connection.js',
|
|
99
|
-
versions: ['>=2'],
|
|
100
|
-
patch: patchConnection,
|
|
101
|
-
unpatch: unpatchConnection
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
name: 'mysql',
|
|
105
|
-
file: 'lib/Pool.js',
|
|
106
|
-
versions: ['>=2'],
|
|
107
|
-
patch: patchPool,
|
|
108
|
-
unpatch: unpatchPool
|
|
109
|
-
}
|
|
110
|
-
]
|
|
58
|
+
module.exports = MySQLPlugin
|
|
@@ -1 +1 @@
|
|
|
1
|
-
module.exports = '2.0.0-
|
|
1
|
+
module.exports = '2.0.0-appsec-beta.5'
|
|
@@ -43,10 +43,10 @@ class SubscriptionManager {
|
|
|
43
43
|
const knownSubscriptions = new Set()
|
|
44
44
|
|
|
45
45
|
// TODO: possible optimization: collect matchedSubscriptions on the fly in Context#setValue
|
|
46
|
-
|
|
47
|
-
const matchedSubscriptions = this.addressToSubscriptions.get(
|
|
46
|
+
newAddresses.forEach((newAddress) => {
|
|
47
|
+
const matchedSubscriptions = this.addressToSubscriptions.get(newAddress)
|
|
48
48
|
|
|
49
|
-
if (matchedSubscriptions === undefined)
|
|
49
|
+
if (matchedSubscriptions === undefined) return
|
|
50
50
|
|
|
51
51
|
for (let j = 0; j < matchedSubscriptions.length; ++j) {
|
|
52
52
|
const subscription = matchedSubscriptions[j]
|
|
@@ -64,24 +64,24 @@ class SubscriptionManager {
|
|
|
64
64
|
subscriptions.add(subscription)
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
-
}
|
|
67
|
+
})
|
|
68
68
|
|
|
69
69
|
return { addresses, subscriptions }
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
dispatch (newAddresses, allAddresses, context) {
|
|
73
|
-
const
|
|
73
|
+
const matches = this.matchSubscriptions(newAddresses, allAddresses)
|
|
74
74
|
|
|
75
75
|
// TODO: possible optimization
|
|
76
|
-
// if
|
|
76
|
+
// check if matches.subscriptions is empty here instead of in runner.js
|
|
77
77
|
|
|
78
78
|
const params = {}
|
|
79
79
|
|
|
80
|
-
addresses.forEach((address) => {
|
|
80
|
+
matches.addresses.forEach((address) => {
|
|
81
81
|
params[address] = context.resolve(address)
|
|
82
82
|
})
|
|
83
83
|
|
|
84
|
-
return Runner.runSubscriptions(subscriptions, params)
|
|
84
|
+
return Runner.runSubscriptions(matches.subscriptions, params)
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -93,48 +93,37 @@ class Context {
|
|
|
93
93
|
constructor () {
|
|
94
94
|
this.store = new Map()
|
|
95
95
|
this.allAddresses = new Set()
|
|
96
|
-
this.newAddresses =
|
|
96
|
+
this.newAddresses = new Set()
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
clear () {
|
|
100
100
|
this.store = new Map()
|
|
101
101
|
this.allAddresses = new Set()
|
|
102
|
-
this.newAddresses =
|
|
102
|
+
this.newAddresses = new Set()
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
setValue (address, value) {
|
|
106
106
|
if (this.allAddresses.size >= MAX_CONTEXT_SIZE) return this
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
if (
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (!this.newAddresses.includes(address)) {
|
|
114
|
-
this.allAddresses.add(address)
|
|
115
|
-
this.newAddresses.push(address)
|
|
108
|
+
// cannot optimize for objects because they're pointers
|
|
109
|
+
if (typeof value !== 'object') {
|
|
110
|
+
const oldValue = this.store.get(address)
|
|
111
|
+
if (oldValue === value) return this
|
|
116
112
|
}
|
|
117
113
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
setMultipleValues (params) {
|
|
122
|
-
const addresses = Object.keys(params)
|
|
123
|
-
|
|
124
|
-
for (let i = 0; i < addresses.length; ++i) {
|
|
125
|
-
const address = addresses[i]
|
|
126
|
-
this.setValue(address, params[address])
|
|
127
|
-
}
|
|
114
|
+
this.store.set(address, value)
|
|
115
|
+
this.allAddresses.add(address)
|
|
116
|
+
this.newAddresses.add(address)
|
|
128
117
|
|
|
129
118
|
return this
|
|
130
119
|
}
|
|
131
120
|
|
|
132
121
|
dispatch () {
|
|
133
|
-
if (this.newAddresses.
|
|
122
|
+
if (this.newAddresses.size === 0) return []
|
|
134
123
|
|
|
135
124
|
const result = Context.manager.dispatch(this.newAddresses, this.allAddresses, this)
|
|
136
125
|
|
|
137
|
-
this.newAddresses
|
|
126
|
+
this.newAddresses.clear()
|
|
138
127
|
|
|
139
128
|
return result
|
|
140
129
|
}
|
|
@@ -27,13 +27,9 @@ function enable (config) {
|
|
|
27
27
|
incomingHttpRequestStart.subscribe(incomingHttpStartTranslator)
|
|
28
28
|
incomingHttpRequestEnd.subscribe(incomingHttpEndTranslator)
|
|
29
29
|
|
|
30
|
-
// add needed
|
|
31
|
-
Gateway.manager.addresses.add(addresses.HTTP_INCOMING_URL)
|
|
30
|
+
// add fields needed for HTTP context reporting
|
|
32
31
|
Gateway.manager.addresses.add(addresses.HTTP_INCOMING_HEADERS)
|
|
33
|
-
Gateway.manager.addresses.add(addresses.HTTP_INCOMING_METHOD)
|
|
34
32
|
Gateway.manager.addresses.add(addresses.HTTP_INCOMING_REMOTE_IP)
|
|
35
|
-
Gateway.manager.addresses.add(addresses.HTTP_INCOMING_REMOTE_PORT)
|
|
36
|
-
Gateway.manager.addresses.add(addresses.HTTP_INCOMING_RESPONSE_CODE)
|
|
37
33
|
Gateway.manager.addresses.add(addresses.HTTP_INCOMING_RESPONSE_HEADERS)
|
|
38
34
|
}
|
|
39
35
|
|
|
@@ -85,6 +81,7 @@ function incomingHttpEndTranslator (data) {
|
|
|
85
81
|
function disable () {
|
|
86
82
|
RuleManager.clearAllRules()
|
|
87
83
|
|
|
84
|
+
// Channel#unsubscribe() is undefined for non active channels
|
|
88
85
|
if (incomingHttpRequestStart.hasSubscribers) incomingHttpRequestStart.unsubscribe(incomingHttpStartTranslator)
|
|
89
86
|
if (incomingHttpRequestEnd.hasSubscribers) incomingHttpRequestEnd.unsubscribe(incomingHttpEndTranslator)
|
|
90
87
|
}
|
|
@@ -61,16 +61,16 @@ function filterHeaders (headers, passlist, prefix) {
|
|
|
61
61
|
const headerName = passlist[i]
|
|
62
62
|
|
|
63
63
|
if (headers[headerName]) {
|
|
64
|
-
result[`${prefix}${formatHeaderName(headerName)}`] = headers[headerName]
|
|
64
|
+
result[`${prefix}${formatHeaderName(headerName)}`] = headers[headerName] + ''
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
return result
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
// TODO: this can be precomputed at start time
|
|
71
72
|
function formatHeaderName (name) {
|
|
72
73
|
return name
|
|
73
|
-
.toString()
|
|
74
74
|
.trim()
|
|
75
75
|
.slice(0, 200)
|
|
76
76
|
.replace(/[^a-zA-Z0-9_\-:/]/g, '_')
|
|
@@ -24,6 +24,7 @@ const TEST_SUITE = 'test.suite'
|
|
|
24
24
|
const TEST_STATUS = 'test.status'
|
|
25
25
|
const TEST_PARAMETERS = 'test.parameters'
|
|
26
26
|
const TEST_SKIP_REASON = 'test.skip_reason'
|
|
27
|
+
const TEST_IS_RUM_ACTIVE = 'test.is_rum_active'
|
|
27
28
|
|
|
28
29
|
const ERROR_TYPE = 'error.type'
|
|
29
30
|
const ERROR_MESSAGE = 'error.msg'
|
|
@@ -43,6 +44,7 @@ module.exports = {
|
|
|
43
44
|
TEST_STATUS,
|
|
44
45
|
TEST_PARAMETERS,
|
|
45
46
|
TEST_SKIP_REASON,
|
|
47
|
+
TEST_IS_RUM_ACTIVE,
|
|
46
48
|
ERROR_TYPE,
|
|
47
49
|
ERROR_MESSAGE,
|
|
48
50
|
ERROR_STACK,
|
|
@@ -54,7 +56,7 @@ module.exports = {
|
|
|
54
56
|
getTestSuitePath
|
|
55
57
|
}
|
|
56
58
|
|
|
57
|
-
function getTestEnvironmentMetadata (testFramework) {
|
|
59
|
+
function getTestEnvironmentMetadata (testFramework, config) {
|
|
58
60
|
// TODO: eventually these will come from the tracer (generally available)
|
|
59
61
|
const ciMetadata = getCIMetadata()
|
|
60
62
|
const {
|
|
@@ -83,13 +85,17 @@ function getTestEnvironmentMetadata (testFramework) {
|
|
|
83
85
|
|
|
84
86
|
const runtimeAndOSMetadata = getRuntimeAndOSMetadata()
|
|
85
87
|
|
|
86
|
-
|
|
88
|
+
const metadata = {
|
|
87
89
|
[TEST_FRAMEWORK]: testFramework,
|
|
88
90
|
...gitMetadata,
|
|
89
91
|
...ciMetadata,
|
|
90
92
|
...userProvidedGitMetadata,
|
|
91
93
|
...runtimeAndOSMetadata
|
|
92
94
|
}
|
|
95
|
+
if (config && config.service) {
|
|
96
|
+
metadata['service.name'] = config.service
|
|
97
|
+
}
|
|
98
|
+
return metadata
|
|
93
99
|
}
|
|
94
100
|
|
|
95
101
|
function getTestParametersString (parametersByTestName, testName) {
|
|
@@ -55,7 +55,6 @@ class AgentExporter {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
export ({ profiles, start, end, tags }) {
|
|
58
|
-
const form = new FormData()
|
|
59
58
|
const types = Object.keys(profiles)
|
|
60
59
|
|
|
61
60
|
const fields = [
|
|
@@ -75,10 +74,6 @@ class AgentExporter {
|
|
|
75
74
|
...Object.entries(tags).map(([key, value]) => ['tags[]', `${key}:${value}`])
|
|
76
75
|
]
|
|
77
76
|
|
|
78
|
-
for (const [key, value] of fields) {
|
|
79
|
-
form.append(key, value)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
77
|
this._logger.debug(() => {
|
|
83
78
|
const body = fields.map(([key, value]) => ` ${key}: ${value}`).join('\n')
|
|
84
79
|
return `Building agent export report: ${'\n' + body}`
|
|
@@ -93,36 +88,14 @@ class AgentExporter {
|
|
|
93
88
|
return `Adding ${type} profile to agent export: ` + bytes
|
|
94
89
|
})
|
|
95
90
|
|
|
96
|
-
|
|
97
|
-
|
|
91
|
+
fields.push([`types[${index}]`, type])
|
|
92
|
+
fields.push([`data[${index}]`, buffer, {
|
|
98
93
|
filename: `${type}.pb.gz`,
|
|
99
94
|
contentType: 'application/octet-stream',
|
|
100
95
|
knownLength: buffer.length
|
|
101
|
-
})
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const options = {
|
|
105
|
-
method: 'POST',
|
|
106
|
-
path: '/profiling/v1/input',
|
|
107
|
-
headers: form.getHeaders()
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (containerId) {
|
|
111
|
-
options.headers['Datadog-Container-ID'] = containerId
|
|
96
|
+
}])
|
|
112
97
|
}
|
|
113
98
|
|
|
114
|
-
if (this._url.protocol === 'unix:') {
|
|
115
|
-
options.socketPath = this._url.pathname
|
|
116
|
-
} else {
|
|
117
|
-
options.protocol = this._url.protocol
|
|
118
|
-
options.hostname = this._url.hostname
|
|
119
|
-
options.port = this._url.port
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
this._logger.debug(() => {
|
|
123
|
-
return `Submitting agent report to: ${JSON.stringify(options)}`
|
|
124
|
-
})
|
|
125
|
-
|
|
126
99
|
return new Promise((resolve, reject) => {
|
|
127
100
|
const operation = retry.operation({
|
|
128
101
|
randomize: true,
|
|
@@ -131,8 +104,36 @@ class AgentExporter {
|
|
|
131
104
|
})
|
|
132
105
|
|
|
133
106
|
operation.attempt((attempt) => {
|
|
134
|
-
const
|
|
135
|
-
|
|
107
|
+
const form = new FormData()
|
|
108
|
+
|
|
109
|
+
for (const [key, value, options] of fields) {
|
|
110
|
+
form.append(key, value, options)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const options = {
|
|
114
|
+
method: 'POST',
|
|
115
|
+
path: '/profiling/v1/input',
|
|
116
|
+
headers: form.getHeaders(),
|
|
117
|
+
timeout: this._backoffTime * Math.pow(2, attempt)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (containerId) {
|
|
121
|
+
options.headers['Datadog-Container-ID'] = containerId
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (this._url.protocol === 'unix:') {
|
|
125
|
+
options.socketPath = this._url.pathname
|
|
126
|
+
} else {
|
|
127
|
+
options.protocol = this._url.protocol
|
|
128
|
+
options.hostname = this._url.hostname
|
|
129
|
+
options.port = this._url.port
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
this._logger.debug(() => {
|
|
133
|
+
return `Submitting profiler agent report attempt #${attempt} to: ${JSON.stringify(options)}`
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
sendRequest(options, form, (err, response) => {
|
|
136
137
|
if (operation.retry(err)) {
|
|
137
138
|
this._logger.error(`Error from the agent: ${err.message}`)
|
|
138
139
|
return
|