dd-trace 1.5.0 → 1.7.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/ci/cypress/plugin.js +3 -0
- package/ci/cypress/support.js +1 -0
- package/ci/init.js +13 -0
- package/ci/jest/env.js +14 -0
- package/index.d.ts +5 -5
- package/package.json +6 -5
- package/packages/datadog-plugin-cucumber/src/index.js +6 -4
- package/packages/datadog-plugin-cypress/src/plugin.js +17 -5
- package/packages/datadog-plugin-cypress/src/support.js +21 -6
- package/packages/datadog-plugin-graphql/src/index.js +16 -10
- package/packages/datadog-plugin-grpc/src/client.js +1 -1
- package/packages/datadog-plugin-jest/src/jest-environment.js +9 -5
- package/packages/datadog-plugin-jest/src/jest-jasmine2.js +13 -4
- package/packages/datadog-plugin-mocha/src/index.js +12 -3
- package/packages/datadog-plugin-redis/src/index.js +31 -1
- package/packages/dd-trace/lib/version.js +1 -1
- package/packages/dd-trace/src/constants.js +6 -1
- package/packages/dd-trace/src/encode/0.4.js +84 -23
- package/packages/dd-trace/src/encode/chunk.js +12 -14
- package/packages/dd-trace/src/format.js +12 -0
- package/packages/dd-trace/src/id.js +22 -18
- package/packages/dd-trace/src/instrumenter.js +2 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +34 -0
- package/packages/dd-trace/src/opentracing/span_context.js +2 -1
- package/packages/dd-trace/src/{.DS_Store → plugins/.DS_Store} +0 -0
- package/packages/dd-trace/src/plugins/util/ci.js +22 -9
- package/packages/dd-trace/src/plugins/util/git.js +11 -25
- package/packages/dd-trace/src/plugins/util/redis.js +0 -2
- package/packages/dd-trace/src/plugins/util/test.js +22 -4
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +72 -0
- package/packages/dd-trace/src/plugins/util/web.js +1 -1
- package/packages/dd-trace/src/priority_sampler.js +71 -19
- package/packages/dd-trace/src/profiling/exporters/agent.js +41 -33
- package/packages/dd-trace/src/profiling/profilers/cpu.js +1 -3
- package/packages/dd-trace/src/proxy.js +1 -1
- package/packages/dd-trace/src/plugins/util/ci-app-spec.json +0 -35
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const {
|
|
2
|
+
GIT_COMMIT_SHA,
|
|
3
|
+
GIT_BRANCH,
|
|
4
|
+
GIT_REPOSITORY_URL,
|
|
5
|
+
GIT_TAG,
|
|
6
|
+
GIT_COMMIT_MESSAGE,
|
|
7
|
+
GIT_COMMIT_COMMITTER_DATE,
|
|
8
|
+
GIT_COMMIT_COMMITTER_EMAIL,
|
|
9
|
+
GIT_COMMIT_COMMITTER_NAME,
|
|
10
|
+
GIT_COMMIT_AUTHOR_DATE,
|
|
11
|
+
GIT_COMMIT_AUTHOR_EMAIL,
|
|
12
|
+
GIT_COMMIT_AUTHOR_NAME
|
|
13
|
+
} = require('./tags')
|
|
14
|
+
|
|
15
|
+
const { normalizeRef } = require('./ci')
|
|
16
|
+
|
|
17
|
+
function removeEmptyValues (tags) {
|
|
18
|
+
return Object.keys(tags).reduce((filteredTags, tag) => {
|
|
19
|
+
if (!tags[tag]) {
|
|
20
|
+
return filteredTags
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
...filteredTags,
|
|
24
|
+
[tag]: tags[tag]
|
|
25
|
+
}
|
|
26
|
+
}, {})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function getUserProviderGitMetadata () {
|
|
30
|
+
const {
|
|
31
|
+
DD_GIT_COMMIT_SHA,
|
|
32
|
+
DD_GIT_BRANCH,
|
|
33
|
+
DD_GIT_REPOSITORY_URL,
|
|
34
|
+
DD_GIT_TAG,
|
|
35
|
+
DD_GIT_COMMIT_MESSAGE,
|
|
36
|
+
DD_GIT_COMMIT_COMMITTER_NAME,
|
|
37
|
+
DD_GIT_COMMIT_COMMITTER_EMAIL,
|
|
38
|
+
DD_GIT_COMMIT_COMMITTER_DATE,
|
|
39
|
+
DD_GIT_COMMIT_AUTHOR_NAME,
|
|
40
|
+
DD_GIT_COMMIT_AUTHOR_EMAIL,
|
|
41
|
+
DD_GIT_COMMIT_AUTHOR_DATE
|
|
42
|
+
} = process.env
|
|
43
|
+
|
|
44
|
+
let branch = normalizeRef(DD_GIT_BRANCH)
|
|
45
|
+
let tag = normalizeRef(DD_GIT_TAG)
|
|
46
|
+
|
|
47
|
+
if (DD_GIT_TAG) {
|
|
48
|
+
branch = undefined
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// if DD_GIT_BRANCH is a tag, we associate its value to TAG instead of BRANCH
|
|
52
|
+
if ((DD_GIT_BRANCH || '').includes('origin/tags') || (DD_GIT_BRANCH || '').includes('refs/heads/tags')) {
|
|
53
|
+
branch = undefined
|
|
54
|
+
tag = normalizeRef(DD_GIT_BRANCH)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return removeEmptyValues({
|
|
58
|
+
[GIT_COMMIT_SHA]: DD_GIT_COMMIT_SHA,
|
|
59
|
+
[GIT_BRANCH]: branch,
|
|
60
|
+
[GIT_REPOSITORY_URL]: DD_GIT_REPOSITORY_URL,
|
|
61
|
+
[GIT_TAG]: tag,
|
|
62
|
+
[GIT_COMMIT_MESSAGE]: DD_GIT_COMMIT_MESSAGE,
|
|
63
|
+
[GIT_COMMIT_COMMITTER_NAME]: DD_GIT_COMMIT_COMMITTER_NAME,
|
|
64
|
+
[GIT_COMMIT_COMMITTER_DATE]: DD_GIT_COMMIT_COMMITTER_DATE,
|
|
65
|
+
[GIT_COMMIT_COMMITTER_EMAIL]: DD_GIT_COMMIT_COMMITTER_EMAIL,
|
|
66
|
+
[GIT_COMMIT_AUTHOR_NAME]: DD_GIT_COMMIT_AUTHOR_NAME,
|
|
67
|
+
[GIT_COMMIT_AUTHOR_EMAIL]: DD_GIT_COMMIT_AUTHOR_EMAIL,
|
|
68
|
+
[GIT_COMMIT_AUTHOR_DATE]: DD_GIT_COMMIT_AUTHOR_DATE
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = { getUserProviderGitMetadata }
|
|
@@ -181,7 +181,7 @@ const web = {
|
|
|
181
181
|
|
|
182
182
|
// Extract the parent span from the headers and start a new span as its child
|
|
183
183
|
startChildSpan (tracer, name, headers) {
|
|
184
|
-
const childOf = tracer.extract(FORMAT_HTTP_HEADERS, headers)
|
|
184
|
+
const childOf = tracer.scope().active() || tracer.extract(FORMAT_HTTP_HEADERS, headers)
|
|
185
185
|
const span = tracer.startSpan(name, { childOf })
|
|
186
186
|
|
|
187
187
|
return span
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const coalesce = require('koalas')
|
|
3
4
|
const RateLimiter = require('./rate_limiter')
|
|
4
5
|
const Sampler = require('./sampler')
|
|
5
6
|
const ext = require('../../../ext')
|
|
6
7
|
const { setSamplingRules } = require('./startup-log')
|
|
7
8
|
|
|
8
9
|
const {
|
|
10
|
+
SAMPLING_MECHANISM_DEFAULT,
|
|
11
|
+
SAMPLING_MECHANISM_AGENT,
|
|
12
|
+
SAMPLING_MECHANISM_RULE,
|
|
13
|
+
SAMPLING_MECHANISM_MANUAL,
|
|
9
14
|
SAMPLING_RULE_DECISION,
|
|
10
15
|
SAMPLING_LIMIT_DECISION,
|
|
11
|
-
SAMPLING_AGENT_DECISION
|
|
16
|
+
SAMPLING_AGENT_DECISION,
|
|
17
|
+
UPSTREAM_SERVICES_KEY
|
|
12
18
|
} = require('./constants')
|
|
13
19
|
|
|
14
20
|
const SERVICE_NAME = ext.tags.SERVICE_NAME
|
|
@@ -21,6 +27,9 @@ const AUTO_KEEP = ext.priority.AUTO_KEEP
|
|
|
21
27
|
const USER_KEEP = ext.priority.USER_KEEP
|
|
22
28
|
const DEFAULT_KEY = 'service:,env:'
|
|
23
29
|
|
|
30
|
+
const defaultSampler = new Sampler(AUTO_KEEP)
|
|
31
|
+
const serviceNames = new Map()
|
|
32
|
+
|
|
24
33
|
class PrioritySampler {
|
|
25
34
|
constructor (env, { sampleRate, rateLimit = 100, rules = [] } = {}) {
|
|
26
35
|
this._env = env
|
|
@@ -33,12 +42,8 @@ class PrioritySampler {
|
|
|
33
42
|
}
|
|
34
43
|
|
|
35
44
|
isSampled (span) {
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return rule
|
|
40
|
-
? this._isSampledByRule(context, rule) && this._isSampledByRateLimit(context)
|
|
41
|
-
: this._isSampledByAgent(context)
|
|
45
|
+
const priority = this._getPriorityFromAuto(span)
|
|
46
|
+
return priority === USER_KEEP || priority === AUTO_KEEP
|
|
42
47
|
}
|
|
43
48
|
|
|
44
49
|
sample (span, auto = true) {
|
|
@@ -50,16 +55,18 @@ class PrioritySampler {
|
|
|
50
55
|
if (context._sampling.priority !== undefined) return
|
|
51
56
|
if (!root) return // noop span
|
|
52
57
|
|
|
53
|
-
const tag = this.
|
|
58
|
+
const tag = this._getPriorityFromTags(context._tags)
|
|
54
59
|
|
|
55
60
|
if (this.validate(tag)) {
|
|
56
61
|
context._sampling.priority = tag
|
|
62
|
+
context._sampling.mechanism = SAMPLING_MECHANISM_MANUAL
|
|
63
|
+
} else if (auto) {
|
|
64
|
+
context._sampling.priority = this._getPriorityFromAuto(root)
|
|
65
|
+
} else {
|
|
57
66
|
return
|
|
58
67
|
}
|
|
59
68
|
|
|
60
|
-
|
|
61
|
-
context._sampling.priority = this.isSampled(root) ? AUTO_KEEP : AUTO_REJECT
|
|
62
|
-
}
|
|
69
|
+
this._addUpstreamService(root)
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
update (rates) {
|
|
@@ -72,7 +79,7 @@ class PrioritySampler {
|
|
|
72
79
|
samplers[key] = sampler
|
|
73
80
|
}
|
|
74
81
|
|
|
75
|
-
samplers[DEFAULT_KEY] = samplers[DEFAULT_KEY] ||
|
|
82
|
+
samplers[DEFAULT_KEY] = samplers[DEFAULT_KEY] || defaultSampler
|
|
76
83
|
|
|
77
84
|
this._samplers = samplers
|
|
78
85
|
}
|
|
@@ -93,7 +100,16 @@ class PrioritySampler {
|
|
|
93
100
|
return typeof span.context === 'function' ? span.context() : span
|
|
94
101
|
}
|
|
95
102
|
|
|
96
|
-
|
|
103
|
+
_getPriorityFromAuto (span) {
|
|
104
|
+
const context = this._getContext(span)
|
|
105
|
+
const rule = this._findRule(context)
|
|
106
|
+
|
|
107
|
+
return rule
|
|
108
|
+
? this._getPriorityByRule(context, rule)
|
|
109
|
+
: this._getPriorityByAgent(context)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
_getPriorityFromTags (tags) {
|
|
97
113
|
if (tags.hasOwnProperty(MANUAL_KEEP) && tags[MANUAL_KEEP] !== false) {
|
|
98
114
|
return USER_KEEP
|
|
99
115
|
} else if (tags.hasOwnProperty(MANUAL_DROP) && tags[MANUAL_DROP] !== false) {
|
|
@@ -109,10 +125,11 @@ class PrioritySampler {
|
|
|
109
125
|
}
|
|
110
126
|
}
|
|
111
127
|
|
|
112
|
-
|
|
128
|
+
_getPriorityByRule (context, rule) {
|
|
113
129
|
context._trace[SAMPLING_RULE_DECISION] = rule.sampleRate
|
|
130
|
+
context._sampling.mechanism = SAMPLING_MECHANISM_RULE
|
|
114
131
|
|
|
115
|
-
return rule.sampler.isSampled(context)
|
|
132
|
+
return rule.sampler.isSampled(context) && this._isSampledByRateLimit(context) ? USER_KEEP : USER_REJECT
|
|
116
133
|
}
|
|
117
134
|
|
|
118
135
|
_isSampledByRateLimit (context) {
|
|
@@ -123,13 +140,48 @@ class PrioritySampler {
|
|
|
123
140
|
return allowed
|
|
124
141
|
}
|
|
125
142
|
|
|
126
|
-
|
|
143
|
+
_getPriorityByAgent (context) {
|
|
127
144
|
const key = `service:${context._tags[SERVICE_NAME]},env:${this._env}`
|
|
128
145
|
const sampler = this._samplers[key] || this._samplers[DEFAULT_KEY]
|
|
129
146
|
|
|
130
147
|
context._trace[SAMPLING_AGENT_DECISION] = sampler.rate()
|
|
131
148
|
|
|
132
|
-
|
|
149
|
+
if (sampler === defaultSampler) {
|
|
150
|
+
context._sampling.mechanism = SAMPLING_MECHANISM_DEFAULT
|
|
151
|
+
} else {
|
|
152
|
+
context._sampling.mechanism = SAMPLING_MECHANISM_AGENT
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return sampler.isSampled(context) ? AUTO_KEEP : AUTO_REJECT
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
_addUpstreamService (span) {
|
|
159
|
+
const context = span.context()
|
|
160
|
+
const trace = context._trace
|
|
161
|
+
const service = this._toBase64(context._tags['service.name'])
|
|
162
|
+
const priority = context._sampling.priority
|
|
163
|
+
const mechanism = context._sampling.mechanism
|
|
164
|
+
const rate = Math.ceil(coalesce(
|
|
165
|
+
context._trace[SAMPLING_RULE_DECISION],
|
|
166
|
+
context._trace[SAMPLING_AGENT_DECISION]
|
|
167
|
+
) * 10000) / 10000
|
|
168
|
+
const group = `${service}|${priority}|${mechanism}|${rate}`
|
|
169
|
+
const groups = trace.tags[UPSTREAM_SERVICES_KEY]
|
|
170
|
+
? `${trace.tags[UPSTREAM_SERVICES_KEY]};${group}`
|
|
171
|
+
: group
|
|
172
|
+
|
|
173
|
+
trace.tags[UPSTREAM_SERVICES_KEY] = groups
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
_toBase64 (serviceName) {
|
|
177
|
+
let encoded = serviceNames.get(serviceName)
|
|
178
|
+
|
|
179
|
+
if (!encoded) {
|
|
180
|
+
encoded = Buffer.from(serviceName).toString('base64')
|
|
181
|
+
serviceNames.set(serviceName, encoded)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return encoded
|
|
133
185
|
}
|
|
134
186
|
|
|
135
187
|
_normalizeRules (rules, sampleRate) {
|
|
@@ -151,9 +203,9 @@ class PrioritySampler {
|
|
|
151
203
|
const service = context._tags['service.name']
|
|
152
204
|
|
|
153
205
|
if (rule.name instanceof RegExp && !rule.name.test(name)) return false
|
|
154
|
-
if (rule.name && rule.name !== name) return false
|
|
206
|
+
if (typeof rule.name === 'string' && rule.name !== name) return false
|
|
155
207
|
if (rule.service instanceof RegExp && !rule.service.test(service)) return false
|
|
156
|
-
if (rule.service && rule.service !== service) return false
|
|
208
|
+
if (typeof rule.service === 'string' && rule.service !== service) return false
|
|
157
209
|
|
|
158
210
|
return true
|
|
159
211
|
}
|
|
@@ -4,9 +4,13 @@ const retry = require('retry')
|
|
|
4
4
|
const { request } = require('http')
|
|
5
5
|
const FormData = require('form-data')
|
|
6
6
|
|
|
7
|
+
// TODO: avoid using dd-trace internals. Make this a separate module?
|
|
8
|
+
const docker = require('../../exporters/agent/docker')
|
|
7
9
|
const version = require('../../../lib/version')
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
const containerId = docker.id()
|
|
12
|
+
|
|
13
|
+
function sendRequest (options, form, callback) {
|
|
10
14
|
const req = request(options, res => {
|
|
11
15
|
if (res.statusCode >= 400) {
|
|
12
16
|
const error = new Error(`HTTP Error ${res.statusCode}`)
|
|
@@ -17,7 +21,7 @@ function sendRequest (options, body, callback) {
|
|
|
17
21
|
}
|
|
18
22
|
})
|
|
19
23
|
req.on('error', callback)
|
|
20
|
-
if (
|
|
24
|
+
if (form) form.pipe(req)
|
|
21
25
|
req.end()
|
|
22
26
|
}
|
|
23
27
|
|
|
@@ -36,7 +40,7 @@ function computeRetries (uploadTimeout) {
|
|
|
36
40
|
tries++
|
|
37
41
|
uploadTimeout /= 2
|
|
38
42
|
}
|
|
39
|
-
return [tries, uploadTimeout]
|
|
43
|
+
return [tries, Math.floor(uploadTimeout)]
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
class AgentExporter {
|
|
@@ -51,7 +55,6 @@ class AgentExporter {
|
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
export ({ profiles, start, end, tags }) {
|
|
54
|
-
const form = new FormData()
|
|
55
58
|
const types = Object.keys(profiles)
|
|
56
59
|
|
|
57
60
|
const fields = [
|
|
@@ -71,10 +74,6 @@ class AgentExporter {
|
|
|
71
74
|
...Object.entries(tags).map(([key, value]) => ['tags[]', `${key}:${value}`])
|
|
72
75
|
]
|
|
73
76
|
|
|
74
|
-
for (const [key, value] of fields) {
|
|
75
|
-
form.append(key, value)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
77
|
this._logger.debug(() => {
|
|
79
78
|
const body = fields.map(([key, value]) => ` ${key}: ${value}`).join('\n')
|
|
80
79
|
return `Building agent export report: ${'\n' + body}`
|
|
@@ -89,33 +88,14 @@ class AgentExporter {
|
|
|
89
88
|
return `Adding ${type} profile to agent export: ` + bytes
|
|
90
89
|
})
|
|
91
90
|
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
fields.push([`types[${index}]`, type])
|
|
92
|
+
fields.push([`data[${index}]`, buffer, {
|
|
94
93
|
filename: `${type}.pb.gz`,
|
|
95
94
|
contentType: 'application/octet-stream',
|
|
96
95
|
knownLength: buffer.length
|
|
97
|
-
})
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const body = form.getBuffer()
|
|
101
|
-
const options = {
|
|
102
|
-
method: 'POST',
|
|
103
|
-
path: '/profiling/v1/input',
|
|
104
|
-
headers: form.getHeaders()
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (this._url.protocol === 'unix:') {
|
|
108
|
-
options.socketPath = this._url.pathname
|
|
109
|
-
} else {
|
|
110
|
-
options.protocol = this._url.protocol
|
|
111
|
-
options.hostname = this._url.hostname
|
|
112
|
-
options.port = this._url.port
|
|
96
|
+
}])
|
|
113
97
|
}
|
|
114
98
|
|
|
115
|
-
this._logger.debug(() => {
|
|
116
|
-
return `Submitting agent report to: ${JSON.stringify(options)}`
|
|
117
|
-
})
|
|
118
|
-
|
|
119
99
|
return new Promise((resolve, reject) => {
|
|
120
100
|
const operation = retry.operation({
|
|
121
101
|
randomize: true,
|
|
@@ -124,8 +104,36 @@ class AgentExporter {
|
|
|
124
104
|
})
|
|
125
105
|
|
|
126
106
|
operation.attempt((attempt) => {
|
|
127
|
-
const
|
|
128
|
-
|
|
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) => {
|
|
129
137
|
if (operation.retry(err)) {
|
|
130
138
|
this._logger.error(`Error from the agent: ${err.message}`)
|
|
131
139
|
return
|
|
@@ -152,4 +160,4 @@ class AgentExporter {
|
|
|
152
160
|
}
|
|
153
161
|
}
|
|
154
162
|
|
|
155
|
-
module.exports = { AgentExporter }
|
|
163
|
+
module.exports = { AgentExporter, computeRetries }
|
|
@@ -46,7 +46,7 @@ class Tracer extends BaseTracer {
|
|
|
46
46
|
metrics.start(config)
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
// dirty require for now so zero appsec code is executed unless
|
|
49
|
+
// dirty require for now so zero appsec code is executed unless explicitly enabled
|
|
50
50
|
if (config.appsec.enabled) {
|
|
51
51
|
require('./appsec').enable(config)
|
|
52
52
|
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
"test.type",
|
|
3
|
-
"test.status",
|
|
4
|
-
"test.framework",
|
|
5
|
-
"test.suite",
|
|
6
|
-
"test.name",
|
|
7
|
-
"test.skip_reason",
|
|
8
|
-
"ci.pipeline.id",
|
|
9
|
-
"ci.pipeline.name",
|
|
10
|
-
"ci.pipeline.number",
|
|
11
|
-
"ci.pipeline.url",
|
|
12
|
-
"ci.provider.name",
|
|
13
|
-
"ci.workspace_path",
|
|
14
|
-
"git.repository_url",
|
|
15
|
-
"ci.job.url",
|
|
16
|
-
"ci.job.name",
|
|
17
|
-
"ci.stage.name",
|
|
18
|
-
"git.commit.sha",
|
|
19
|
-
"git.branch",
|
|
20
|
-
"git.tag",
|
|
21
|
-
"git.commit.message",
|
|
22
|
-
"git.commit.committer.date",
|
|
23
|
-
"git.commit.committer.email",
|
|
24
|
-
"git.commit.committer.name",
|
|
25
|
-
"git.commit.author.date",
|
|
26
|
-
"git.commit.author.email",
|
|
27
|
-
"git.commit.author.name",
|
|
28
|
-
"os.platform",
|
|
29
|
-
"os.version",
|
|
30
|
-
"os.architecture",
|
|
31
|
-
"runtime.name",
|
|
32
|
-
"runtime.version",
|
|
33
|
-
"test.parameters",
|
|
34
|
-
"_dd.origin"
|
|
35
|
-
]
|