dd-trace 4.45.0 → 4.46.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 (84) hide show
  1. package/package.json +3 -3
  2. package/packages/datadog-instrumentations/src/aerospike.js +1 -1
  3. package/packages/datadog-instrumentations/src/apollo-server.js +1 -1
  4. package/packages/datadog-instrumentations/src/aws-sdk.js +4 -4
  5. package/packages/datadog-instrumentations/src/body-parser.js +4 -4
  6. package/packages/datadog-instrumentations/src/cassandra-driver.js +2 -2
  7. package/packages/datadog-instrumentations/src/child_process.js +2 -2
  8. package/packages/datadog-instrumentations/src/connect.js +4 -4
  9. package/packages/datadog-instrumentations/src/cookie-parser.js +4 -4
  10. package/packages/datadog-instrumentations/src/couchbase.js +12 -12
  11. package/packages/datadog-instrumentations/src/cucumber.js +6 -5
  12. package/packages/datadog-instrumentations/src/dns.js +10 -10
  13. package/packages/datadog-instrumentations/src/elasticsearch.js +4 -4
  14. package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +3 -3
  15. package/packages/datadog-instrumentations/src/express.js +4 -4
  16. package/packages/datadog-instrumentations/src/fastify.js +6 -6
  17. package/packages/datadog-instrumentations/src/fetch.js +1 -1
  18. package/packages/datadog-instrumentations/src/find-my-way.js +2 -2
  19. package/packages/datadog-instrumentations/src/fs.js +2 -2
  20. package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +2 -2
  21. package/packages/datadog-instrumentations/src/grpc/client.js +4 -6
  22. package/packages/datadog-instrumentations/src/grpc/server.js +2 -2
  23. package/packages/datadog-instrumentations/src/hapi.js +10 -13
  24. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  25. package/packages/datadog-instrumentations/src/http/client.js +3 -3
  26. package/packages/datadog-instrumentations/src/jest.js +5 -4
  27. package/packages/datadog-instrumentations/src/knex.js +2 -2
  28. package/packages/datadog-instrumentations/src/koa.js +5 -5
  29. package/packages/datadog-instrumentations/src/ldapjs.js +1 -1
  30. package/packages/datadog-instrumentations/src/mariadb.js +8 -8
  31. package/packages/datadog-instrumentations/src/memcached.js +2 -2
  32. package/packages/datadog-instrumentations/src/microgateway-core.js +4 -4
  33. package/packages/datadog-instrumentations/src/mocha/common.js +1 -1
  34. package/packages/datadog-instrumentations/src/mocha/main.js +1 -0
  35. package/packages/datadog-instrumentations/src/mocha/utils.js +2 -3
  36. package/packages/datadog-instrumentations/src/mocha.js +4 -0
  37. package/packages/datadog-instrumentations/src/moleculer/server.js +2 -2
  38. package/packages/datadog-instrumentations/src/mongodb-core.js +7 -7
  39. package/packages/datadog-instrumentations/src/mongoose.js +5 -6
  40. package/packages/datadog-instrumentations/src/mysql.js +3 -3
  41. package/packages/datadog-instrumentations/src/mysql2.js +6 -6
  42. package/packages/datadog-instrumentations/src/net.js +2 -2
  43. package/packages/datadog-instrumentations/src/next.js +5 -5
  44. package/packages/datadog-instrumentations/src/openai.js +58 -69
  45. package/packages/datadog-instrumentations/src/oracledb.js +8 -8
  46. package/packages/datadog-instrumentations/src/passport-http.js +1 -1
  47. package/packages/datadog-instrumentations/src/passport-local.js +1 -1
  48. package/packages/datadog-instrumentations/src/passport-utils.js +1 -1
  49. package/packages/datadog-instrumentations/src/pg.js +1 -1
  50. package/packages/datadog-instrumentations/src/pino.js +4 -4
  51. package/packages/datadog-instrumentations/src/playwright.js +6 -4
  52. package/packages/datadog-instrumentations/src/redis.js +2 -2
  53. package/packages/datadog-instrumentations/src/restify.js +4 -4
  54. package/packages/datadog-instrumentations/src/rhea.js +4 -4
  55. package/packages/datadog-instrumentations/src/router.js +5 -5
  56. package/packages/datadog-instrumentations/src/sharedb.js +2 -2
  57. package/packages/datadog-instrumentations/src/vitest.js +4 -3
  58. package/packages/datadog-instrumentations/src/winston.js +2 -3
  59. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +5 -6
  60. package/packages/datadog-plugin-hapi/src/index.js +2 -2
  61. package/packages/datadog-plugin-jest/src/index.js +1 -0
  62. package/packages/datadog-plugin-openai/src/index.js +58 -47
  63. package/packages/datadog-shimmer/src/shimmer.js +144 -10
  64. package/packages/dd-trace/src/appsec/blocking.js +23 -17
  65. package/packages/dd-trace/src/appsec/graphql.js +3 -1
  66. package/packages/dd-trace/src/appsec/iast/iast-log.js +2 -1
  67. package/packages/dd-trace/src/appsec/remote_config/manager.js +4 -1
  68. package/packages/dd-trace/src/appsec/rule_manager.js +8 -0
  69. package/packages/dd-trace/src/appsec/telemetry.js +3 -3
  70. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +2 -1
  71. package/packages/dd-trace/src/config.js +40 -31
  72. package/packages/dd-trace/src/lambda/handler.js +1 -0
  73. package/packages/dd-trace/src/lambda/index.js +12 -1
  74. package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -6
  75. package/packages/dd-trace/src/plugins/util/test.js +1 -5
  76. package/packages/dd-trace/src/profiler.js +15 -5
  77. package/packages/dd-trace/src/profiling/config.js +2 -4
  78. package/packages/dd-trace/src/profiling/exporter_cli.js +13 -1
  79. package/packages/dd-trace/src/profiling/exporters/agent.js +7 -1
  80. package/packages/dd-trace/src/profiling/profiler.js +0 -9
  81. package/packages/dd-trace/src/profiling/ssi-heuristics.js +49 -58
  82. package/packages/dd-trace/src/proxy.js +21 -21
  83. package/packages/dd-trace/src/telemetry/index.js +23 -6
  84. package/packages/dd-trace/src/telemetry/logs/index.js +20 -0
