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.
- package/package.json +3 -3
- package/packages/datadog-instrumentations/src/aerospike.js +1 -1
- package/packages/datadog-instrumentations/src/apollo-server.js +1 -1
- package/packages/datadog-instrumentations/src/aws-sdk.js +4 -4
- package/packages/datadog-instrumentations/src/body-parser.js +4 -4
- package/packages/datadog-instrumentations/src/cassandra-driver.js +2 -2
- package/packages/datadog-instrumentations/src/child_process.js +2 -2
- package/packages/datadog-instrumentations/src/connect.js +4 -4
- package/packages/datadog-instrumentations/src/cookie-parser.js +4 -4
- package/packages/datadog-instrumentations/src/couchbase.js +12 -12
- package/packages/datadog-instrumentations/src/cucumber.js +6 -5
- package/packages/datadog-instrumentations/src/dns.js +10 -10
- package/packages/datadog-instrumentations/src/elasticsearch.js +4 -4
- package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +3 -3
- package/packages/datadog-instrumentations/src/express.js +4 -4
- package/packages/datadog-instrumentations/src/fastify.js +6 -6
- package/packages/datadog-instrumentations/src/fetch.js +1 -1
- package/packages/datadog-instrumentations/src/find-my-way.js +2 -2
- package/packages/datadog-instrumentations/src/fs.js +2 -2
- package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +2 -2
- package/packages/datadog-instrumentations/src/grpc/client.js +4 -6
- package/packages/datadog-instrumentations/src/grpc/server.js +2 -2
- package/packages/datadog-instrumentations/src/hapi.js +10 -13
- package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
- package/packages/datadog-instrumentations/src/http/client.js +3 -3
- package/packages/datadog-instrumentations/src/jest.js +5 -4
- package/packages/datadog-instrumentations/src/knex.js +2 -2
- package/packages/datadog-instrumentations/src/koa.js +5 -5
- package/packages/datadog-instrumentations/src/ldapjs.js +1 -1
- package/packages/datadog-instrumentations/src/mariadb.js +8 -8
- package/packages/datadog-instrumentations/src/memcached.js +2 -2
- package/packages/datadog-instrumentations/src/microgateway-core.js +4 -4
- package/packages/datadog-instrumentations/src/mocha/common.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +1 -0
- package/packages/datadog-instrumentations/src/mocha/utils.js +2 -3
- package/packages/datadog-instrumentations/src/mocha.js +4 -0
- package/packages/datadog-instrumentations/src/moleculer/server.js +2 -2
- package/packages/datadog-instrumentations/src/mongodb-core.js +7 -7
- package/packages/datadog-instrumentations/src/mongoose.js +5 -6
- package/packages/datadog-instrumentations/src/mysql.js +3 -3
- package/packages/datadog-instrumentations/src/mysql2.js +6 -6
- package/packages/datadog-instrumentations/src/net.js +2 -2
- package/packages/datadog-instrumentations/src/next.js +5 -5
- package/packages/datadog-instrumentations/src/openai.js +58 -69
- package/packages/datadog-instrumentations/src/oracledb.js +8 -8
- package/packages/datadog-instrumentations/src/passport-http.js +1 -1
- package/packages/datadog-instrumentations/src/passport-local.js +1 -1
- package/packages/datadog-instrumentations/src/passport-utils.js +1 -1
- package/packages/datadog-instrumentations/src/pg.js +1 -1
- package/packages/datadog-instrumentations/src/pino.js +4 -4
- package/packages/datadog-instrumentations/src/playwright.js +6 -4
- package/packages/datadog-instrumentations/src/redis.js +2 -2
- package/packages/datadog-instrumentations/src/restify.js +4 -4
- package/packages/datadog-instrumentations/src/rhea.js +4 -4
- package/packages/datadog-instrumentations/src/router.js +5 -5
- package/packages/datadog-instrumentations/src/sharedb.js +2 -2
- package/packages/datadog-instrumentations/src/vitest.js +4 -3
- package/packages/datadog-instrumentations/src/winston.js +2 -3
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +5 -6
- package/packages/datadog-plugin-hapi/src/index.js +2 -2
- package/packages/datadog-plugin-jest/src/index.js +1 -0
- package/packages/datadog-plugin-openai/src/index.js +58 -47
- package/packages/datadog-shimmer/src/shimmer.js +144 -10
- package/packages/dd-trace/src/appsec/blocking.js +23 -17
- package/packages/dd-trace/src/appsec/graphql.js +3 -1
- package/packages/dd-trace/src/appsec/iast/iast-log.js +2 -1
- package/packages/dd-trace/src/appsec/remote_config/manager.js +4 -1
- package/packages/dd-trace/src/appsec/rule_manager.js +8 -0
- package/packages/dd-trace/src/appsec/telemetry.js +3 -3
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +2 -1
- package/packages/dd-trace/src/config.js +40 -31
- package/packages/dd-trace/src/lambda/handler.js +1 -0
- package/packages/dd-trace/src/lambda/index.js +12 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -6
- package/packages/dd-trace/src/plugins/util/test.js +1 -5
- package/packages/dd-trace/src/profiler.js +15 -5
- package/packages/dd-trace/src/profiling/config.js +2 -4
- package/packages/dd-trace/src/profiling/exporter_cli.js +13 -1
- package/packages/dd-trace/src/profiling/exporters/agent.js +7 -1
- package/packages/dd-trace/src/profiling/profiler.js +0 -9
- package/packages/dd-trace/src/profiling/ssi-heuristics.js +49 -58
- package/packages/dd-trace/src/proxy.js +21 -21
- package/packages/dd-trace/src/telemetry/index.js +23 -6
- 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('
|
|
93
|
+
appsecMetrics.count('rasp.rule.eval', tags).inc(1)
|
|
94
94
|
|
|
95
95
|
if (metrics.wafTimeout) {
|
|
96
|
-
appsecMetrics.count('
|
|
96
|
+
appsecMetrics.count('rasp.timeout', tags).inc(1)
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
if (metrics.ruleTriggered) {
|
|
100
|
-
appsecMetrics.count('
|
|
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
|
-
|
|
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 (
|
|
703
|
-
|
|
704
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1147
|
-
const unprocessed = unprocessedValues[i]
|
|
1154
|
+
const value = container[name]
|
|
1148
1155
|
|
|
1149
|
-
if ((
|
|
1150
|
-
if (get(this, name) ===
|
|
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({
|
|
1161
|
+
changes.push({
|
|
1162
|
+
name,
|
|
1163
|
+
value: unprocessedValues[i][name] || value,
|
|
1164
|
+
origin: origins[i]
|
|
1165
|
+
})
|
|
1157
1166
|
|
|
1158
1167
|
break
|
|
1159
1168
|
}
|
|
@@ -2,4 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
const { registerLambdaHook } = require('./runtime/ritm')
|
|
4
4
|
|
|
5
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
73
|
-
return this.
|
|
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.
|
|
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
|
-
|
|
94
|
-
|
|
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:${
|
|
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
|
|
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.
|
|
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.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
this._emittedRuntimeId
|
|
181
|
-
|
|
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
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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 (
|
|
132
|
+
if (ssiHeuristics.heuristicsActive) {
|
|
129
133
|
ssiHeuristics.onTriggered(() => {
|
|
130
|
-
mockProfiler
|
|
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
|
-
|
|
137
|
-
|
|
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
|
|