dd-trace 2.41.0 → 2.42.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.
Files changed (44) 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/jest.js +6 -4
  5. package/packages/datadog-plugin-graphql/src/execute.js +6 -4
  6. package/packages/datadog-plugin-jest/src/index.js +8 -3
  7. package/packages/datadog-plugin-openai/src/index.js +39 -16
  8. package/packages/datadog-plugin-openai/src/services.js +13 -9
  9. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +3 -1
  10. package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +45 -0
  11. package/packages/dd-trace/src/appsec/iast/analyzers/index.js +3 -3
  12. package/packages/dd-trace/src/appsec/iast/analyzers/missing-header-analyzer.js +66 -0
  13. package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +25 -8
  14. package/packages/dd-trace/src/appsec/iast/analyzers/xcontenttype-header-missing-analyzer.js +19 -0
  15. package/packages/dd-trace/src/appsec/iast/index.js +5 -2
  16. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +4 -2
  17. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +17 -1
  18. package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +1 -0
  19. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +5 -1
  20. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +3 -1
  21. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
  22. package/packages/dd-trace/src/config.js +26 -10
  23. package/packages/dd-trace/src/external-logger/src/index.js +9 -1
  24. package/packages/dd-trace/src/external-logger/test/index.spec.js +1 -1
  25. package/packages/dd-trace/src/format.js +1 -1
  26. package/packages/dd-trace/src/lambda/handler.js +8 -1
  27. package/packages/dd-trace/src/opentelemetry/span.js +3 -1
  28. package/packages/dd-trace/src/opentracing/span_context.js +2 -1
  29. package/packages/dd-trace/src/plugins/util/ci.js +2 -1
  30. package/packages/dd-trace/src/plugins/util/web.js +1 -0
  31. package/packages/dd-trace/src/profiling/config.js +8 -5
  32. package/packages/dd-trace/src/profiling/exporters/agent.js +4 -1
  33. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  34. package/packages/dd-trace/src/profiling/profilers/wall.js +144 -4
  35. package/packages/dd-trace/src/service-naming/index.js +2 -2
  36. package/packages/dd-trace/src/service-naming/schemas/v0/graphql.js +12 -0
  37. package/packages/dd-trace/src/service-naming/schemas/v0/index.js +2 -1
  38. package/packages/dd-trace/src/service-naming/schemas/v1/graphql.js +12 -0
  39. package/packages/dd-trace/src/service-naming/schemas/v1/index.js +2 -1
  40. package/packages/dd-trace/src/span_processor.js +0 -4
  41. package/packages/dd-trace/src/span_sampler.js +1 -1
  42. package/packages/dd-trace/src/telemetry/dependencies.js +24 -12
  43. package/packages/dd-trace/src/telemetry/metrics.js +11 -1
  44. package/scripts/version.js +0 -66
