dd-trace 1.4.0 → 1.5.1

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.
@@ -18,6 +18,7 @@ require,multer,MIT,Copyright 2014 Hage Yaapa
18
18
  require,opentracing,MIT,Copyright 2016 Resonance Labs Inc
19
19
  require,path-to-regexp,MIT,Copyright 2014 Blake Embrey
20
20
  require,performance-now,MIT,Copyright 2013 Braveg1rl
21
+ require,retry,MIT,Copyright 2011 Tim Koschützki Felix Geisendörfer
21
22
  require,semver,ISC,Copyright Isaac Z. Schlueter and Contributors
22
23
  require,source-map,BSD-3-Clause,Copyright 2009-2011 Mozilla Foundation and contributors
23
24
  require,source-map-resolve,MIT,Copyright 2014-2020 Simon Lydell 2019 Jinxiang
@@ -51,7 +52,6 @@ dev,msgpack-lite,MIT,Copyright 2015 Yusuke Kawasaki
51
52
  dev,nock,MIT,Copyright 2017 Pedro Teixeira and other contributors
52
53
  dev,nyc,ISC,Copyright 2015 Contributors
53
54
  dev,proxyquire,MIT,Copyright 2013 Thorsten Lorenz
54
- dev,retry,MIT,Copyright 2011 Tim Koschützki Felix Geisendörfer
55
55
  dev,rimraf,ISC,Copyright Isaac Z. Schlueter and Contributors
56
56
  dev,sinon,BSD-3-Clause,Copyright 2010-2017 Christian Johansen
57
57
  dev,sinon-chai,WTFPL and BSD-2-Clause,Copyright 2004 Sam Hocevar 2012–2017 Domenic Denicola
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "1.4.0",
3
+ "version": "1.5.1",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -56,13 +56,13 @@
56
56
  "node": ">=12"
57
57
  },
58
58
  "dependencies": {
59
- "@datadog/native-metrics": "^1.0.0",
60
- "@datadog/pprof": "^0.1.3",
59
+ "@datadog/native-metrics": "^1.0.1",
60
+ "@datadog/pprof": "^0.3.0",
61
61
  "@datadog/sketches-js": "^1.0.4",
62
62
  "@types/node": "^10.12.18",
63
63
  "crypto-randomuuid": "^1.0.0",
64
64
  "form-data": "^3.0.0",
65
- "import-in-the-middle": "^1.1.1",
65
+ "import-in-the-middle": "^1.1.2",
66
66
  "koalas": "^1.0.2",
67
67
  "limiter": "^1.1.4",
68
68
  "lodash.kebabcase": "^4.1.1",
@@ -75,13 +75,14 @@
75
75
  "opentracing": ">=0.12.1",
76
76
  "path-to-regexp": "^0.1.2",
77
77
  "performance-now": "^2.1.0",
78
+ "retry": "^0.10.1",
78
79
  "semver": "^5.5.0",
79
80
  "source-map": "^0.7.3",
80
81
  "source-map-resolve": "^0.6.0"
81
82
  },
