dd-trace 4.7.0 → 4.8.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.
Files changed (55) hide show
  1. package/README.md +2 -2
  2. package/package.json +3 -3
  3. package/packages/datadog-core/src/storage/async_resource.js +4 -0
  4. package/packages/datadog-instrumentations/src/aws-sdk.js +6 -2
  5. package/packages/datadog-instrumentations/src/couchbase.js +4 -4
  6. package/packages/datadog-instrumentations/src/fetch.js +24 -21
  7. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  8. package/packages/datadog-instrumentations/src/http/client.js +41 -32
  9. package/packages/datadog-instrumentations/src/http2/client.js +1 -0
  10. package/packages/datadog-instrumentations/src/jest.js +32 -4
  11. package/packages/datadog-plugin-fetch/src/index.js +7 -13
  12. package/packages/datadog-plugin-graphql/src/execute.js +6 -4
  13. package/packages/datadog-plugin-http/src/client.js +25 -20
  14. package/packages/datadog-plugin-jest/src/index.js +8 -3
  15. package/packages/datadog-plugin-openai/src/index.js +39 -16
  16. package/packages/datadog-plugin-openai/src/services.js +13 -9
  17. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +3 -1
  18. package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +45 -0
  19. package/packages/dd-trace/src/appsec/iast/analyzers/index.js +3 -3
  20. package/packages/dd-trace/src/appsec/iast/analyzers/missing-header-analyzer.js +66 -0
  21. package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +25 -8
  22. package/packages/dd-trace/src/appsec/iast/analyzers/xcontenttype-header-missing-analyzer.js +19 -0
  23. package/packages/dd-trace/src/appsec/iast/index.js +5 -2
  24. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +4 -2
  25. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +17 -1
  26. package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +1 -0
  27. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +5 -1
  28. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +3 -1
  29. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
  30. package/packages/dd-trace/src/config.js +26 -10
  31. package/packages/dd-trace/src/external-logger/src/index.js +9 -1
  32. package/packages/dd-trace/src/external-logger/test/index.spec.js +1 -1
  33. package/packages/dd-trace/src/format.js +1 -1
  34. package/packages/dd-trace/src/lambda/handler.js +8 -1
  35. package/packages/dd-trace/src/opentelemetry/span.js +3 -1
  36. package/packages/dd-trace/src/opentracing/span_context.js +2 -1
  37. package/packages/dd-trace/src/plugins/index.js +1 -0
  38. package/packages/dd-trace/src/plugins/util/ci.js +2 -1
  39. package/packages/dd-trace/src/plugins/util/web.js +1 -0
  40. package/packages/dd-trace/src/profiling/config.js +8 -5
  41. package/packages/dd-trace/src/profiling/exporters/agent.js +4 -1
  42. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  43. package/packages/dd-trace/src/profiling/profilers/wall.js +144 -4
  44. package/packages/dd-trace/src/service-naming/index.js +2 -2
  45. package/packages/dd-trace/src/service-naming/schemas/v0/graphql.js +12 -0
  46. package/packages/dd-trace/src/service-naming/schemas/v0/index.js +2 -1
  47. package/packages/dd-trace/src/service-naming/schemas/v1/graphql.js +12 -0
  48. package/packages/dd-trace/src/service-naming/schemas/v1/index.js +2 -1
  49. package/packages/dd-trace/src/span_processor.js +0 -4
  50. package/packages/dd-trace/src/span_sampler.js +1 -1
  51. package/packages/dd-trace/src/telemetry/dependencies.js +24 -12
  52. package/packages/dd-trace/src/telemetry/metrics.js +11 -1
  53. package/packages/diagnostics_channel/src/index.js +1 -1
  54. package/scripts/install_plugin_modules.js +1 -0
  55. package/scripts/version.js +0 -66
package/README.md CHANGED
@@ -143,8 +143,8 @@ $ yarn leak:plugins
143
143
 
144
144
  ### Linting
145
145
 