@@ -90,14 +90,14 @@ function updateRaspRequestsMetricTags (metrics, req, raspRuleType) {
90
90
  if (!enabled) return
91
91
 
92
92
  const tags = { rule_type: raspRuleType, waf_version: metrics.wafVersion }
93
- appsecMetrics.count('appsec.rasp.rule.eval', tags).inc(1)
93
+ appsecMetrics.count('rasp.rule.eval', tags).inc(1)
94
94
 
95
95
  if (metrics.wafTimeout) {
96
- appsecMetrics.count('appsec.rasp.timeout', tags).inc(1)
96
+ appsecMetrics.count('rasp.timeout', tags).inc(1)
97
97
  }
98
98
 
99
99
  if (metrics.ruleTriggered) {
100
- appsecMetrics.count('appsec.rasp.rule.match', tags).inc(1)
100
+ appsecMetrics.count('rasp.rule.match', tags).inc(1)
101
101
  }
102
102
  }
103
103
 
@@ -201,7 +201,8 @@ class CiVisibilityExporter extends AgentInfoExporter {
201
201
  isEarlyFlakeDetectionEnabled: isEarlyFlakeDetectionEnabled && this._config.isEarlyFlakeDetectionEnabled,
202
202
  earlyFlakeDetectionNumRetries,
203
203
  earlyFlakeDetectionFaultyThreshold,
204
- isFlakyTestRetriesEnabled
204
+ isFlakyTestRetriesEnabled: isFlakyTestRetriesEnabled && this._config.isFlakyTestRetriesEnabled,
205
+ flakyTestRetriesCount: this._config.flakyTestRetriesCount
205
206
  }
206
207
  }
207
208
 