82
83
  "devDependencies": {
83
84
  "autocannon": "^4.5.2",
84
- "axios": "^0.21.1",
85
+ "axios": "^0.21.2",
85
86
  "benchmark": "^2.1.4",
86
87
  "body-parser": "^1.18.2",
87
88
  "chai": "^4.2.0",
@@ -110,7 +111,6 @@
110
111
  "nock": "^11.3.3",
111
112
  "nyc": "^15.1.0",
112
113
  "proxyquire": "^1.8.0",
113
- "retry": "^0.10.1",
114
114
  "rimraf": "^3.0.0",
115
115
  "sinon": "^11.1.2",
116
116
  "sinon-chai": "^3.7.0",
@@ -1,5 +1,3 @@
1
- const { relative } = require('path')
2
-
3
1
  const { SAMPLING_RULE_DECISION } = require('../../dd-trace/src/constants')
4
2
 
5
3
  const {
@@ -11,7 +9,8 @@ const {
11
9
  CI_APP_ORIGIN,
12
10
  ERROR_MESSAGE,
13
11
  getTestEnvironmentMetadata,
14
- finishAllTraceSpans
12
+ finishAllTraceSpans,
13
+ getTestSuitePath
15
14
  } = require('../../dd-trace/src/plugins/util/test')
16
15
 
17
16
  function setStatusFromResult (span, result, tag) {
@@ -42,11 +41,11 @@ function setStatusFromResultLatest (span, result, tag) {
42
41
  }
43
42
  }
44
43
 
45
- function createWrapRun (tracer, testEnvironmentMetadata, getTestSuiteName, setStatus) {
44
+ function createWrapRun (tracer, testEnvironmentMetadata, sourceRoot, setStatus) {
46
45
  return function wrapRun (run) {
47
46
  return function handleRun () {
48
47
  const testName = this.pickle.name
49
- const testSuite = getTestSuiteName(this.pickle.uri)
48
+ const testSuite = getTestSuitePath(this.pickle.uri, sourceRoot)
50
49
 
51
50
  const commonSpanTags = {
52
51
  [TEST_TYPE]: 'test',
@@ -105,14 +104,11 @@ module.exports = [
105
104
  patch (PickleRunner, tracer) {
106
105
  const testEnvironmentMetadata = getTestEnvironmentMetadata('cucumber')
107
106
  const sourceRoot = process.cwd()
108
- const getTestSuiteName = (pickleUri) => {
109
- return relative(sourceRoot, pickleUri)
110
- }
111
107
  const pl = PickleRunner.default
112
108
  this.wrap(
113
109
  pl.prototype,
114
110
  'run',
115
- createWrapRun(tracer, testEnvironmentMetadata, getTestSuiteName, setStatusFromResult)
111
+ createWrapRun(tracer, testEnvironmentMetadata, sourceRoot, setStatusFromResult)
116
112
  )
117
113
  const getResourceName = (testStep) => {
118
114
  return testStep.isHook ? 'hook' : testStep.pickleStep.text
@@ -132,14 +128,11 @@ module.exports = [
132
128
  patch (TestCaseRunner, tracer) {
133
129
  const testEnvironmentMetadata = getTestEnvironmentMetadata('cucumber')
134
130
  const sourceRoot = process.cwd()
135
- const getTestSuiteName = (pickleUri) => {
136
- return relative(sourceRoot, pickleUri)
137
- }
138
131
  const pl = TestCaseRunner.default
139
132
  this.wrap(
140
133
  pl.prototype,
141
134
  'run',
142
- createWrapRun(tracer, testEnvironmentMetadata, getTestSuiteName, setStatusFromResultLatest)
135
+ createWrapRun(tracer, testEnvironmentMetadata, sourceRoot, setStatusFromResultLatest)
143
136
  )
144
137
  const getResourceName = (testStep) => {
145
138
  return testStep.text
@@ -9,20 +9,17 @@ let tools
9
9
  function createWrapExecute (tracer, config, defaultFieldResolver) {
10
10
  return function wrapExecute (execute) {
11
11
  return function executeWithTrace () {
12
- const args = normalizeArgs(arguments)
12
+ const args = normalizeArgs(arguments, tracer, config, defaultFieldResolver)
13
13
  const schema = args.schema
14
14
  const document = args.document
15
15
  const source = document && document._datadog_source
16
- const fieldResolver = args.fieldResolver || defaultFieldResolver
17
- const contextValue = args.contextValue = args.contextValue || {}
16
+ const contextValue = args.contextValue
18
17
  const operation = getOperation(document, args.operationName)
19
18
 
20
19
  if (contextValue._datadog_graphql) {
21
20
  return execute.apply(this, arguments)
22
21
  }
23
22
 
24
- args.fieldResolver = wrapResolve(fieldResolver, tracer, config)
25
-
26
23
  if (schema) {
27
24
  wrapFields(schema._queryType, tracer, config)
28
25
  wrapFields(schema._mutationType, tracer, config)
@@ -32,7 +29,7 @@ function createWrapExecute (tracer, config, defaultFieldResolver) {
32
29
 
33
30
  contextValue._datadog_graphql = { source, span, fields: {} }
34
31
 
35
- return call(execute, span, this, [args], (err, res) => {
32
+ return call(execute, span, this, arguments, (err, res) => {
36
33
  finishResolvers(contextValue, config)
37
34
 
38
35
  setError(span, err || (res && res.errors && res.errors[0]))
@@ -212,10 +209,19 @@ function getField (contextValue, path) {
212
209
  return contextValue._datadog_graphql.fields[path.join('.')]
213
210
  }
214
211
 
215
- function normalizeArgs (args) {
216
- if (args.length === 1) {
217
- return args[0]
218
- }
212
+ function normalizeArgs (args, tracer, config, defaultFieldResolver) {
213
+ if (args.length !== 1) return normalizePositional(args, tracer, config, defaultFieldResolver)
214
+
215
+ args[0].contextValue = args[0].contextValue || {}
216
+ args[0].fieldResolver = wrapResolve(args[0].fieldResolver || defaultFieldResolver, tracer, config)
217
+
218
+ return args[0]
219
+ }
220
+
221
+ function normalizePositional (args, tracer, config, defaultFieldResolver) {
222
+ args[3] = args[3] || {} // contextValue
223
+ args[6] = wrapResolve(args[6] || defaultFieldResolver, tracer, config) // fieldResolver
224
+ args.length = Math.max(args.length, 7)
219
225
 
220
226
  return {
221
227
  schema: args[0],
@@ -48,7 +48,7 @@ function createWrapMakeClientConstructor (tracer, config) {
48
48
  function wrapPackageDefinition (tracer, config, def) {
49
49
  for (const name in def) {
50
50
  if (def[name].format) continue
51
- if (def[name].service) {
51
+ if (def[name].service && def[name].prototype) {
52
52
  wrapClientConstructor(tracer, config, def[name], def[name].service)
53
53
  } else {
54
54
  wrapPackageDefinition(tracer, config, def[name])
@@ -11,7 +11,8 @@ const {
11
11
  CI_APP_ORIGIN,
12
12
  getTestEnvironmentMetadata,
13
13
  getTestParametersString,
14
- finishAllTraceSpans
14
+ finishAllTraceSpans,
15
+ getTestSuitePath
15
16
  } = require('../../dd-trace/src/plugins/util/test')
16
17
  const {
17
18
  getFormattedJestTestParameters,
@@ -30,7 +31,7 @@ function wrapEnvironment (BaseEnvironment) {
30
31
  return class DatadogJestEnvironment extends BaseEnvironment {
31
32
  constructor (config, context) {
32
33
  super(config, context)
33
- this.testSuite = context.testPath.replace(`${config.rootDir}/`, '')
34
+ this.testSuite = getTestSuitePath(context.testPath, config.rootDir)
34
35
  this.testSpansByTestName = {}
35
36
  this.originalTestFnByTestName = {}
36
37
  }
@@ -7,7 +7,8 @@ const {
7
7
  TEST_STATUS,
8
8
  CI_APP_ORIGIN,
9
9
  getTestEnvironmentMetadata,
10
- finishAllTraceSpans
10
+ finishAllTraceSpans,
11
+ getTestSuitePath
11
12
  } = require('../../dd-trace/src/plugins/util/test')
12
13
  const { getTestSpanTags, setSuppressedErrors } = require('./util')
13
14
 
@@ -21,7 +22,7 @@ function createWrapIt (tracer, globalConfig, globalInput, testEnvironmentMetadat
21
22
 
22
23
  const { childOf, commonSpanTags } = getTestSpanTags(tracer, testEnvironmentMetadata)
23
24
 
24
- const testSuite = globalInput.jasmine.testPath.replace(`${globalConfig.rootDir}/`, '')
25
+ const testSuite = getTestSuitePath(globalInput.jasmine.testPath, globalConfig.rootDir)
25
26
 
26
27
  const newSpecFunction = tracer.wrap(
27
28
  'jest.test',
@@ -107,7 +108,7 @@ function createWrapItSkip (tracer, globalConfig, globalInput, testEnvironmentMet
107
108
  return function itSkipWithTrace () {
108
109
  const { childOf, commonSpanTags } = getTestSpanTags(tracer, testEnvironmentMetadata)
109
110
 
110
- const testSuite = globalInput.jasmine.testPath.replace(`${globalConfig.rootDir}/`, '')
111
+ const testSuite = getTestSuitePath(globalInput.jasmine.testPath, globalConfig.rootDir)
111
112
 
112
113
  const spec = it.apply(this, arguments)
113
114
 
@@ -13,22 +13,23 @@ const {
13
13
  getTestEnvironmentMetadata,
14
14
  getTestParametersString,
15
15
  finishAllTraceSpans,
16
- getTestParentSpan
16
+ getTestParentSpan,
17
+ getTestSuitePath
17
18
  } = require('../../dd-trace/src/plugins/util/test')
18
19
 
19
20
  function getTestSpanMetadata (tracer, test, sourceRoot) {
20
21
  const childOf = getTestParentSpan(tracer)
21
22
 
22
- const { file: testSuite } = test
23
+ const { file: testSuiteAbsolutePath } = test
23
24
  const fullTestName = test.fullTitle()
24
- const strippedTestSuite = testSuite ? testSuite.replace(`${sourceRoot}/`, '') : ''
25
+ const testSuite = getTestSuitePath(testSuiteAbsolutePath, sourceRoot)
25
26
 
26
27
  return {
27
28
  childOf,
28
- resource: `${strippedTestSuite}.${fullTestName}`,
29
+ resource: `${testSuite}.${fullTestName}`,
29
30
  [TEST_TYPE]: 'test',
30
31
  [TEST_NAME]: fullTestName,
31
- [TEST_SUITE]: strippedTestSuite,
32
+ [TEST_SUITE]: testSuite,
32
33
  [SAMPLING_RULE_DECISION]: 1,
33
34
  [SAMPLING_PRIORITY]: AUTO_KEEP
34
35
  }
@@ -37,6 +38,12 @@ function getTestSpanMetadata (tracer, test, sourceRoot) {
37
38
  function createWrapRunTest (tracer, testEnvironmentMetadata, sourceRoot) {
38
39
  return function wrapRunTest (runTest) {
39
40
  return async function runTestWithTrace () {
41
+ // `runTest` is rerun when retries are configured through `this.retries` and the test fails.
42
+ // This clause prevents rewrapping `this.test.fn` when it has already been wrapped.
43
+ if (this.test._currentRetry !== undefined && this.test._currentRetry !== 0) {
44
+ return runTest.apply(this, arguments)
45
+ }
46
+
40
47
  let specFunction = this.test.fn
41
48
  if (specFunction.length) {
42
49
  specFunction = promisify(specFunction)
@@ -1 +1 @@
1
- module.exports = '1.4.0'
1
+ module.exports = '1.5.1'
@@ -61,13 +61,15 @@ class AgentEncoder {
61
61
  this._encodeArrayPrefix(bytes, trace)
62
62
 
63
63
  for (const span of trace) {
64
+ bytes.reserve(1)
65
+
64
66
  if (span.type) {
65
- bytes.push(0x8c)
67
+ bytes.buffer[bytes.length++] = 0x8c
66
68
 
67
69
  this._encodeString(bytes, 'type')
68
70
  this._encodeString(bytes, span.type)
69
71
  } else {
70
- bytes.push(0x8b)
72
+ bytes.buffer[bytes.length++] = 0x8b
71
73
  }
72
74
 
73
75
  this._encodeString(bytes, 'trace_id')
@@ -107,52 +109,105 @@ class AgentEncoder {
107
109
 
108
110
  _encodeArrayPrefix (bytes, value) {
109
111
  const length = value.length
112
+ const buffer = bytes.buffer
113
+ const offset = bytes.length
114
+
115
+ bytes.reserve(5)
116
+ bytes.length += 5
110
117
 
111
- bytes.push(0xdd, length >> 24, length >> 16, length >> 8, length)
118
+ buffer[offset] = 0xdd
119
+ buffer[offset + 1] = length >> 24
120
+ buffer[offset + 2] = length >> 16
121
+ buffer[offset + 3] = length >> 8
122
+ buffer[offset + 4] = length
112
123
  }
113
124
 
114
125
  _encodeByte (bytes, value) {
115
- bytes.push(value)
126
+ const buffer = bytes.buffer
127
+
128
+ bytes.reserve(1)
129
+
130
+ buffer[bytes.length++] = value
116
131
  }
117
132
 
118
133
  _encodeId (bytes, id) {
134
+ const buffer = bytes.buffer
135
+ const offset = bytes.length
136
+
137
+ bytes.reserve(9)
138
+ bytes.length += 9
139
+
119
140
  id = id.toArray()
120
- bytes.push(0xcf, id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7])
141
+
142
+ buffer[offset] = 0xcf
143
+ buffer[offset + 1] = id[0]
144
+ buffer[offset + 2] = id[1]
145
+ buffer[offset + 3] = id[2]
146
+ buffer[offset + 4] = id[3]
147
+ buffer[offset + 5] = id[4]
148
+ buffer[offset + 6] = id[5]
149
+ buffer[offset + 7] = id[6]
150
+ buffer[offset + 8] = id[7]
121
151
  }
122
152
 
123
153
  _encodeInteger (bytes, value) {
124
- bytes.push(0xce, value >> 24, value >> 16, value >> 8, value)
154
+ const buffer = bytes.buffer
155
+ const offset = bytes.length
156
+
157
+ bytes.reserve(5)
158
+ bytes.length += 5
159
+
160
+ buffer[offset] = 0xce
161
+ buffer[offset + 1] = value >> 24
162
+ buffer[offset + 2] = value >> 16
163
+ buffer[offset + 3] = value >> 8
164
+ buffer[offset + 4] = value
125
165
  }
126
166
 
127
167
  _encodeLong (bytes, value) {
168
+ const buffer = bytes.buffer
169
+ const offset = bytes.length
128
170
  const hi = (value / Math.pow(2, 32)) >> 0
129
171
  const lo = value >>> 0
130
172
 
131
- bytes.push(0xcf, hi >> 24, hi >> 16, hi >> 8, hi, lo >> 24, lo >> 16, lo >> 8, lo)
173
+ bytes.reserve(9)
174
+ bytes.length += 9
175
+
176
+ buffer[offset] = 0xcf
177
+ buffer[offset + 1] = hi >> 24
178
+ buffer[offset + 2] = hi >> 16
179
+ buffer[offset + 3] = hi >> 8
180
+ buffer[offset + 4] = hi
181
+ buffer[offset + 5] = lo >> 24
182
+ buffer[offset + 6] = lo >> 16
183
+ buffer[offset + 7] = lo >> 8
184
+ buffer[offset + 8] = lo
132
185
  }
133
186
 
134
187
  _encodeMap (bytes, value) {
135
- const keys = []
136
- const allKeys = Object.keys(value)
188
+ const keys = Object.keys(value)
189
+ const buffer = bytes.buffer
190
+ const offset = bytes.length
137
191
 
138
- let key = ''
192
+ bytes.reserve(5)
193
+ bytes.length += 5
139
194
 
140
- for (let i = 0, l = allKeys.length; i < l; i++) {
141
- key = allKeys[i]
142
- if (typeof value[key] !== 'function') {
143
- keys.push(key)
144
- }
145
- }
195
+ let length = 0
146
196
 
147
- const length = keys.length
197
+ for (const key of keys) {
198
+ if (typeof value[key] !== 'string' && typeof value[key] !== 'number') return
148
199
 
149
- bytes.push(0xdf, length >> 24, length >> 16, length >> 8, length)
200
+ length++
150
201
 
151
- for (let i = 0; i < length; i++) {
152
- key = keys[i]
153
202
  this._encodeString(bytes, key)
154
203
  this._encodeValue(bytes, value[key])
155
204
  }
205
+
206
+ buffer[offset] = 0xdf
207
+ buffer[offset + 1] = length >> 24
208
+ buffer[offset + 2] = length >> 16
209
+ buffer[offset + 3] = length >> 8
210
+ buffer[offset + 4] = length
156
211
  }
157
212
 
158
213
  _encodeValue (bytes, value) {
@@ -179,15 +234,21 @@ class AgentEncoder {
179
234
  _encodeFloat (bytes, value) {
180
235
  float64Array[0] = value
181
236
 
182
- bytes.push(0xcb)
237
+ const buffer = bytes.buffer
238
+ const offset = bytes.length
239
+
240
+ bytes.reserve(9)
241
+ bytes.length += 9
242
+
243
+ buffer[offset] = 0xcb
183
244
 
184
245
  if (bigEndian) {
185
246
  for (let i = 0; i <= 7; i++) {
186
- bytes.push(uInt8Float64Array[i])
247
+ buffer[offset + i + 1] = uInt8Float64Array[i]
187
248
  }
188
249
  } else {
189
250
  for (let i = 7; i >= 0; i--) {
190
- bytes.push(uInt8Float64Array[i])
251
+ buffer[bytes.length - i - 1] = uInt8Float64Array[i]
191
252
  }
192
253
  }
193
254
  }
@@ -14,26 +14,24 @@ class Chunk {
14
14
  this._minSize = minSize
15
15
  }
16
16
 
17
- push (...bytes) {
18
- this._reserve(bytes.length)
19
-
20
- for (const byte of bytes) {
21
- this.buffer[this.length++] = byte
22
- }
23
- }
24
-
25
17
  write (value) {
26
18
  const length = Buffer.byteLength(value)
27
19
  const offset = this.length
28
20
 
29
21
  if (length < 0x20) { // fixstr
30
- this.push(length | 0xa0)
22
+ this.reserve(length + 1)
23
+ this.length += 1
24
+ this.buffer[offset] = length | 0xa0
31
25
  } else if (length < 0x100000000) { // str 32
32
- this.push(0xdb, length >> 24, length >> 16, length >> 8, length)
26
+ this.reserve(length + 5)
27
+ this.length += 5
28
+ this.buffer[offset] = 0xdb
29
+ this.buffer[offset + 1] = length >> 24
30
+ this.buffer[offset + 2] = length >> 16
31
+ this.buffer[offset + 3] = length >> 8
32
+ this.buffer[offset + 4] = length
33
33
  }
34
34
 
35
- this._reserve(length)
36
-
37
35
  this.length += this.buffer.utf8Write(value, this.length, length)
38
36
 
39
37
  return this.length - offset
@@ -44,13 +42,13 @@ class Chunk {
44
42
  }
45
43
 
46
44
  set (array) {
47
- this._reserve(array.length)
45
+ this.reserve(array.length)
48
46
 
49
47
  this.buffer.set(array, this.length)
50
48
  this.length += array.length
51
49
  }
52
50
 
53
- _reserve (size) {
51
+ reserve (size) {
54
52
  if (this.length + size > this.buffer.length) {
55
53
  this._resize(this._minSize * Math.ceil((this.length + size) / this._minSize))
56
54
  }
@@ -1,17 +1,17 @@
1
1
  'use strict'
2
2
 
3
- const { randomBytes } = require('crypto')
3
+ const { randomFillSync } = require('crypto')
4
4
 
5
5
  const UINT_MAX = 4294967296
6
6
 
7
+ const data = new Uint8Array(8 * 8192)
7
8
  const zeroId = new Uint8Array(8)
8
9
 
9
- // Cryptographically secure local seeds to mitigate Math.random() seed reuse.
10
- const seed = new Uint32Array(randomBytes(8).buffer)
11
-
12
10
  const map = Array.prototype.map
13
11
  const pad = byte => `${byte < 16 ? '0' : ''}${byte.toString(16)}`
14
12
 
13
+ let batch = 0
14
+
15
15
  // Internal representation of a trace or span ID.
16
16
  class Identifier {
17
17
  constructor (value, radix) {
@@ -36,7 +36,7 @@ class Identifier {
36
36
  if (this._buffer.length === 8) {
37
37
  return this._buffer
38
38
  }
39
- return this._buffer.subarray(-8)
39
+ return this._buffer.slice(-8)
40
40
  }
41
41
 
42
42
  toJSON () {
@@ -50,7 +50,7 @@ function createBuffer (value) {
50
50
  if (!value) return pseudoRandom()
51
51
 
52
52
  const size = Math.ceil(value.length / 2)
53
- const buffer = new Uint8Array(size)
53
+ const buffer = new Array(size)
54
54
 
55
55
  for (let i = 0; i < size; i++) {
56
56
  buffer[i] = parseInt(value.substring(i * 2, i * 2 + 2), 16)
@@ -61,7 +61,7 @@ function createBuffer (value) {
61
61
 
62
62
  // Convert a numerical string to a buffer using the specified radix.
63
63
  function fromString (str, raddix) {
64
- const buffer = new Uint8Array(8)
64
+ const buffer = new Array(8)
65
65
  const len = str.length
66
66
 
67
67
  let pos = 0
@@ -126,20 +126,24 @@ function toHexString (buffer) {
126
126
 
127
127
  // Simple pseudo-random 64-bit ID generator.
128
128
  function pseudoRandom () {
129
- const buffer = new Uint8Array(8)
130
-
131
- const hi = randomUInt32(seed[0]) & 0x7FFFFFFF // only positive int64
132
- const lo = randomUInt32(seed[1])
129
+ if (batch === 0) {
130
+ randomFillSync(data)
131
+ }
133
132
 
134
- writeUInt32BE(buffer, hi, 0)
135
- writeUInt32BE(buffer, lo, 4)
133
+ batch = (batch + 1) % 8192
136
134
 
137
- return buffer
138
- }
135
+ const offset = batch * 8
139
136
 
140
- // Generate a random unsigned 32-bit integer.
141
- function randomUInt32 (seed) {
142
- return seed ^ Math.floor(Math.random() * (0xFFFFFFFF + 1))
137
+ return [
138
+ data[offset] & 0x7F, // only positive int64,
139
+ data[offset + 1],
140
+ data[offset + 2],
141
+ data[offset + 3],
142
+ data[offset + 4],
143
+ data[offset + 5],
144
+ data[offset + 6],
145
+ data[offset + 7]
146
+ ]
143
147
  }
144
148
 
145
149
  // Read a buffer to unsigned integer bytes.
@@ -7,10 +7,10 @@ const Loader = require('./loader')
7
7
  const { isTrue } = require('./util')
8
8
  const plugins = require('./plugins')
9
9
 
10
- const disabldPlugins = process.env.DD_TRACE_DISABLED_PLUGINS
10
+ const disabledPlugins = process.env.DD_TRACE_DISABLED_PLUGINS
11
11
 
12
12
  const collectDisabledPlugins = () => {
13
- return new Set(disabldPlugins && disabldPlugins.split(',').map(plugin => plugin.trim()))
13
+ return new Set(disabledPlugins && disabledPlugins.split(',').map(plugin => plugin.trim()))
14
14
  }
15
15
 
16
16
  function cleanEnv (name) {
@@ -85,51 +85,26 @@ class Loader {
85
85
 
86
86
  const moduleVersion = getVersion(moduleBaseDir)
87
87
 
88
- Array.from(this._plugins.keys())
89
- .filter(plugin => [].concat(plugin).some(instrumentation =>
90
- filename(instrumentation) === moduleName && matchVersion(moduleVersion, instrumentation.versions)
91
- ))
92
- .forEach(plugin => this._validate(plugin, moduleName, moduleBaseDir, moduleVersion))
93
-
94
- this._plugins
95
- .forEach((meta, plugin) => {
96
- try {
97
- [].concat(plugin)
98
- .filter(instrumentation => moduleName === filename(instrumentation))
99
- .filter(instrumentation => matchVersion(moduleVersion, instrumentation.versions))
100
- .forEach(instrumentation => {
101
- const config = this._plugins.get(plugin).config
102
-
103
- if (config.enabled !== false) {
104
- moduleExports = this._instrumenter.patch(instrumentation, moduleExports, config) || moduleExports
105
- }
106
- })
107
- } catch (e) {
108
- log.error(e)
109
- this._instrumenter.unload(plugin)
110
- log.debug(`Error while trying to patch ${meta.name}. The plugin has been disabled.`)
111
- }
112
- })
113
-
114
- return moduleExports
115
- }
116
-
117
- _validate (plugin, moduleName, moduleBaseDir, moduleVersion) {
118
- const meta = this._plugins.get(plugin)
119
- const instrumentations = [].concat(plugin)
88
+ for (const [plugin, meta] of this._plugins) {
89
+ if (meta.config.enabled === false) {
90
+ continue
91
+ }
92
+ try {
93
+ for (const instrumentation of [].concat(plugin)) {
94
+ if (moduleName !== filename(instrumentation) || !matchVersion(moduleVersion, instrumentation.versions)) {
95
+ continue
96
+ }
120
97
 
121
- for (let i = 0; i < instrumentations.length; i++) {
122
- if (moduleName.indexOf(instrumentations[i].name) !== 0) continue
123
- if (instrumentations[i].versions && !matchVersion(moduleVersion, instrumentations[i].versions)) continue
124
- if (instrumentations[i].file && !exists(moduleBaseDir, instrumentations[i].file)) {
98
+ moduleExports = this._instrumenter.patch(instrumentation, moduleExports, meta.config) || moduleExports
99
+ }
100
+ } catch (e) {
101
+ log.error(e)
125
102
  this._instrumenter.unload(plugin)
126
- log.debug([
127
- `Plugin "${meta.name}" requires "${instrumentations[i].file}" which was not found.`,
128
- `The plugin was disabled.`
129
- ].join(' '))
130
- break
103
+ log.debug(`Error while trying to patch ${meta.name}. The plugin has been disabled.`)
131
104
  }
132
105
  }
106
+
107
+ return moduleExports
133
108
  }
134
109
  }
135
110
 
@@ -151,13 +126,4 @@ function filename (plugin) {
151
126
  return [plugin.name, plugin.file].filter(val => val).join('/')
152
127
  }
153
128
 
154
- function exists (basedir, file) {
155
- try {
156
- require.resolve(`${basedir}/${file}`)
157
- return true
158
- } catch (e) {
159
- return false
160
- }
161
- }
162
-
163
129
  module.exports = Loader
@@ -7,8 +7,10 @@ const SpanContext = require('./span_context')
7
7
  const constants = require('../constants')
8
8
  const id = require('../id')
9
9
  const tagger = require('../tagger')
10
+ const log = require('../log')
10
11
 
11
12
  const SAMPLE_RATE_METRIC_KEY = constants.SAMPLE_RATE_METRIC_KEY
13
+ const { DD_TRACE_EXPERIMENTAL_STATE_TRACKING } = process.env
12
14
 
13
15
  class DatadogSpan extends Span {
14
16
  constructor (tracer, processor, sampler, prioritySampler, fields, debug) {
@@ -117,6 +119,12 @@ class DatadogSpan extends Span {
117
119
  return
118
120
  }
119
121
 
122
+ if (DD_TRACE_EXPERIMENTAL_STATE_TRACKING === 'true') {
123
+ if (!this._spanContext._tags['service.name']) {
124
+ log.error(`Finishing invalid span: ${this}`)
125
+ }
126
+ }
127
+
120
128
  finishTime = parseFloat(finishTime) || this._getTime()
121
129
 
122
130
  this._duration = finishTime - this._startTime
@@ -42,7 +42,7 @@ const redis = {
42
42
  function formatCommand (command, args) {
43
43
  command = command.toUpperCase()
44
44
 
45
- if (!args) return command
45
+ if (!args || command === 'AUTH') return command
46
46
 
47
47
  for (let i = 0, l = args.length; i < l; i++) {
48
48
  if (typeof args[i] === 'function') continue
@@ -1,3 +1,5 @@
1
+ const path = require('path')
2
+
1
3
  const { getGitMetadata } = require('./git')
2
4
  const { getCIMetadata } = require('./ci')
3
5
  const { getRuntimeAndOSMetadata } = require('./env')
@@ -42,7 +44,8 @@ module.exports = {
42
44
  getTestEnvironmentMetadata,
43
45
  getTestParametersString,
44
46
  finishAllTraceSpans,
45
- getTestParentSpan
47
+ getTestParentSpan,
48
+ getTestSuitePath
46
49
  }
47
50
 
48
51
  function getTestEnvironmentMetadata (testFramework) {
@@ -110,3 +113,16 @@ function getTestParentSpan (tracer) {
110
113
  'x-datadog-sampled': 1
111
114
  })
112
115
  }
116
+ /**
117
+ * We want to make sure that test suites are reported the same way for
118
+ * every OS, so we replace `path.sep` by `/`
119
+ */
120
+ function getTestSuitePath (testSuiteAbsolutePath, sourceRoot) {
121
+ if (!testSuiteAbsolutePath) {
122
+ return sourceRoot
123
+ }
124
+ const testSuitePath = testSuiteAbsolutePath === sourceRoot
125
+ ? testSuiteAbsolutePath : path.relative(sourceRoot, testSuiteAbsolutePath)
126
+
127
+ return testSuitePath.replace(path.sep, '/')
128
+ }
@@ -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
@@ -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
- function sendRequest (options, body, callback) {
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 (body) req.write(body)
24
+ if (form) form.pipe(req)
21
25
  req.end()
22
26
  }
23
27
 
@@ -97,13 +101,16 @@ class AgentExporter {
97
101
  })
98
102
  }
99
103
 
100
- const body = form.getBuffer()
101
104
  const options = {
102
105
  method: 'POST',
103
106
  path: '/profiling/v1/input',
104
107
  headers: form.getHeaders()
105
108
  }
106
109
 
110
+ if (containerId) {
111
+ options.headers['Datadog-Container-ID'] = containerId
112
+ }
113
+
107
114
  if (this._url.protocol === 'unix:') {
108
115
  options.socketPath = this._url.pathname
109
116
  } else {
@@ -125,7 +132,7 @@ class AgentExporter {
125
132
 
126
133
  operation.attempt((attempt) => {
127
134
  const timeout = Math.pow(this._backoffTime, attempt)
128
- sendRequest({ ...options, timeout }, body, (err, response) => {
135
+ sendRequest({ ...options, timeout }, form, (err, response) => {
129
136
  if (operation.retry(err)) {
130
137
  this._logger.error(`Error from the agent: ${err.message}`)
131
138
  return
@@ -25,9 +25,7 @@ class NativeCpuProfiler {
25
25
 
26
26
  profile () {
27
27
  if (!this._stop) return
28
- const profile = this._stop()
29
- this._record()
30
- return profile
28
+ return this._stop(true)
31
29
  }
32
30
 
33
31
  encode (profile) {
@@ -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 explicitely enabled
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,6 +1,9 @@
1
1
  const log = require('./log')
2
2
  const format = require('./format')
3
3
 
4
+ const startedSpans = new WeakSet()
5
+ const finishedSpans = new WeakSet()
6
+
4
7
  class SpanProcessor {
5
8
  constructor (exporter, prioritySampler) {
6
9
  this._exporter = exporter
@@ -27,9 +30,80 @@ class SpanProcessor {
27
30
  }
28
31
 
29
32
  _erase (trace) {
30
- trace.finished.forEach(span => {
33
+ if (process.env.DD_TRACE_EXPERIMENTAL_STATE_TRACKING === 'true') {
34
+ const started = new Set()
35
+ const startedIds = new Set()
36
+ const finished = new Set()
37
+ const finishedIds = new Set()
38
+
39
+ for (const span of trace.finished) {
40
+ const context = span.context()
41
+ const id = context.toSpanId()
42
+
43
+ if (finished.has(span)) {
44
+ log.error(`Span was already finished in the same trace: ${span}`)
45
+ } else {
46
+ finished.add(span)
47
+
48
+ if (finishedIds.has(id)) {
49
+ log.error(`Another span with the same ID was already finished in the same trace: ${span}`)
50
+ } else {
51
+ finishedIds.add(id)
52
+ }
53
+
54
+ if (context._trace !== trace) {
55
+ log.error(`A span was finished in the wrong trace: ${span}.`)
56
+ }
57
+
58
+ if (finishedSpans.has(span)) {
59
+ log.error(`Span was already finished in a different trace: ${span}`)
60
+ } else {
61
+ finishedSpans.add(span)
62
+ }
63
+ }
64
+ }
65
+
66
+ for (const span of trace.started) {
67
+ const context = span.context()
68
+ const id = context.toSpanId()
69
+
70
+ if (started.has(span)) {
71
+ log.error(`Span was already started in the same trace: ${span}`)
72
+ } else {
73
+ started.add(span)
74
+
75
+ if (startedIds.has(id)) {
76
+ log.error(`Another span with the same ID was already started in the same trace: ${span}`)
77
+ } else {
78
+ startedIds.add(id)
79
+ }
80
+
81
+ if (context._trace !== trace) {
82
+ log.error(`A span was started in the wrong trace: ${span}.`)
83
+ }
84
+
85
+ if (startedSpans.has(span)) {
86
+ log.error(`Span was already started in a different trace: ${span}`)
87
+ } else {
88
+ startedSpans.add(span)
89
+ }
90
+ }
91
+
92
+ if (!finished.has(span)) {
93
+ log.error(`Span started in one trace but was finished in another trace: ${span}`)
94
+ }
95
+ }
96
+
97
+ for (const span of trace.finished) {
98
+ if (!started.has(span)) {
99
+ log.error(`Span finished in one trace but was started in another trace: ${span}`)
100
+ }
101
+ }
102
+ }
103
+
104
+ for (const span of trace.finished) {
31
105
  span.context()._tags = {}
32
- })
106
+ }
33
107
 
34
108
  trace.started = []
35
109
  trace.finished = []
package/NOTICE DELETED
@@ -1,4 +0,0 @@
1
- Datadog dd-trace-js
2
- Copyright 2016-Present Datadog, Inc.
3
-
4
- This product includes software developed at Datadog, Inc. (https://www.datadoghq.com/).