@@ -54,7 +54,11 @@ class VulnerabilityFormatter {
54
54
 
55
55
  formatEvidence (type, evidence, sourcesIndexes, sources) {
56
56
  if (!evidence.ranges) {
57
- return { value: evidence.value }
57
+ if (typeof evidence.value === 'undefined') {
58
+ return undefined
59
+ } else {
60
+ return { value: evidence.value }
61
+ }
58
62
  }
59
63
 
60
64
  return this._redactVulnearbilities
@@ -1,5 +1,6 @@
1
1
  module.exports = {
2
2
  COMMAND_INJECTION: 'COMMAND_INJECTION',
3
+ HSTS_HEADER_MISSING: 'HSTS_HEADER_MISSING',
3
4
  INSECURE_COOKIE: 'INSECURE_COOKIE',
4
5
  LDAP_INJECTION: 'LDAP_INJECTION',
5
6
  NO_HTTPONLY_COOKIE: 'NO_HTTPONLY_COOKIE',
@@ -9,5 +10,6 @@ module.exports = {
9
10
  SSRF: 'SSRF',
10
11
  UNVALIDATED_REDIRECT: 'UNVALIDATED_REDIRECT',
11
12
  WEAK_CIPHER: 'WEAK_CIPHER',
12
- WEAK_HASH: 'WEAK_HASH'
13
+ WEAK_HASH: 'WEAK_HASH',
14
+ XCONTENTTYPE_HEADER_MISSING: 'XCONTENTTYPE_HEADER_MISSING'
13
15
  }
@@ -28,7 +28,7 @@ function addVulnerability (iastContext, vulnerability) {
28
28
 
29
29
  function isValidVulnerability (vulnerability) {
30
30
  return vulnerability && vulnerability.type &&
31
- vulnerability.evidence && vulnerability.evidence.value &&
31
+ vulnerability.evidence &&
32
32
  vulnerability.location && vulnerability.location.spanId
33
33
  }
34
34
 
@@ -278,7 +278,7 @@ class Config {
278
278
  process.env.DD_TRACE_EXPERIMENTAL_B3_ENABLED,
279
279
  false
280
280
  )
281
- const defaultPropagationStyle = ['tracecontext', 'datadog']
281
+ const defaultPropagationStyle = ['datadog', 'tracecontext']
282
282
  if (isTrue(DD_TRACE_B3_ENABLED)) {
283
283
  defaultPropagationStyle.push('b3')
284
284
  defaultPropagationStyle.push('b3 single header')
@@ -318,7 +318,10 @@ class Config {
318
318
  false
319
319
  )
320
320
  const DD_TRACE_SPAN_ATTRIBUTE_SCHEMA = validateNamingVersion(
321
- process.env.DD_TRACE_SPAN_ATTRIBUTE_SCHEMA
321
+ coalesce(
322
+ options.spanAttributeSchema,
323
+ process.env.DD_TRACE_SPAN_ATTRIBUTE_SCHEMA
324
+ )
322
325
  )
323
326
  const DD_TRACE_PEER_SERVICE_MAPPING = coalesce(
324
327
  options.peerServiceMapping,
@@ -326,11 +329,27 @@ class Config {
326
329
  process.env.DD_TRACE_PEER_SERVICE_MAPPING.split(',').map(x => x.trim().split(':'))
327
330
  ) : {}
328
331
  )
329
- const DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED = process.env.DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED
332
+
333
+ const peerServiceSet = (
334
+ options.hasOwnProperty('spanComputePeerService') ||
335
+ process.env.hasOwnProperty('DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED')
336
+ )
337
+ const peerServiceValue = coalesce(
338
+ options.spanComputePeerService,
339
+ process.env.DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED
340
+ )
341
+
342
+ const DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED = (
343
+ DD_TRACE_SPAN_ATTRIBUTE_SCHEMA === 'v0'
344
+ // In v0, peer service is computed only if it is explicitly set to true
345
+ ? peerServiceSet && isTrue(peerServiceValue)
346
+ // In >v0, peer service is false only if it is explicitly set to false
347
+ : (peerServiceSet ? !isFalse(peerServiceValue) : true)
348
+ )
330
349
 
331
350
  const DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED = coalesce(
332
- isTrue(process.env.DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED),
333
- false
351
+ options.spanRemoveIntegrationFromService,
352
+ isTrue(process.env.DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED)
334
353
  )
335
354
  const DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH = coalesce(
336
355
  process.env.DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH,
@@ -562,11 +581,8 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
562
581
  exporters: DD_PROFILING_EXPORTERS
563
582
  }
564
583
  this.spanAttributeSchema = DD_TRACE_SPAN_ATTRIBUTE_SCHEMA
565
- this.spanComputePeerService = (this.spanAttributeSchema === 'v0'
566
- ? isTrue(DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED)
567
- : true
568
- )
569
- this.traceRemoveIntegrationServiceNamesEnabled = DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED
584
+ this.spanComputePeerService = DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED
585
+ this.spanRemoveIntegrationFromService = DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED
570
586
  this.peerServiceMapping = DD_TRACE_PEER_SERVICE_MAPPING
571
587
  this.lookup = options.lookup
572
588
  this.startupLogs = isTrue(DD_TRACE_STARTUP_LOGS)
@@ -127,4 +127,12 @@ class ExternalLogger {
127
127
  }
128
128
  }
129
129
 
130
- module.exports = ExternalLogger
130
+ class NoopExternalLogger {
131
+ log () { }
132
+ enqueue () { }
133
+ shutdown () { }
134
+ flush () { }
135
+ }
136
+
137
+ module.exports.ExternalLogger = ExternalLogger
138
+ module.exports.NoopExternalLogger = NoopExternalLogger
@@ -15,7 +15,7 @@ describe('External Logger', () => {
15
15
  beforeEach(() => {
16
16
  errorLog = sinon.spy(tracerLogger, 'error')
17
17
 
18
- const ExternalLogger = proxyquire('../src', {
18
+ const { ExternalLogger } = proxyquire('../src', {
19
19
  '../../log': {
20
20
  error: errorLog
21
21
  }
@@ -107,7 +107,7 @@ function extractTags (trace, span) {
107
107
  }
108
108
  }
109
109
 
110
- setSingleSpanIngestionTags(trace, context._sampling.spanSampling)
110
+ setSingleSpanIngestionTags(trace, context._spanSampling)
111
111
 
112
112
  addTag(trace.meta, trace.metrics, 'language', 'javascript')
113
113
  addTag(trace.meta, trace.metrics, PROCESS_ID, process.pid)
@@ -86,6 +86,13 @@ exports.datadog = function datadog (lambdaHandler) {
86
86
  const context = extractContext(args)
87
87
 
88
88
  checkTimeout(context)
89
- return lambdaHandler.apply(this, args).then((res) => { clearTimeout(__lambdaTimeout); return res })
89
+ const result = lambdaHandler.apply(this, args)
90
+ if (result && typeof result.then === 'function') {
91
+ return result.then((res) => {
92
+ clearTimeout(__lambdaTimeout)
93
+ return res
94
+ })
95
+ }
96
+ return result
90
97
  }
91
98
  }
@@ -10,6 +10,7 @@ const { timeInputToHrTime } = require('@opentelemetry/core')
10
10
  const tracer = require('../../')
11
11
  const DatadogSpan = require('../opentracing/span')
12
12
  const { ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK } = require('../constants')
13
+ const { SERVICE_NAME, RESOURCE_NAME } = require('../../../../ext/tags')
13
14
 
14
15
  const SpanContext = require('./span_context')
15
16
 
@@ -40,7 +41,8 @@ class Span {
40
41
  hostname: _tracer._hostname,
41
42
  integrationName: 'otel',
42
43
  tags: {
43
- 'service.name': _tracer._service
44
+ [SERVICE_NAME]: _tracer._service,
45
+ [RESOURCE_NAME]: spanName
44
46
  }
45
47
  }, _tracer._debug)
46
48
 
@@ -12,7 +12,8 @@ class DatadogSpanContext {
12
12
  this._name = props.name
13
13
  this._isFinished = props.isFinished || false
14
14
  this._tags = props.tags || {}
15
- this._sampling = Object.assign({}, props.sampling)
15
+ this._sampling = props.sampling || {}
16
+ this._spanSampling = undefined
16
17
  this._baggageItems = props.baggageItems || {}
17
18
  this._traceparent = props.traceparent
18
19
  this._tracestate = props.tracestate
@@ -399,6 +399,7 @@ module.exports = {
399
399
  BITBUCKET_BRANCH,
400
400
  BITBUCKET_COMMIT,
401
401
  BITBUCKET_GIT_SSH_ORIGIN,
402
+ BITBUCKET_GIT_HTTP_ORIGIN,
402
403
  BITBUCKET_TAG,
403
404
  BITBUCKET_PIPELINE_UUID,
404
405
  BITBUCKET_CLONE_DIR
@@ -416,7 +417,7 @@ module.exports = {
416
417
  [CI_PIPELINE_URL]: url,
417
418
  [GIT_BRANCH]: BITBUCKET_BRANCH,
418
419
  [GIT_TAG]: BITBUCKET_TAG,
419
- [GIT_REPOSITORY_URL]: BITBUCKET_GIT_SSH_ORIGIN,
420
+ [GIT_REPOSITORY_URL]: BITBUCKET_GIT_SSH_ORIGIN || BITBUCKET_GIT_HTTP_ORIGIN,
420
421
  [CI_WORKSPACE_PATH]: BITBUCKET_CLONE_DIR,
421
422
  [CI_PIPELINE_ID]: BITBUCKET_PIPELINE_UUID && BITBUCKET_PIPELINE_UUID.replace(/{|}/gm, '')
422
423
  }
@@ -103,6 +103,7 @@ const web = {
103
103
  context.res = res
104
104
 
105
105
  this.setConfig(req, config)
106
+ addRequestTags(context)
106
107
 
107
108
  return span
108
109
  },
@@ -18,7 +18,6 @@ class Config {
18
18
  const {
19
19
  DD_PROFILING_ENABLED,
20
20
  DD_PROFILING_PROFILERS,
21
- DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
22
21
  DD_ENV,
23
22
  DD_TAGS,
24
23
  DD_SERVICE,
@@ -36,7 +35,9 @@ class Config {
36
35
  DD_PROFILING_EXPERIMENTAL_OOM_MONITORING_ENABLED,
37
36
  DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE,
38
37
  DD_PROFILING_EXPERIMENTAL_OOM_MAX_HEAP_EXTENSION_COUNT,
39
- DD_PROFILING_EXPERIMENTAL_OOM_EXPORT_STRATEGIES
38
+ DD_PROFILING_EXPERIMENTAL_OOM_EXPORT_STRATEGIES,
39
+ DD_PROFILING_EXPERIMENTAL_CODEHOTSPOTS_ENABLED,
40
+ DD_PROFILING_EXPERIMENTAL_ENDPOINT_COLLECTION_ENABLED
40
41
  } = process.env
41
42
 
42
43
  const enabled = isTrue(coalesce(options.enabled, DD_PROFILING_ENABLED, true))
@@ -51,8 +52,8 @@ class Config {
51
52
  Number(DD_PROFILING_UPLOAD_TIMEOUT), 60 * 1000)
52
53
  const sourceMap = coalesce(options.sourceMap,
53
54
  DD_PROFILING_SOURCE_MAP, true)
54
- const endpointCollection = coalesce(options.endpointCollection,
55
- DD_PROFILING_ENDPOINT_COLLECTION_ENABLED, false)
55
+ const endpointCollectionEnabled = coalesce(options.endpointCollection,
56
+ DD_PROFILING_EXPERIMENTAL_ENDPOINT_COLLECTION_ENABLED, false)
56
57
  const pprofPrefix = coalesce(options.pprofPrefix,
57
58
  DD_PROFILING_PPROF_PREFIX, '')
58
59
 
@@ -73,7 +74,7 @@ class Config {
73
74
  this.uploadTimeout = uploadTimeout
74
75
  this.sourceMap = sourceMap
75
76
  this.debugSourceMaps = isTrue(coalesce(options.debugSourceMaps, DD_PROFILING_DEBUG_SOURCE_MAPS, false))
76
- this.endpointCollection = endpointCollection
77
+ this.endpointCollectionEnabled = endpointCollectionEnabled
77
78
  this.pprofPrefix = pprofPrefix
78
79
 
79
80
  const hostname = coalesce(options.hostname, DD_AGENT_HOST) || 'localhost'
@@ -110,6 +111,8 @@ class Config {
110
111
  const profilers = options.profilers
111
112
  ? options.profilers
112
113
  : getProfilers({ DD_PROFILING_HEAP_ENABLED, DD_PROFILING_WALLTIME_ENABLED, DD_PROFILING_PROFILERS })
114
+ this.codeHotspotsEnabled = isTrue(coalesce(options.codeHotspotsEnabled,
115
+ DD_PROFILING_EXPERIMENTAL_CODEHOTSPOTS_ENABLED, false))
113
116
 
114
117
  this.profilers = ensureProfilers(profilers, this)
115
118
  }
@@ -1,7 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  const retry = require('retry')
4
- const { request } = require('http')
4
+ const { request: httpRequest } = require('http')
5
+ const { request: httpsRequest } = require('https')
5
6
 
6
7
  // TODO: avoid using dd-trace internals. Make this a separate module?
7
8
  const docker = require('../../exporters/common/docker')
@@ -12,6 +13,8 @@ const version = require('../../../../../package.json').version
12
13
  const containerId = docker.id()
13
14
 
14
15
  function sendRequest (options, form, callback) {
16
+ const request = options.protocol === 'https:' ? httpsRequest : httpRequest
17
+
15
18
  const store = storage.getStore()
16
19
  storage.enterWith({ noop: true })
17
20
  const req = request(options, res => {
@@ -23,7 +23,7 @@ class Profiler extends EventEmitter {
23
23
  }
24
24
 
25
25
  start (options) {
26
- this._start(options).catch(() => {})
26
+ this._start(options).catch((err) => { if (options.logger) options.logger.error(err) })
27
27
  return this
28
28
  }
29
29
 
@@ -1,23 +1,111 @@
1
1
  'use strict'
2
2
 
3
+ const { storage } = require('../../../../datadog-core')
4
+
5
+ const dc = require('../../../../diagnostics_channel')
6
+
7
+ const beforeCh = dc.channel('dd-trace:storage:before')
8
+ const enterCh = dc.channel('dd-trace:storage:enter')
9
+
10
+ let kSampleCount
11
+
12
+ function getActiveSpan () {
13
+ const store = storage.getStore()
14
+ return store && store.span
15
+ }
16
+
17
+ function getStartedSpans (context) {
18
+ return context._trace.started
19
+ }
20
+
21
+ function generateLabels ({ spanId, rootSpanId, webTags, endpoint }) {
22
+ const labels = {}
23
+ if (spanId) {
24
+ labels['span id'] = spanId
25
+ }
26
+ if (rootSpanId) {
27
+ labels['local root span id'] = rootSpanId
28
+ }
29
+ if (webTags && Object.keys(webTags).length !== 0) {
30
+ labels['trace endpoint'] = endpointNameFromTags(webTags)
31
+ } else if (endpoint) {
32
+ // fallback to endpoint computed when sample was taken
33
+ labels['trace endpoint'] = endpoint
34
+ }
35
+
36
+ return labels
37
+ }
38
+
39
+ function getSpanContextTags (span) {
40
+ return span.context()._tags
41
+ }
42
+
43
+ function isWebServerSpan (tags) {
44
+ return tags['span.type'] === 'web'
45
+ }
46
+
47
+ function endpointNameFromTags (tags) {
48
+ return tags['resource.name'] || [
49
+ tags['http.method'],
50
+ tags['http.route']
51
+ ].filter(v => v).join(' ')
52
+ }
53
+
54
+ function updateContext (context, span, startedSpans, endpointCollectionEnabled) {
55
+ context.spanId = span.context().toSpanId()
56
+ const rootSpan = startedSpans[0]
57
+ if (rootSpan) {
58
+ context.rootSpanId = rootSpan.context().toSpanId()
59
+ if (endpointCollectionEnabled) {
60
+ // Find the first webspan starting from the end:
61
+ // There might be several webspans, for example with next.js, http plugin creates a first span
62
+ // and then next.js plugin creates a child span, and this child span haves the correct endpoint information.
63
+ for (let i = startedSpans.length - 1; i >= 0; i--) {
64
+ const tags = getSpanContextTags(startedSpans[i])
65
+ if (isWebServerSpan(tags)) {
66
+ context.webTags = tags
67
+ // endpoint may not be determined yet, but keep it as fallback
68
+ // if tags are not available anymore during serialization
69
+ context.endpoint = endpointNameFromTags(tags)
70
+ break
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }
76
+
3
77
  class NativeWallProfiler {
4
78
  constructor (options = {}) {
5
79
  this.type = 'wall'
6
80
  this._samplingIntervalMicros = options.samplingInterval || 1e6 / 99 // 99hz
7
81
  this._flushIntervalMillis = options.flushInterval || 60 * 1e3 // 60 seconds
8
82
  this._codeHotspotsEnabled = !!options.codeHotspotsEnabled
83
+ this._endpointCollectionEnabled = !!options.endpointCollectionEnabled
9
84
  this._mapper = undefined
10
85
  this._pprof = undefined
11
86
 
87
+ // Bind to this so the same value can be used to unsubscribe later
88
+ this._enter = this._enter.bind(this)
12
89
  this._logger = options.logger
13
90
  this._started = false
14
91
  }
15
92
 
93
+ codeHotspotsEnabled () {
94
+ return this._codeHotspotsEnabled
95
+ }
96
+
16
97
  start ({ mapper } = {}) {
17
98
  if (this._started) return
18
99
 
100
+ if (this._codeHotspotsEnabled && !this._emittedFFMessage && this._logger) {
101
+ this._logger.debug(
102
+ `Wall profiler: Enable config_trace_show_breakdown_profiling_for_node feature flag to see code hotspots.`)
103
+ this._emittedFFMessage = true
104
+ }
105
+
19
106
  this._mapper = mapper
20
107
  this._pprof = require('@datadog/pprof')
108
+ kSampleCount = this._pprof.time.constants.kSampleCount
21
109
 
22
110
  // pprof otherwise crashes in worker threads
23
111
  if (!process._startProfilerIdleNotifier) {
@@ -31,16 +119,62 @@ class NativeWallProfiler {
31
119
  intervalMicros: this._samplingIntervalMicros,
32
120
  durationMillis: this._flushIntervalMillis,
33
121
  sourceMapper: this._mapper,
34
- customLabels: this._codeHotspotsEnabled,
122
+ withContexts: this._codeHotspotsEnabled,
35
123
  lineNumbers: false
36
124
  })
37
125
 
126
+ if (this._codeHotspotsEnabled) {
127
+ this._profilerState = this._pprof.time.getState()
128
+ this._currentContext = {}
129
+ this._pprof.time.setContext(this._currentContext)
130
+ this._lastSpan = undefined
131
+ this._lastStartedSpans = undefined
132
+ this._lastSampleCount = 0
133
+
134
+ beforeCh.subscribe(this._enter)
135
+ enterCh.subscribe(this._enter)
136
+ }
137
+
38
138
  this._started = true
39
139
  }
40
140
 
41
- profile () {
141
+ _enter () {
42
142
  if (!this._started) return
43
- return this._pprof.time.stop(true)
143
+
144
+ const sampleCount = this._profilerState[kSampleCount]
145
+ if (sampleCount !== this._lastSampleCount) {
146
+ this._lastSampleCount = sampleCount
147
+ const context = this._currentContext
148
+ this._currentContext = {}
149
+ this._pprof.time.setContext(this._currentContext)
150
+
151
+ if (this._lastSpan) {
152
+ updateContext(context, this._lastSpan, this._lastStartedSpans, this._endpointCollectionEnabled)
153
+ }
154
+ }
155
+
156
+ const span = getActiveSpan()
157
+ if (span) {
158
+ this._lastSpan = span
159
+ this._lastStartedSpans = getStartedSpans(span.context())
160
+ } else {
161
+ this._lastStartedSpans = undefined
162
+ this._lastSpan = undefined
163
+ }
164
+ }
165
+
166
+ _stop (restart) {
167
+ if (!this._started) return
168
+ if (this._codeHotspotsEnabled) {
169
+ // update last sample context if needed
170
+ this._enter()
171
+ this._lastSampleCount = 0
172
+ }
173
+ return this._pprof.time.stop(restart, this._codeHotspotsEnabled ? generateLabels : undefined)
174
+ }
175
+
176
+ profile () {
177
+ return this._stop(true)
44
178
  }
45
179
 
46
180
  encode (profile) {
@@ -50,7 +184,13 @@ class NativeWallProfiler {
50
184
  stop () {
51
185
  if (!this._started) return
52
186
 
53
- const profile = this._pprof.time.stop()
187
+ const profile = this._stop(false)
188
+ if (this._codeHotspotsEnabled) {
189
+ beforeCh.unsubscribe(this._enter)
190
+ enterCh.subscribe(this._enter)
191
+ this._profilerState = undefined
192
+ }
193
+
54
194
  this._started = false
55
195
  return profile
56
196
  }
@@ -3,7 +3,7 @@ const { schemaDefinitions } = require('./schemas')
3
3
  class SchemaManager {
4
4
  constructor () {
5
5
  this.schemas = schemaDefinitions
6
- this.config = { spanAttributeSchema: 'v0', traceRemoveIntegrationServiceNamesEnabled: false }
6
+ this.config = { spanAttributeSchema: 'v0', spanRemoveIntegrationFromService: false }
7
7
  }
8
8
 
9
9
  get schema () {
@@ -15,7 +15,7 @@ class SchemaManager {
15
15
  }
16
16
 
17
17
  get shouldUseConsistentServiceNaming () {
18
- return this.config.traceRemoveIntegrationServiceNamesEnabled && this.version === 'v0'
18
+ return this.config.spanRemoveIntegrationFromService && this.version === 'v0'
19
19
  }
20
20
 
21
21
  opName (type, kind, plugin, ...opNameArgs) {
@@ -0,0 +1,12 @@
1
+ const { identityService } = require('../util')
2
+
3
+ const graphql = {
4
+ server: {
5
+ graphql: {
6
+ opName: () => 'graphql.execute',
7
+ serviceName: identityService
8
+ }
9
+ }
10
+ }
11
+
12
+ module.exports = graphql
@@ -1,6 +1,7 @@
1
1
  const SchemaDefinition = require('../definition')
2
2
  const messaging = require('./messaging')
3
3
  const storage = require('./storage')
4
+ const graphql = require('./graphql')
4
5
  const web = require('./web')
5
6
 
6
- module.exports = new SchemaDefinition({ messaging, storage, web })
7
+ module.exports = new SchemaDefinition({ messaging, storage, web, graphql })
@@ -0,0 +1,12 @@
1
+ const { identityService } = require('../util')
2
+
3
+ const graphql = {
4
+ server: {
5
+ graphql: {
6
+ opName: () => 'graphql.server.request',
7
+ serviceName: identityService
8
+ }
9
+ }
10
+ }
11
+
12
+ module.exports = graphql
@@ -1,6 +1,7 @@
1
1
  const SchemaDefinition = require('../definition')
2
2
  const messaging = require('./messaging')
3
3
  const storage = require('./storage')
4
+ const graphql = require('./graphql')
4
5
  const web = require('./web')
5
6
 
6
- module.exports = new SchemaDefinition({ messaging, storage, web })
7
+ module.exports = new SchemaDefinition({ messaging, storage, web, graphql })
@@ -138,10 +138,6 @@ class SpanProcessor {
138
138
  }
139
139
  }
140
140
 
141
- for (const span of trace.finished) {
142
- span.context()._tags = {}
143
- }
144
-
145
141
  trace.started = active
146
142
  trace.finished = []
147
143
  }
@@ -82,7 +82,7 @@ class SpanSampler {
82
82
 
83
83
  const rule = this.findRule(service, name)
84
84
  if (rule && rule.sample()) {
85
- span.context()._sampling.spanSampling = {
85
+ span.context()._spanSampling = {
86
86
  sampleRate: rule.sampleRate,
87
87
  maxPerSecond: rule.maxPerSecond
88
88
  }
@@ -7,8 +7,10 @@ const { sendData } = require('./send-data')
7
7
  const dc = require('../../../diagnostics_channel')
8
8
  const { fileURLToPath } = require('url')
9
9
 
10
- const savedDependencies = new Set()
11
- const detectedDependencyNames = new Set()
10
+ const savedDependenciesToSend = new Set()
11
+ const detectedDependencyKeys = new Set()
12
+ const detectedDependencyVersions = new Set()
13
+
12
14
  const FILE_URI_START = `file://`
13
15
  const moduleLoadStartChannel = dc.channel('dd-trace:moduleLoadStart')
14
16
 
@@ -18,14 +20,14 @@ function waitAndSend (config, application, host) {
18
20
  if (!immediate) {
19
21
  immediate = setImmediate(() => {
20
22
  immediate = null
21
- if (savedDependencies.size > 0) {
22
- const dependencies = Array.from(savedDependencies.values()).splice(0, 1000).map(pair => {
23
- savedDependencies.delete(pair)
23
+ if (savedDependenciesToSend.size > 0) {
24
+ const dependencies = Array.from(savedDependenciesToSend.values()).splice(0, 1000).map(pair => {
25
+ savedDependenciesToSend.delete(pair)
24
26
  const [name, version] = pair.split(' ')
25
27
  return { name, version }
26
28
  })
27
29
  sendData(config, application, host, 'app-dependencies-loaded', { dependencies })
28
- if (savedDependencies.size > 0) {
30
+ if (savedDependenciesToSend.size > 0) {
29
31
  waitAndSend(config, application, host)
30
32
  }
31
33
  }
@@ -46,15 +48,24 @@ function onModuleLoad (data) {
46
48
  }
47
49
  const parseResult = filename && parse(filename)
48
50
  const request = data.request || (parseResult && parseResult.name)
49
- if (filename && request && isDependency(filename, request) && !detectedDependencyNames.has(request)) {
50
- detectedDependencyNames.add(request)
51
+ const dependencyKey = parseResult && parseResult.basedir ? parseResult.basedir : request
52
+
53
+ if (filename && request && isDependency(filename, request) && !detectedDependencyKeys.has(dependencyKey)) {
54
+ detectedDependencyKeys.add(dependencyKey)
55
+
51
56
  if (parseResult) {
52
57
  const { name, basedir } = parseResult
53
58
  if (basedir) {
54
59
  try {
55
60
  const { version } = requirePackageJson(basedir, module)
56
- savedDependencies.add(`${name} ${version}`)
57
- waitAndSend(config, application, host)
61
+ const dependencyAndVersion = `${name} ${version}`
62
+
63
+ if (!detectedDependencyVersions.has(dependencyAndVersion)) {
64
+ savedDependenciesToSend.add(dependencyAndVersion)
65
+ detectedDependencyVersions.add(dependencyAndVersion)
66
+
67
+ waitAndSend(config, application, host)
68
+ }
58
69
  } catch (e) {
59
70
  // can not read the package.json, do nothing
60
71
  }
@@ -88,8 +99,9 @@ function stop () {
88
99
  config = null
89
100
  application = null
90
101
  host = null
91
- detectedDependencyNames.clear()
92
- savedDependencies.clear()
102
+ detectedDependencyKeys.clear()
103
+ savedDependenciesToSend.clear()
104
+ detectedDependencyVersions.clear()
93
105
  if (moduleLoadStartChannel.hasSubscribers) {
94
106
  moduleLoadStartChannel.unsubscribe(onModuleLoad)
95
107
  }
@@ -25,6 +25,10 @@ function mapToJsonArray (map) {
25
25
  return Array.from(map.values()).map(v => v.toJSON())
26
26
  }
27
27
 
28
+ function hasPoints (metric) {
29
+ return metric.points.length > 0
30
+ }
31
+
28
32
  class Metric {
29
33
  constructor (namespace, metric, common, tags) {
30
34
  this.namespace = namespace.toString()
@@ -172,10 +176,16 @@ class MetricsCollection extends Map {
172
176
 
173
177
  toJSON () {
174
178
  if (!this.size) return
179
+
180
+ const series = mapToJsonArray(this)
181
+ .filter(hasPoints)
182
+
183
+ if (!series.length) return
184
+
175
185
  const { namespace } = this
176
186
  return {
177
187
  namespace,
178
- series: mapToJsonArray(this)
188
+ series
179
189
  }
180
190
  }
181
191
  }