@@ -441,9 +441,12 @@ class Config {
441
441
  this._setValue(defaults, 'iast.redactionValuePattern', null)
442
442
  this._setValue(defaults, 'iast.requestSampling', 30)
443
443
  this._setValue(defaults, 'iast.telemetryVerbosity', 'INFORMATION')
444
+ this._setValue(defaults, 'injectionEnabled', [])
444
445
  this._setValue(defaults, 'isAzureFunction', false)
445
446
  this._setValue(defaults, 'isCiVisibility', false)
446
447
  this._setValue(defaults, 'isEarlyFlakeDetectionEnabled', false)
448
+ this._setValue(defaults, 'isFlakyTestRetriesEnabled', false)
449
+ this._setValue(defaults, 'flakyTestRetriesCount', 5)
447
450
  this._setValue(defaults, 'isGCPFunction', false)
448
451
  this._setValue(defaults, 'isGitUploadEnabled', false)
449
452
  this._setValue(defaults, 'isIntelligentTestRunnerEnabled', false)
@@ -459,8 +462,6 @@ class Config {
459
462
  this._setValue(defaults, 'profiling.enabled', undefined)
460
463
  this._setValue(defaults, 'profiling.exporters', 'agent')
461
464
  this._setValue(defaults, 'profiling.sourceMap', true)
462
- this._setValue(defaults, 'profiling.ssi', false)
463
- this._setValue(defaults, 'profiling.heuristicsEnabled', false)
464
465
  this._setValue(defaults, 'profiling.longLivedThreshold', undefined)
465
466
  this._setValue(defaults, 'protocolVersion', '0.4')
466
467
  this._setValue(defaults, 'queryStringObfuscation', qsRegex)
@@ -681,6 +682,7 @@ class Config {
681
682
  }
682
683
  this._envUnprocessed['iast.requestSampling'] = DD_IAST_REQUEST_SAMPLING
683
684
  this._setString(env, 'iast.telemetryVerbosity', DD_IAST_TELEMETRY_VERBOSITY)
685
+ this._setArray(env, 'injectionEnabled', DD_INJECTION_ENABLED)
684
686
  this._setBoolean(env, 'isAzureFunction', getIsAzureFunction())
685
687
  this._setBoolean(env, 'isGCPFunction', getIsGCPFunction())
686
688
  this._setBoolean(env, 'logInjection', DD_LOGS_INJECTION)
@@ -696,18 +698,18 @@ class Config {
696
698
  this._envUnprocessed.peerServiceMapping = DD_TRACE_PEER_SERVICE_MAPPING
697
699
  }
698
700
  this._setString(env, 'port', DD_TRACE_AGENT_PORT)
699
- this._setBoolean(env, 'profiling.enabled', coalesce(DD_EXPERIMENTAL_PROFILING_ENABLED, DD_PROFILING_ENABLED))
701
+ const profilingEnabledEnv = coalesce(DD_EXPERIMENTAL_PROFILING_ENABLED, DD_PROFILING_ENABLED)
702
+ const profilingEnabled = isTrue(profilingEnabledEnv)
703
+ ? 'true'
704
+ : isFalse(profilingEnabledEnv)
705
+ ? 'false'
706
+ : profilingEnabledEnv === 'auto' ? 'auto' : undefined
707
+ this._setString(env, 'profiling.enabled', profilingEnabled)
700
708
  this._setString(env, 'profiling.exporters', DD_PROFILING_EXPORTERS)
701
709
  this._setBoolean(env, 'profiling.sourceMap', DD_PROFILING_SOURCE_MAP && !isFalse(DD_PROFILING_SOURCE_MAP))
702
- if (DD_PROFILING_ENABLED === 'auto' || DD_INJECTION_ENABLED) {
703
- this._setBoolean(env, 'profiling.ssi', true)
704
- if (DD_PROFILING_ENABLED === 'auto' || DD_INJECTION_ENABLED.split(',').includes('profiler')) {
705
- this._setBoolean(env, 'profiling.heuristicsEnabled', true)
706
- }
707
- if (DD_INTERNAL_PROFILING_LONG_LIVED_THRESHOLD) {
708
- // This is only used in testing to not have to wait 30s
709
- this._setValue(env, 'profiling.longLivedThreshold', Number(DD_INTERNAL_PROFILING_LONG_LIVED_THRESHOLD))
710
- }
710
+ if (DD_INTERNAL_PROFILING_LONG_LIVED_THRESHOLD) {
711
+ // This is only used in testing to not have to wait 30s
712
+ this._setValue(env, 'profiling.longLivedThreshold', Number(DD_INTERNAL_PROFILING_LONG_LIVED_THRESHOLD))
711
713
  }
712
714
 
713
715
  this._setString(env, 'protocolVersion', DD_TRACE_AGENT_PROTOCOL_VERSION)
@@ -762,12 +764,7 @@ class Config {
762
764
  this._setBoolean(env, 'telemetry.dependencyCollection', DD_TELEMETRY_DEPENDENCY_COLLECTION_ENABLED)
763
765
  this._setValue(env, 'telemetry.heartbeatInterval', maybeInt(Math.floor(DD_TELEMETRY_HEARTBEAT_INTERVAL * 1000)))
764
766
  this._envUnprocessed['telemetry.heartbeatInterval'] = DD_TELEMETRY_HEARTBEAT_INTERVAL * 1000
765
- const hasTelemetryLogsUsingFeatures =
766
- env['iast.enabled'] || env['profiling.enabled'] || env['profiling.heuristicsEnabled']
767
- ? true
768
- : undefined
769
- this._setBoolean(env, 'telemetry.logCollection', coalesce(DD_TELEMETRY_LOG_COLLECTION_ENABLED,
770
- hasTelemetryLogsUsingFeatures))
767
+ this._setBoolean(env, 'telemetry.logCollection', DD_TELEMETRY_LOG_COLLECTION_ENABLED)
771
768
  this._setBoolean(env, 'telemetry.metrics', DD_TELEMETRY_METRICS_ENABLED)
772
769
  this._setBoolean(env, 'traceId128BitGenerationEnabled', DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED)
773
770
  this._setBoolean(env, 'traceId128BitLoggingEnabled', DD_TRACE_128_BIT_TRACEID_LOGGING_ENABLED)
@@ -862,7 +859,10 @@ class Config {
862
859
  this._setValue(opts, 'peerServiceMapping', options.peerServiceMapping)
863
860
  this._setBoolean(opts, 'plugins', options.plugins)
864
861
  this._setString(opts, 'port', options.port)
865
- this._setBoolean(opts, 'profiling.enabled', options.profiling)
862
+ const strProfiling = String(options.profiling)
863
+ if (['true', 'false', 'auto'].includes(strProfiling)) {
864
+ this._setString(opts, 'profiling.enabled', strProfiling)
865
+ }
866
866
  this._setString(opts, 'protocolVersion', options.protocolVersion)
867
867
  if (options.remoteConfig) {
868
868
  this._setValue(opts, 'remoteConfig.pollInterval', maybeFloat(options.remoteConfig.pollInterval))
@@ -885,10 +885,6 @@ class Config {
885
885
  this._setBoolean(opts, 'spanRemoveIntegrationFromService', options.spanRemoveIntegrationFromService)
886
886
  this._setBoolean(opts, 'startupLogs', options.startupLogs)
887
887
  this._setTags(opts, 'tags', tags)
888
- const hasTelemetryLogsUsingFeatures =
889
- (options.iast && (options.iast === true || options.iast?.enabled === true)) ||
890
- (options.profiling && options.profiling === true)
891
- this._setBoolean(opts, 'telemetry.logCollection', hasTelemetryLogsUsingFeatures)
892
888
  this._setBoolean(opts, 'traceId128BitGenerationEnabled', options.traceId128BitGenerationEnabled)
893
889
  this._setBoolean(opts, 'traceId128BitLoggingEnabled', options.traceId128BitLoggingEnabled)
894
890
  this._setString(opts, 'version', options.version || tags.version)
@@ -987,7 +983,9 @@ class Config {
987
983
 
988
984
  const {
989
985
  DD_CIVISIBILITY_AGENTLESS_URL,
990
- DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED
986
+ DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED,
987
+ DD_CIVISIBILITY_FLAKY_RETRY_ENABLED,
988
+ DD_CIVISIBILITY_FLAKY_RETRY_COUNT
991
989
  } = process.env
992
990
 
993
991
  if (DD_CIVISIBILITY_AGENTLESS_URL) {
@@ -998,6 +996,9 @@ class Config {
998
996
  if (this._isCiVisibility()) {
999
997
  this._setBoolean(calc, 'isEarlyFlakeDetectionEnabled',
1000
998
  coalesce(DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED, true))
999
+ this._setBoolean(calc, 'isFlakyTestRetriesEnabled',
1000
+ coalesce(DD_CIVISIBILITY_FLAKY_RETRY_ENABLED, true))
1001
+ this._setValue(calc, 'flakyTestRetriesCount', coalesce(maybeInt(DD_CIVISIBILITY_FLAKY_RETRY_COUNT), 5))
1001
1002
  this._setBoolean(calc, 'isIntelligentTestRunnerEnabled', isTrue(this._isCiVisibilityItrEnabled()))
1002
1003
  this._setBoolean(calc, 'isManualApiEnabled', this._isCiVisibilityManualApiEnabled())
1003
1004
  }
@@ -1019,6 +1020,13 @@ class Config {
1019
1020
  calc['tracePropagationStyle.inject'] = calc['tracePropagationStyle.inject'] || defaultPropagationStyle
1020
1021
  calc['tracePropagationStyle.extract'] = calc['tracePropagationStyle.extract'] || defaultPropagationStyle
1021
1022
  }
1023
+
1024
+ const iastEnabled = coalesce(this._options['iast.enabled'], this._env['iast.enabled'])
1025
+ const profilingEnabled = coalesce(this._options['profiling.enabled'], this._env['profiling.enabled'])
1026
+ const injectionIncludesProfiler = (this._env.injectionEnabled || []).includes('profiler')
1027
+ if (iastEnabled || ['auto', 'true'].includes(profilingEnabled) || injectionIncludesProfiler) {
1028
+ this._setBoolean(calc, 'telemetry.logCollection', true)
1029
+ }
1022
1030
  }
1023
1031
 
1024
1032
  _applyRemote (options) {
@@ -1143,17 +1151,18 @@ class Config {
1143
1151
  for (const name in this._defaults) {
1144
1152
  for (let i = 0; i < containers.length; i++) {
1145
1153
  const container = containers[i]
1146
- const origin = origins[i]
1147
- const unprocessed = unprocessedValues[i]
1154
+ const value = container[name]
1148
1155
 
1149
- if ((container[name] !== null && container[name] !== undefined) || container === this._defaults) {
1150
- if (get(this, name) === container[name] && has(this, name)) break
1156
+ if ((value !== null && value !== undefined) || container === this._defaults) {
1157
+ if (get(this, name) === value && has(this, name)) break
1151
1158
 
1152
- let value = container[name]
1153
1159
  set(this, name, value)
1154
- value = unprocessed[name] || value
1155
1160
 
1156
- changes.push({ name, value, origin })
1161
+ changes.push({
1162
+ name,
1163
+ value: unprocessedValues[i][name] || value,
1164
+ origin: origins[i]
1165
+ })
1157
1166
 
1158
1167
  break
1159
1168
  }
@@ -93,6 +93,7 @@ exports.datadog = function datadog (lambdaHandler) {
93
93
  return res
94
94
  })
95
95
  }
96
+ clearTimeout(__lambdaTimeout)
96
97
  return result
97
98
  }
98
99
  }
@@ -2,4 +2,15 @@
2
2
 
3
3
  const { registerLambdaHook } = require('./runtime/ritm')
4
4
 
5
- registerLambdaHook()
5
+ /**
6
+ * It is safe to do it this way, since customers will never be expected to disable
7
+ * this specific instrumentation through the init config object.
8
+ */
9
+ const _DD_TRACE_DISABLED_INSTRUMENTATIONS = process.env.DD_TRACE_DISABLED_INSTRUMENTATIONS || ''
10
+ const _disabledInstrumentations = new Set(
11
+ _DD_TRACE_DISABLED_INSTRUMENTATIONS ? _DD_TRACE_DISABLED_INSTRUMENTATIONS.split(',') : []
12
+ )
13
+
14
+ if (!_disabledInstrumentations.has('lambda')) {
15
+ registerLambdaHook()
16
+ }
@@ -236,8 +236,7 @@ class TextMapPropagator {
236
236
  }
237
237
 
238
238
  _hasParentIdInTags (spanContext) {
239
- return tags.DD_PARENT_ID in spanContext._trace.tags &&
240
- spanContext._trace.tags[tags.DD_PARENT_ID] !== zeroTraceId
239
+ return tags.DD_PARENT_ID in spanContext._trace.tags
241
240
  }
242
241
 
243
242
  _updateParentIdFromDdHeaders (carrier, firstSpanContext) {
@@ -445,10 +444,6 @@ class TextMapPropagator {
445
444
  }
446
445
  })
447
446
 
448
- if (!spanContext._trace.tags[tags.DD_PARENT_ID]) {
449
- spanContext._trace.tags[tags.DD_PARENT_ID] = zeroTraceId
450
- }
451
-
452
447
  this._extractBaggageItems(carrier, spanContext)
453
448
  return spanContext
454
449
  }
@@ -95,9 +95,6 @@ const MOCHA_WORKER_TRACE_PAYLOAD_CODE = 80
95
95
  const EFD_STRING = "Retried by Datadog's Early Flake Detection"
96
96
  const EFD_TEST_NAME_REGEX = new RegExp(EFD_STRING + ' \\(#\\d+\\): ', 'g')
97
97
 
98
- // Flaky test retries
99
- const NUM_FAILED_TEST_RETRIES = 5
100
-
101
98
  module.exports = {
102
99
  TEST_CODE_OWNERS,
103
100
  TEST_FRAMEWORK,
@@ -170,8 +167,7 @@ module.exports = {
170
167
  TEST_BROWSER_DRIVER,
171
168
  TEST_BROWSER_DRIVER_VERSION,
172
169
  TEST_BROWSER_NAME,
173
- TEST_BROWSER_VERSION,
174
- NUM_FAILED_TEST_RETRIES
170
+ TEST_BROWSER_VERSION
175
171
  }
176
172
 
177
173
  // Returns pkg manager and its version, separated by '-', e.g. npm-8.15.0 or yarn-1.22.19
@@ -8,8 +8,8 @@ process.once('beforeExit', () => { profiler.stop() })
8
8
 
9
9
  module.exports = {
10
10
  start: config => {
11
- const { service, version, env, url, hostname, port, tags, repositoryUrl, commitSHA } = config
12
- const { enabled, sourceMap, exporters, heuristicsEnabled } = config.profiling
11
+ const { service, version, env, url, hostname, port, tags, repositoryUrl, commitSHA, injectionEnabled } = config
12
+ const { enabled, sourceMap, exporters } = config.profiling
13
13
  const logger = {
14
14
  debug: (message) => log.debug(message),
15
15
  info: (message) => log.info(message),
@@ -17,9 +17,17 @@ module.exports = {
17
17
  error: (message) => log.error(message)
18
18
  }
19
19
 
20
+ const libraryInjected = injectionEnabled.length > 0
21
+ let activation
22
+ if (enabled === 'auto') {
23
+ activation = 'auto'
24
+ } else if (enabled === 'true') {
25
+ activation = 'manual'
26
+ } else if (injectionEnabled.includes('profiler')) {
27
+ activation = 'injection'
28
+ } // else activation = undefined
29
+
20
30
  return profiler.start({
21
- enabled,
22
- heuristicsEnabled,
23
31
  service,
24
32
  version,
25
33
  env,
@@ -31,7 +39,9 @@ module.exports = {
31
39
  port,
32
40
  tags,
33
41
  repositoryUrl,
34
- commitSHA
42
+ commitSHA,
43
+ libraryInjected,
44
+ activation
35
45
  })
36
46
  },
37
47
 
@@ -23,7 +23,6 @@ class Config {
23
23
  DD_PROFILING_CODEHOTSPOTS_ENABLED,
24
24
  DD_PROFILING_CPU_ENABLED,
25
25
  DD_PROFILING_DEBUG_SOURCE_MAPS,
26
- DD_PROFILING_ENABLED,
27
26
  DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
28
27
  DD_PROFILING_EXPERIMENTAL_CODEHOTSPOTS_ENABLED,
29
28
  DD_PROFILING_EXPERIMENTAL_CPU_ENABLED,
@@ -49,7 +48,6 @@ class Config {
49
48
  DD_VERSION
50
49
  } = process.env
51
50
 
52
- const enabled = isTrue(coalesce(options.enabled, DD_PROFILING_ENABLED, true))
53
51
  const env = coalesce(options.env, DD_ENV)
54
52
  const service = options.service || DD_SERVICE || 'node'
55
53
  const host = os.hostname()
@@ -64,8 +62,6 @@ class Config {
64
62
  const pprofPrefix = coalesce(options.pprofPrefix,
65
63
  DD_PROFILING_PPROF_PREFIX, '')
66
64
 
67
- this.enabled = enabled
68
- this.heuristicsEnabled = options.heuristicsEnabled
69
65
  this.service = service
70
66
  this.env = env
71
67
  this.host = host
@@ -129,6 +125,8 @@ class Config {
129
125
  port
130
126
  })))
131
127
 
128
+ this.libraryInjected = options.libraryInjected
129
+ this.activation = options.activation
132
130
  this.exporters = ensureExporters(options.exporters || [
133
131
  new AgentExporter(this)
134
132
  ], this)
@@ -16,10 +16,22 @@ function exporterFromURL (url) {
16
16
  if (url.protocol === 'file:') {
17
17
  return new FileExporter({ pprofPrefix: fileURLToPath(url) })
18
18
  } else {
19
+ const injectionEnabled = (process.env.DD_INJECTION_ENABLED || '').split(',')
20
+ const libraryInjected = injectionEnabled.length > 0
21
+ const profilingEnabled = (process.env.DD_PROFILING_ENABLED || '').toLowerCase()
22
+ const activation = ['true', '1'].includes(profilingEnabled)
23
+ ? 'manual'
24
+ : profilingEnabled === 'auto'
25
+ ? 'auto'
26
+ : injectionEnabled.includes('profiling')
27
+ ? 'injection'
28
+ : 'unknown'
19
29
  return new AgentExporter({
20
30
  url,
21
31
  logger,
22
- uploadTimeout: timeoutMs
32
+ uploadTimeout: timeoutMs,
33
+ libraryInjected,
34
+ activation
23
35
  })
24
36
  }
25
37
  }
@@ -53,7 +53,7 @@ function computeRetries (uploadTimeout) {
53
53
  }
54
54
 
55
55
  class AgentExporter {
56
- constructor ({ url, logger, uploadTimeout, env, host, service, version } = {}) {
56
+ constructor ({ url, logger, uploadTimeout, env, host, service, version, libraryInjected, activation } = {}) {
57
57
  this._url = url
58
58
  this._logger = logger
59
59
 
@@ -65,6 +65,8 @@ class AgentExporter {
65
65
  this._host = host
66
66
  this._service = service
67
67
  this._appVersion = version
68
+ this._libraryInjected = !!libraryInjected
69
+ this._activation = activation || 'unknown'
68
70
  }
69
71
 
70
72
  export ({ profiles, start, end, tags }) {
@@ -105,6 +107,10 @@ class AgentExporter {
105
107
  kernel_version: os.version()
106
108
  },
107
109
  profiler: {
110
+ activation: this._activation,
111
+ ssi: {
112
+ mechanism: this._libraryInjected ? 'injected_agent' : 'none'
113
+ },
108
114
  version
109
115
  },
110
116
  runtime: {
@@ -5,7 +5,6 @@ const { Config } = require('./config')
5
5
  const { snapshotKinds } = require('./constants')
6
6
  const { threadNamePrefix } = require('./profilers/shared')
7
7
  const dc = require('dc-polyfill')
8
- const telemetryLog = dc.channel('datadog:telemetry:log')
9
8
 
10
9
  const profileSubmittedChannel = dc.channel('datadog:profiling:profile-submitted')
11
10
 
@@ -20,13 +19,6 @@ function logError (logger, err) {
20
19
  if (logger) {
21
20
  logger.error(err)
22
21
  }
23
- if (telemetryLog.hasSubscribers) {
24
- telemetryLog.publish({
25
- message: err.message,
26
- level: 'ERROR',
27
- stack_trace: err.stack
28
- })
29
- }
30
22
  }
31
23
 
32
24
  class Profiler extends EventEmitter {
@@ -55,7 +47,6 @@ class Profiler extends EventEmitter {
55
47
  if (this._enabled) return true
56
48
 
57
49
  const config = this._config = new Config(options)
58
- if (!config.enabled && !config.heuristicsEnabled) return false
59
50
 
60
51
  this._logger = config.logger
61
52
  this._enabled = true
@@ -7,40 +7,6 @@ const dc = require('dc-polyfill')
7
7
  // If the process lives for at least 30 seconds, it's considered long-lived
8
8
  const DEFAULT_LONG_LIVED_THRESHOLD = 30000
9
9
 
10
- const EnablementChoice = {
11
- MANUALLY_ENABLED: Symbol('SSITelemetry.EnablementChoice.MANUALLY_ENABLED'),
12
- SSI_ENABLED: Symbol('SSITelemetry.EnablementChoice.SSI_ENABLED'),
13
- SSI_NOT_ENABLED: Symbol('SSITelemetry.EnablementChoice.SSI_NOT_ENABLED'),
14
- DISABLED: Symbol('SSITelemetry.EnablementChoice.DISABLED')
15
- }
16
- Object.freeze(EnablementChoice)
17
-
18
- function getEnablementChoiceFromConfig (config) {
19
- if (config.ssi === false || config.enabled === false) {
20
- return EnablementChoice.DISABLED
21
- } else if (config.heuristicsEnabled === true) {
22
- return EnablementChoice.SSI_ENABLED
23
- } else if (config.enabled === true) {
24
- return EnablementChoice.MANUALLY_ENABLED
25
- } else {
26
- return EnablementChoice.SSI_NOT_ENABLED
27
- }
28
- }
29
-
30
- function enablementChoiceToTagValue (enablementChoice) {
31
- switch (enablementChoice) {
32
- case EnablementChoice.MANUALLY_ENABLED:
33
- return 'manually_enabled'
34
- case EnablementChoice.SSI_ENABLED:
35
- return 'ssi_enabled'
36
- case EnablementChoice.SSI_NOT_ENABLED:
37
- return 'not_enabled'
38
- case EnablementChoice.DISABLED:
39
- // Can't emit this one as a tag
40
- throw new Error('Invalid enablement choice')
41
- }
42
- }
43
-
44
10
  /**
45
11
  * This class embodies the SSI profiler-triggering heuristics and also emits telemetry metrics about
46
12
  * the profiler behavior under SSI. It emits the following metrics:
@@ -56,9 +22,23 @@ function enablementChoiceToTagValue (enablementChoice) {
56
22
  */
57
23
  class SSIHeuristics {
58
24
  constructor (config) {
59
- this.enablementChoice = getEnablementChoiceFromConfig(config)
25
+ const injectionIncludesProfiler = config.injectionEnabled.includes('profiler')
26
+ this._heuristicsActive = injectionIncludesProfiler || config.profiling.enabled === 'auto'
27
+ this._emitsTelemetry = config.injectionEnabled.length > 0 && config.profiling.enabled !== 'false'
28
+
29
+ if (this._emitsTelemetry) {
30
+ if (config.profiling.enabled === 'true') {
31
+ this.enablementChoice = 'manually_enabled'
32
+ } else if (injectionIncludesProfiler) {
33
+ this.enablementChoice = 'ssi_enabled'
34
+ } else if (config.profiling.enabled === 'auto') {
35
+ this.enablementChoice = 'auto_enabled'
36
+ } else {
37
+ this.enablementChoice = 'ssi_not_enabled'
38
+ }
39
+ }
60
40
 
61
- const longLivedThreshold = config.longLivedThreshold || DEFAULT_LONG_LIVED_THRESHOLD
41
+ const longLivedThreshold = config.profiling.longLivedThreshold || DEFAULT_LONG_LIVED_THRESHOLD
62
42
  if (typeof longLivedThreshold !== 'number' || longLivedThreshold <= 0) {
63
43
  throw new Error('Long-lived threshold must be a positive number')
64
44
  }
@@ -69,12 +49,16 @@ class SSIHeuristics {
69
49
  this.shortLived = true
70
50
  }
71
51
 
72
- enabled () {
73
- return this.enablementChoice !== EnablementChoice.DISABLED
52
+ get emitsTelemetry () {
53
+ return this._emitsTelemetry
54
+ }
55
+
56
+ get heuristicsActive () {
57
+ return this._heuristicsActive
74
58
  }
75
59
 
76
60
  start () {
77
- if (this.enabled()) {
61
+ if (this.heuristicsActive || this.emitsTelemetry) {
78
62
  // Used to determine short-livedness of the process. We could use the process start time as the
79
63
  // reference point, but the tracer initialization point is more relevant, as we couldn't be
80
64
  // collecting profiles earlier anyway. The difference is not particularly significant if the
@@ -85,13 +69,17 @@ class SSIHeuristics {
85
69
  }, this.longLivedThreshold).unref()
86
70
 
87
71
  this._onSpanCreated = this._onSpanCreated.bind(this)
88
- this._onProfileSubmitted = this._onProfileSubmitted.bind(this)
89
- this._onMockProfileSubmitted = this._onMockProfileSubmitted.bind(this)
90
- this._onAppClosing = this._onAppClosing.bind(this)
91
-
92
72
  dc.subscribe('dd-trace:span:start', this._onSpanCreated)
93
- dc.subscribe('datadog:profiling:profile-submitted', this._onProfileSubmitted)
94
- dc.subscribe('datadog:profiling:mock-profile-submitted', this._onMockProfileSubmitted)
73
+
74
+ if (this.emitsTelemetry) {
75
+ this._onProfileSubmitted = this._onProfileSubmitted.bind(this)
76
+ this._onMockProfileSubmitted = this._onMockProfileSubmitted.bind(this)
77
+
78
+ dc.subscribe('datadog:profiling:profile-submitted', this._onProfileSubmitted)
79
+ dc.subscribe('datadog:profiling:mock-profile-submitted', this._onMockProfileSubmitted)
80
+ }
81
+
82
+ this._onAppClosing = this._onAppClosing.bind(this)
95
83
  dc.subscribe('datadog:telemetry:app-closing', this._onAppClosing)
96
84
  }
97
85
  }
@@ -152,7 +140,7 @@ class SSIHeuristics {
152
140
 
153
141
  const tags = [
154
142
  'installation:ssi',
155
- `enablement_choice:${enablementChoiceToTagValue(this.enablementChoice)}`,
143
+ `enablement_choice:${this.enablementChoice}`,
156
144
  `has_sent_profiles:${this.hasSentProfiles}`,
157
145
  `heuristic_hypothetical_decision:${decision.join('_')}`
158
146
  ]
@@ -163,9 +151,9 @@ class SSIHeuristics {
163
151
  if (
164
152
  !this._emittedRuntimeId &&
165
153
  decision[0] === 'triggered' &&
166
- // When enablement choice is SSI_ENABLED, hasSentProfiles can transition from false to true when the
154
+ // When heuristics are active, hasSentProfiles can transition from false to true when the
167
155
  // profiler gets started and the first profile is submitted, so we have to wait for it.
168
- (this.enablementChoice !== EnablementChoice.SSI_ENABLED || this.hasSentProfiles)
156
+ (!this.heuristicsActive || this.hasSentProfiles)
169
157
  ) {
170
158
  // Tags won't change anymore, so we can emit the runtime ID metric now.
171
159
  this._emittedRuntimeId = true
@@ -174,17 +162,20 @@ class SSIHeuristics {
174
162
  }
175
163
 
176
164
  _onAppClosing () {
177
- this._ensureProfileMetrics()
178
- // Last ditch effort to emit a runtime ID count metric
179
- if (!this._emittedRuntimeId) {
180
- this._emittedRuntimeId = true
181
- this._runtimeIdCount.inc()
165
+ if (this.emitsTelemetry) {
166
+ this._ensureProfileMetrics()
167
+ // Last ditch effort to emit a runtime ID count metric
168
+ if (!this._emittedRuntimeId) {
169
+ this._emittedRuntimeId = true
170
+ this._runtimeIdCount.inc()
171
+ }
172
+ // So we have the metrics in the final state
173
+ this._profileCount.inc(0)
174
+
175
+ dc.unsubscribe('datadog:profiling:profile-submitted', this._onProfileSubmitted)
176
+ dc.unsubscribe('datadog:profiling:mock-profile-submitted', this._onMockProfileSubmitted)
182
177
  }
183
- // So we have the metrics in the final state
184
- this._profileCount.inc(0)
185
178
 
186
- dc.unsubscribe('datadog:profiling:profile-submitted', this._onProfileSubmitted)
187
- dc.unsubscribe('datadog:profiling:mock-profile-submitted', this._onMockProfileSubmitted)
188
179
  dc.unsubscribe('datadog:telemetry:app-closing', this._onAppClosing)
189
180
  if (this.noSpan) {
190
181
  dc.unsubscribe('dd-trace:span:start', this._onSpanCreated)
@@ -192,4 +183,4 @@ class SSIHeuristics {
192
183
  }
193
184
  }
194
185
 
195
- module.exports = { SSIHeuristics, EnablementChoice }
186
+ module.exports = { SSIHeuristics }
@@ -14,7 +14,6 @@ const dogstatsd = require('./dogstatsd')
14
14
  const NoopDogStatsDClient = require('./noop/dogstatsd')
15
15
  const spanleak = require('./spanleak')
16
16
  const { SSIHeuristics } = require('./profiling/ssi-heuristics')
17
- const telemetryLog = require('dc-polyfill').channel('datadog:telemetry:log')
18
17
  const appsecStandalone = require('./appsec/standalone')
19
18
 
20
19
  class LazyModule {
@@ -117,24 +116,32 @@ class Tracer extends NoopProxy {
117
116
  require('./serverless').maybeStartServerlessMiniAgent(config)
118
117
  }
119
118
 
120
- const ssiHeuristics = new SSIHeuristics(config.profiling)
121
- ssiHeuristics.start()
122
- if (config.profiling.enabled) {
123
- this._profilerStarted = this._startProfiler(config)
124
- } else if (config.profiling.ssi) {
125
- const mockProfiler = require('./profiling/ssi-telemetry-mock-profiler')
126
- mockProfiler.start(config)
119
+ if (config.profiling.enabled !== 'false') {
120
+ const ssiHeuristics = new SSIHeuristics(config)
121
+ ssiHeuristics.start()
122
+ let mockProfiler = null
123
+ if (config.profiling.enabled === 'true') {
124
+ this._profilerStarted = this._startProfiler(config)
125
+ } else if (ssiHeuristics.emitsTelemetry) {
126
+ // Start a mock profiler that emits mock profile-submitted events for the telemetry.
127
+ // It will be stopped if the real profiler is started by the heuristics.
128
+ mockProfiler = require('./profiling/ssi-telemetry-mock-profiler')
129
+ mockProfiler.start(config)
130
+ }
127
131
 
128
- if (config.profiling.heuristicsEnabled) {
132
+ if (ssiHeuristics.heuristicsActive) {
129
133
  ssiHeuristics.onTriggered(() => {
130
- mockProfiler.stop()
134
+ if (mockProfiler) {
135
+ mockProfiler.stop()
136
+ }
131
137
  this._startProfiler(config)
132
- ssiHeuristics.onTriggered()
138
+ ssiHeuristics.onTriggered() // deregister this callback
133
139
  })
134
140
  }
135
- }
136
- if (!this._profilerStarted) {
137
- this._profilerStarted = Promise.resolve(false)
141
+
142
+ if (!this._profilerStarted) {
143
+ this._profilerStarted = Promise.resolve(false)
144
+ }
138
145
  }
139
146
 
140
147
  if (config.runtimeMetrics) {
@@ -163,13 +170,6 @@ class Tracer extends NoopProxy {
163
170
  return require('./profiler').start(config)
164
171
  } catch (e) {
165
172
  log.error(e)
166
- if (telemetryLog.hasSubscribers) {
167
- telemetryLog.publish({
168
- message: e.message,
169
- level: 'ERROR',
170
- stack_trace: e.stack
171
- })
172
- }
173
173
  }
174
174
  }
175
175