146
- We use [ESLint](https://eslint.org) to make sure that new code is
147
- conform to our coding standards.
146
+ We use [ESLint](https://eslint.org) to make sure that new code
147
+ conforms to our coding standards.
148
148
 
149
149
  To run the linter, use:
150
150
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "4.7.0",
3
+ "version": "4.8.1",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -68,9 +68,9 @@
68
68
  "dependencies": {
69
69
  "@datadog/native-appsec": "^3.2.0",
70
70
  "@datadog/native-iast-rewriter": "2.0.1",
71
- "@datadog/native-iast-taint-tracking": "^1.5.0",
71
+ "@datadog/native-iast-taint-tracking": "1.5.0",
72
72
  "@datadog/native-metrics": "^2.0.0",
73
- "@datadog/pprof": "3.0.0",
73
+ "@datadog/pprof": "3.1.0",
74
74
  "@datadog/sketches-js": "^2.1.0",
75
75
  "@opentelemetry/api": "^1.0.0",
76
76
  "@opentelemetry/core": "^1.14.0",
@@ -5,6 +5,7 @@ const { channel } = require('../../../diagnostics_channel')
5
5
 
6
6
  const beforeCh = channel('dd-trace:storage:before')
7
7
  const afterCh = channel('dd-trace:storage:after')
8
+ const enterCh = channel('dd-trace:storage:enter')
8
9
 
9
10
  let PrivateSymbol = Symbol
10
11
  function makePrivateSymbol () {
@@ -52,6 +53,7 @@ class AsyncResourceStorage {
52
53
  const resource = this._executionAsyncResource()
53
54
 
54
55
  resource[this._ddResourceStore] = store
56
+ enterCh.publish()
55
57
  }
56
58
 
57
59
  run (store, callback, ...args) {
@@ -61,11 +63,13 @@ class AsyncResourceStorage {
61
63
  const oldStore = resource[this._ddResourceStore]
62
64
 
63
65
  resource[this._ddResourceStore] = store
66
+ enterCh.publish()
64
67
 
65
68
  try {
66
69
  return callback(...args)
67
70
  } finally {
68
71
  resource[this._ddResourceStore] = oldStore
72
+ enterCh.publish()
69
73
  }
70
74
  }
71
75
 
@@ -177,7 +177,6 @@ addHook({ name: '@aws-sdk/smithy-client', versions: ['>=3'] }, smithy => {
177
177
  })
178
178
 
179
179
  addHook({ name: 'aws-sdk', versions: ['>=2.3.0'] }, AWS => {
180
- shimmer.wrap(AWS.Request.prototype, 'promise', wrapRequest)
181
180
  shimmer.wrap(AWS.config, 'setPromisesDependency', setPromisesDependency => {
182
181
  return function wrappedSetPromisesDependency (dep) {
183
182
  const result = setPromisesDependency.apply(this, arguments)
@@ -188,9 +187,14 @@ addHook({ name: 'aws-sdk', versions: ['>=2.3.0'] }, AWS => {
188
187
  return AWS
189
188
  })
190
189
 
190
+ addHook({ name: 'aws-sdk', file: 'lib/core.js', versions: ['>=2.3.0'] }, AWS => {
191
+ shimmer.wrap(AWS.Request.prototype, 'promise', wrapRequest)
192
+ return AWS
193
+ })
194
+
191
195
  // <2.1.35 has breaking changes for instrumentation
192
196
  // https://github.com/aws/aws-sdk-js/pull/629
193
- addHook({ name: 'aws-sdk', versions: ['>=2.1.35'] }, AWS => {
197
+ addHook({ name: 'aws-sdk', file: 'lib/core.js', versions: ['>=2.1.35'] }, AWS => {
194
198
  shimmer.wrap(AWS.Request.prototype, 'send', wrapRequest)
195
199
  return AWS
196
200
  })
@@ -160,7 +160,7 @@ function wrapV3Query (query) {
160
160
  }
161
161
 
162
162
  // semver >=2 <3
163
- addHook({ name: 'couchbase', file: 'lib/bucket.js', versions: ['^2.6.5'] }, Bucket => {
163
+ addHook({ name: 'couchbase', file: 'lib/bucket.js', versions: ['^2.6.12'] }, Bucket => {
164
164
  const startCh = channel('apm:couchbase:query:start')
165
165
  const finishCh = channel('apm:couchbase:query:finish')
166
166
  const errorCh = channel('apm:couchbase:query:error')
@@ -208,7 +208,7 @@ addHook({ name: 'couchbase', file: 'lib/bucket.js', versions: ['^2.6.5'] }, Buck
208
208
  return Bucket
209
209
  })
210
210
 
211
- addHook({ name: 'couchbase', file: 'lib/cluster.js', versions: ['^2.6.5'] }, Cluster => {
211
+ addHook({ name: 'couchbase', file: 'lib/cluster.js', versions: ['^2.6.12'] }, Cluster => {
212
212
  Cluster.prototype._maybeInvoke = wrapMaybeInvoke(Cluster.prototype._maybeInvoke)
213
213
  Cluster.prototype.query = wrapQuery(Cluster.prototype.query)
214
214
 
@@ -217,7 +217,7 @@ addHook({ name: 'couchbase', file: 'lib/cluster.js', versions: ['^2.6.5'] }, Clu
217
217
 
218
218
  // semver >=3 <3.2.0
219
219
 
220
- addHook({ name: 'couchbase', file: 'lib/collection.js', versions: ['>=3.0.0 <3.2.0'] }, Collection => {
220
+ addHook({ name: 'couchbase', file: 'lib/collection.js', versions: ['^3.0.7', '^3.1.3'] }, Collection => {
221
221
  wrapAllNames(['upsert', 'insert', 'replace'], name => {
222
222
  shimmer.wrap(Collection.prototype, name, wrapWithName(name))
223
223
  })
@@ -225,7 +225,7 @@ addHook({ name: 'couchbase', file: 'lib/collection.js', versions: ['>=3.0.0 <3.2
225
225
  return Collection
226
226
  })
227
227
 
228
- addHook({ name: 'couchbase', file: 'lib/cluster.js', versions: ['>=3.0.0 <3.2.0'] }, Cluster => {
228
+ addHook({ name: 'couchbase', file: 'lib/cluster.js', versions: ['^3.0.7', '^3.1.3'] }, Cluster => {
229
229
  shimmer.wrap(Cluster.prototype, 'query', wrapV3Query)
230
230
  return Cluster
231
231
  })
@@ -17,29 +17,32 @@ function wrapFetch (fetch, Request) {
17
17
  const headers = req.headers
18
18
  const message = { req, headers }
19
19
 
20
- startChannel.publish(message)
21
-
22
- // Request object is read-only so we need new objects to change headers.
23
- arguments[0] = message.req
24
- arguments[1] = { headers: message.headers }
25
-
26
- return fetch.apply(this, arguments)
27
- .then(
28
- res => {
29
- finishChannel.publish({ req, res })
30
-
31
- return res
32
- },
33
- err => {
34
- if (err.name !== 'AbortError') {
35
- errorChannel.publish(err)
36
- }
20
+ return startChannel.runStores(message, () => {
21
+ // Request object is read-only so we need new objects to change headers.
22
+ arguments[0] = message.req
23
+ arguments[1] = { headers: message.headers }
24
+
25
+ return fetch.apply(this, arguments)
26
+ .then(
27
+ res => {
28
+ message.res = res
29
+
30
+ finishChannel.publish(message)
37
31
 
38
- finishChannel.publish({ req })
32
+ return res
33
+ },
34
+ err => {
35
+ if (err.name !== 'AbortError') {
36
+ message.error = err
37
+ errorChannel.publish(message)
38
+ }
39
39
 
40
- throw err
41
- }
42
- )
40
+ finishChannel.publish(message)
41
+
42
+ throw err
43
+ }
44
+ )
45
+ })
43
46
  }
44
47
  }
45
48
 
@@ -10,6 +10,7 @@ module.exports = {
10
10
  '@grpc/grpc-js': () => require('../grpc'),
11
11
  '@hapi/hapi': () => require('../hapi'),
12
12
  '@jest/core': () => require('../jest'),
13
+ '@jest/transform': () => require('../jest'),
13
14
  '@jest/reporters': () => require('../jest'),
14
15
  '@koa/router': () => require('../koa'),
15
16
  '@node-redis/client': () => require('../redis'),
@@ -3,18 +3,16 @@
3
3
  /* eslint-disable no-fallthrough */
4
4
 
5
5
  const url = require('url')
6
- const {
7
- channel,
8
- addHook,
9
- AsyncResource
10
- } = require('../helpers/instrument')
6
+ const { channel, addHook } = require('../helpers/instrument')
11
7
  const shimmer = require('../../../datadog-shimmer')
12
8
 
13
9
  const log = require('../../../dd-trace/src/log')
14
10
 
15
- const startClientCh = channel('apm:http:client:request:start')
16
- const finishClientCh = channel('apm:http:client:request:finish')
17
- const errorClientCh = channel('apm:http:client:request:error')
11
+ const startChannel = channel('apm:http:client:request:start')
12
+ const finishChannel = channel('apm:http:client:request:finish')
13
+ const endChannel = channel('apm:http:client:request:end')
14
+ const asyncStartChannel = channel('apm:http:client:request:asyncStart')
15
+ const errorChannel = channel('apm:http:client:request:error')
18
16
 
19
17
  addHook({ name: 'https' }, hookFn)
20
18
 
@@ -32,7 +30,7 @@ function patch (http, methodName) {
32
30
 
33
31
  function instrumentRequest (request) {
34
32
  return function () {
35
- if (!startClientCh.hasSubscribers) {
33
+ if (!startChannel.hasSubscribers) {
36
34
  return request.apply(this, arguments)
37
35
  }
38
36
 
@@ -45,57 +43,68 @@ function patch (http, methodName) {
45
43
  return request.apply(this, arguments)
46
44
  }
47
45
 
48
- const callbackResource = new AsyncResource('bound-anonymous-fn')
49
- const asyncResource = new AsyncResource('bound-anonymous-fn')
50
-
51
- return asyncResource.runInAsyncScope(() => {
52
- startClientCh.publish({ args, http })
46
+ const ctx = { args, http }
53
47
 
48
+ return startChannel.runStores(ctx, () => {
54
49
  let finished = false
55
50
  let callback = args.callback
56
51
 
57
52
  if (callback) {
58
- callback = callbackResource.bind(callback)
53
+ callback = function () {
54
+ return asyncStartChannel.runStores(ctx, () => {
55
+ return args.callback.apply(this, arguments)
56
+ })
57
+ }
59
58
  }
60
59
 
61
60
  const options = args.options
62
- const req = request.call(this, options, callback)
63
- const emit = req.emit
64
-
65
- const finish = (req, res) => {
61
+ const finish = () => {
66
62
  if (!finished) {
67
63
  finished = true
68
- finishClientCh.publish({ req, res })
64
+ finishChannel.publish(ctx)
69
65
  }
70
66
  }
71
67
 
72
- req.emit = function (eventName, arg) {
73
- asyncResource.runInAsyncScope(() => {
68
+ try {
69
+ const req = request.call(this, options, callback)
70
+ const emit = req.emit
71
+
72
+ ctx.req = req
73
+
74
+ req.emit = function (eventName, arg) {
74
75
  switch (eventName) {
75
76
  case 'response': {
76
77
  const res = arg
77
- const listener = asyncResource.bind(() => finish(req, res))
78
- res.on('end', listener)
79
- res.on('error', listener)
78
+ ctx.res = res
79
+ res.on('end', finish)
80
+ res.on('error', finish)
80
81
  break
81
82
  }
82
83
  case 'connect':
83
84
  case 'upgrade':
84
- finish(req, arg)
85
+ ctx.res = arg
86
+ finish()
85
87
  break
86
88
  case 'error':
87
89
  case 'timeout':
88
- errorClientCh.publish(arg)
90
+ ctx.error = arg
91
+ errorChannel.publish(ctx)
89
92
  case 'abort': // deprecated and replaced by `close` in node 17
90
93
  case 'close':
91
- finish(req)
94
+ finish()
92
95
  }
93
- })
94
96
 
95
- return emit.apply(this, arguments)
96
- }
97
+ return emit.apply(this, arguments)
98
+ }
97
99
 
98
- return req
100
+ return req
101
+ } catch (e) {
102
+ ctx.error = e
103
+ errorChannel.publish(ctx)
104
+ throw e
105
+ } finally {
106
+ endChannel.publish(ctx)
107
+ }
99
108
  })
100
109
  }
101
110
  }
@@ -42,6 +42,7 @@ function createWrapRequest (authority, options) {
42
42
  } catch (e) {
43
43
  ctx.error = e
44
44
  errorChannel.publish(ctx)
45
+ throw e
45
46
  } finally {
46
47
  endChannel.publish(ctx)
47
48
  }
@@ -129,8 +129,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
129
129
  suite: this.testSuite,
130
130
  runner: 'jest-circus',
131
131
  testParameters,
132
- frameworkVersion: jestVersion,
133
- testStartLine: getTestLineStart(event.test.asyncError, this.testSuite)
132
+ frameworkVersion: jestVersion
134
133
  })
135
134
  originalTestFns.set(event.test, event.test.fn)
136
135
  event.test.fn = asyncResource.bind(event.test.fn)
@@ -145,7 +144,10 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
145
144
  const formattedError = formatJestError(event.test.errors[0])
146
145
  testErrCh.publish(formattedError)
147
146
  }
148
- testRunFinishCh.publish(status)
147
+ testRunFinishCh.publish({
148
+ status,
149
+ testStartLine: getTestLineStart(event.test.asyncError, this.testSuite)
150
+ })
149
151
  // restore in case it is retried
150
152
  event.test.fn = originalTestFns.get(event.test)
151
153
  })
@@ -408,6 +410,32 @@ function jestConfigSyncWrapper (jestConfig) {
408
410
  return jestConfig
409
411
  }
410
412
 
413
+ addHook({
414
+ name: '@jest/transform',
415
+ versions: ['>=24.8.0'],
416
+ file: 'build/ScriptTransformer.js'
417
+ }, transformPackage => {
418
+ const originalCreateScriptTransformer = transformPackage.createScriptTransformer
419
+
420
+ transformPackage.createScriptTransformer = async function (config) {
421
+ const { testEnvironmentOptions, ...restOfConfig } = config
422
+ const {
423
+ _ddTestModuleId,
424
+ _ddTestSessionId,
425
+ _ddTestCommand,
426
+ ...restOfTestEnvironmentOptions
427
+ } = testEnvironmentOptions
428
+
429
+ restOfConfig.testEnvironmentOptions = restOfTestEnvironmentOptions
430
+
431
+ arguments[0] = restOfConfig
432
+
433
+ return originalCreateScriptTransformer.apply(this, arguments)
434
+ }
435
+
436
+ return transformPackage
437
+ })
438
+
411
439
  /**
412
440
  * Hook to remove the test paths (test suite) that are part of `skippableSuites`
413
441
  */
@@ -471,7 +499,7 @@ function jasmineAsyncInstallWraper (jasmineAsyncInstallExport, jestVersion) {
471
499
  const formattedError = formatJestError(spec.result.failedExpectations[0].error)
472
500
  testErrCh.publish(formattedError)
473
501
  }
474
- testRunFinishCh.publish(specStatusToTestStatus[spec.result.status])
502
+ testRunFinishCh.publish({ status: specStatusToTestStatus[spec.result.status] })
475
503
  onComplete.apply(this, arguments)
476
504
  })
477
505
  arguments[0] = callback
@@ -1,35 +1,29 @@
1
1
  'use strict'
2
2
 
3
3
  const HttpClientPlugin = require('../../datadog-plugin-http/src/client')
4
- const { HTTP_HEADERS } = require('../../../ext/formats')
5
4
 
6
5
  class FetchPlugin extends HttpClientPlugin {
7
6
  static get id () { return 'fetch' }
7
+ static get prefix () { return `apm:fetch:request` }
8
8
 
9
9
  addTraceSub (eventName, handler) {
10
10
  this.addSub(`apm:${this.constructor.id}:${this.operation}:${eventName}`, handler)
11
11
  }
12
12
 
13
- start (message) {
13
+ bindStart (message) {
14
14
  const req = message.req
15
15
  const options = new URL(req.url)
16
16
  const headers = options.headers = Object.fromEntries(req.headers.entries())
17
17
 
18
- const args = { options }
18
+ options.method = req.method
19
19
 
20
- super.start({ args })
20
+ message.args = { options }
21
21
 
22
- message.req = new globalThis.Request(req, { headers })
23
- }
22
+ const store = super.bindStart(message)
24
23
 
25
- _inject (span, headers) {
26
- const carrier = {}
27
-
28
- this.tracer.inject(span, HTTP_HEADERS, carrier)
24
+ message.req = new globalThis.Request(req, { headers })
29
25
 
30
- for (const name in carrier) {
31
- headers.append(name, carrier[name])
32
- }
26
+ return store
33
27
  }
34
28
  }
35
29
 
@@ -7,6 +7,8 @@ let tools
7
7
  class GraphQLExecutePlugin extends TracingPlugin {
8
8
  static get id () { return 'graphql' }
9
9
  static get operation () { return 'execute' }
10
+ static get type () { return 'graphql' }
11
+ static get kind () { return 'server' }
10
12
 
11
13
  start ({ operation, args, docSource }) {
12
14
  const type = operation && operation.operation
@@ -14,11 +16,11 @@ class GraphQLExecutePlugin extends TracingPlugin {
14
16
  const document = args.document
15
17
  const source = this.config.source && document && docSource
16
18
 
17
- const span = this.startSpan('graphql.execute', {
18
- service: this.config.service,
19
+ const span = this.startSpan(this.operationName(), {
20
+ service: this.config.service || this.serviceName(),
19
21
  resource: getSignature(document, name, type, this.config.signature),
20
- kind: 'server',
21
- type: 'graphql',
22
+ kind: this.constructor.kind,
23
+ type: this.constructor.type,
22
24
  meta: {
23
25
  'graphql.operation.type': type,
24
26
  'graphql.operation.name': name,
@@ -16,15 +16,11 @@ const HTTP_REQUEST_HEADERS = tags.HTTP_REQUEST_HEADERS
16
16
  const HTTP_RESPONSE_HEADERS = tags.HTTP_RESPONSE_HEADERS
17
17
 
18
18
  class HttpClientPlugin extends ClientPlugin {
19
- static get id () {
20
- return 'http'
21
- }
22
-
23
- addTraceSub (eventName, handler) {
24
- this.addSub(`apm:${this.constructor.id}:client:${this.operation}:${eventName}`, handler)
25
- }
19
+ static get id () { return 'http' }
20
+ static get prefix () { return `apm:http:client:request` }
26
21
 
27
- start ({ args, http = {} }) {
22
+ bindStart (message) {
23
+ const { args, http = {} } = message
28
24
  const store = storage.getStore()
29
25
  const options = args.options
30
26
  const agent = options.agent || options._defaultAgent || http.globalAgent || {}
@@ -55,7 +51,7 @@ class HttpClientPlugin extends ClientPlugin {
55
51
  metrics: {
56
52
  [CLIENT_PORT_KEY]: parseInt(options.port)
57
53
  }
58
- })
54
+ }, false)
59
55
 
60
56
  // TODO: Figure out a better way to do this for any span.
61
57
  if (!allowed) {
@@ -67,11 +63,19 @@ class HttpClientPlugin extends ClientPlugin {
67
63
  }
68
64
 
69
65
  analyticsSampler.sample(span, this.config.measured)
70
- this.enter(span, store)
66
+
67
+ message.span = span
68
+ message.parentStore = store
69
+ message.currentStore = { ...store, span }
70
+
71
+ return message.currentStore
71
72
  }
72
73
 
73
- finish ({ req, res }) {
74
- const span = storage.getStore().span
74
+ bindAsyncStart ({ parentStore }) {
75
+ return parentStore
76
+ }
77
+
78
+ finish ({ req, res, span }) {
75
79
  if (res) {
76
80
  const status = res.status || res.statusCode
77
81
 
@@ -87,17 +91,18 @@ class HttpClientPlugin extends ClientPlugin {
87
91
  addRequestHeaders(req, span, this.config)
88
92
 
89
93
  this.config.hooks.request(span, req, res)
90
- super.finish()
91
- }
92
94
 
93
- error (err) {
94
- const span = storage.getStore().span
95
+ this.tagPeerService(span)
96
+
97
+ span.finish()
98
+ }
95
99
 
96
- if (err) {
100
+ error ({ span, error }) {
101
+ if (error) {
97
102
  span.addTags({
98
- [ERROR_TYPE]: err.name,
99
- [ERROR_MESSAGE]: err.message || err.code,
100
- [ERROR_STACK]: err.stack
103
+ [ERROR_TYPE]: error.name,
104
+ [ERROR_MESSAGE]: error.message || error.code,
105
+ [ERROR_STACK]: error.stack
101
106
  })
102
107
  } else {
103
108
  span.setTag('error', 1)
@@ -166,9 +166,12 @@ class JestPlugin extends CiPlugin {
166
166
  this.enter(span, store)
167
167
  })
168
168
 
169
- this.addSub('ci:jest:test:finish', (status) => {
169
+ this.addSub('ci:jest:test:finish', ({ status, testStartLine }) => {
170
170
  const span = storage.getStore().span
171
171
  span.setTag(TEST_STATUS, status)
172
+ if (testStartLine) {
173
+ span.setTag(TEST_SOURCE_START, testStartLine)
174
+ }
172
175
  span.finish()
173
176
  finishAllTraceSpans(span)
174
177
  })
@@ -197,8 +200,10 @@ class JestPlugin extends CiPlugin {
197
200
  const extraTags = {
198
201
  [JEST_TEST_RUNNER]: runner,
199
202
  [TEST_PARAMETERS]: testParameters,
200
- [TEST_FRAMEWORK_VERSION]: frameworkVersion,
201
- [TEST_SOURCE_START]: testStartLine
203
+ [TEST_FRAMEWORK_VERSION]: frameworkVersion
204
+ }
205
+ if (testStartLine) {
206
+ extraTags[TEST_SOURCE_START] = testStartLine
202
207
  }
203
208
 
204
209
  return super.startTestSpan(name, suite, this.testSuiteSpan, extraTags)