dd-trace 3.14.1 → 3.16.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/LICENSE-3rdparty.csv +2 -2
- package/README.md +9 -5
- package/ci/init.js +9 -1
- package/ext/exporters.d.ts +2 -1
- package/ext/exporters.js +2 -1
- package/index.d.ts +36 -3
- package/package.json +21 -19
- package/packages/datadog-instrumentations/src/cucumber.js +80 -3
- package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +100 -27
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/jest.js +35 -3
- package/packages/datadog-instrumentations/src/ldapjs.js +12 -2
- package/packages/datadog-instrumentations/src/mariadb.js +130 -11
- package/packages/datadog-instrumentations/src/mocha.js +30 -6
- package/packages/datadog-instrumentations/src/mongodb-core.js +8 -2
- package/packages/datadog-instrumentations/src/mongoose.js +1 -1
- package/packages/datadog-instrumentations/src/next.js +33 -4
- package/packages/datadog-instrumentations/src/playwright.js +42 -13
- package/packages/datadog-plugin-amqp10/src/consumer.js +1 -1
- package/packages/datadog-plugin-amqp10/src/index.js +1 -1
- package/packages/datadog-plugin-amqp10/src/producer.js +3 -2
- package/packages/datadog-plugin-amqplib/src/client.js +3 -2
- package/packages/datadog-plugin-amqplib/src/consumer.js +1 -1
- package/packages/datadog-plugin-amqplib/src/index.js +1 -1
- package/packages/datadog-plugin-amqplib/src/producer.js +3 -2
- package/packages/datadog-plugin-aws-sdk/src/base.js +7 -2
- package/packages/datadog-plugin-aws-sdk/src/index.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +2 -0
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +2 -0
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +2 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -0
- package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +2 -0
- package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +2 -0
- package/packages/datadog-plugin-aws-sdk/src/services/s3.js +2 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +2 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -0
- package/packages/datadog-plugin-bunyan/src/index.js +1 -1
- package/packages/datadog-plugin-cassandra-driver/src/index.js +3 -2
- package/packages/datadog-plugin-connect/src/index.js +1 -1
- package/packages/datadog-plugin-couchbase/src/index.js +1 -1
- package/packages/datadog-plugin-cucumber/src/index.js +33 -6
- package/packages/datadog-plugin-cypress/src/index.js +1 -1
- package/packages/datadog-plugin-cypress/src/plugin.js +40 -33
- package/packages/datadog-plugin-dns/src/index.js +1 -1
- package/packages/datadog-plugin-dns/src/lookup.js +1 -1
- package/packages/datadog-plugin-dns/src/lookup_service.js +1 -1
- package/packages/datadog-plugin-dns/src/resolve.js +1 -1
- package/packages/datadog-plugin-dns/src/reverse.js +1 -1
- package/packages/datadog-plugin-elasticsearch/src/index.js +1 -1
- package/packages/datadog-plugin-express/src/index.js +1 -1
- package/packages/datadog-plugin-fastify/src/index.js +1 -1
- package/packages/datadog-plugin-find-my-way/src/index.js +1 -1
- package/packages/datadog-plugin-fs/src/index.js +1 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +5 -5
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +1 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/index.js +1 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +7 -6
- package/packages/datadog-plugin-graphql/src/execute.js +1 -1
- package/packages/datadog-plugin-graphql/src/index.js +1 -1
- package/packages/datadog-plugin-graphql/src/parse.js +1 -1
- package/packages/datadog-plugin-graphql/src/resolve.js +1 -1
- package/packages/datadog-plugin-graphql/src/validate.js +1 -1
- package/packages/datadog-plugin-grpc/src/client.js +1 -1
- package/packages/datadog-plugin-grpc/src/index.js +1 -1
- package/packages/datadog-plugin-grpc/src/server.js +1 -1
- package/packages/datadog-plugin-hapi/src/index.js +1 -1
- package/packages/datadog-plugin-http/src/client.js +2 -2
- package/packages/datadog-plugin-http/src/index.js +1 -1
- package/packages/datadog-plugin-http/src/server.js +3 -3
- package/packages/datadog-plugin-http2/src/client.js +4 -3
- package/packages/datadog-plugin-http2/src/index.js +1 -1
- package/packages/datadog-plugin-http2/src/server.js +3 -3
- package/packages/datadog-plugin-ioredis/src/index.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +53 -19
- package/packages/datadog-plugin-kafkajs/src/consumer.js +1 -1
- package/packages/datadog-plugin-kafkajs/src/index.js +1 -1
- package/packages/datadog-plugin-kafkajs/src/producer.js +1 -1
- package/packages/datadog-plugin-koa/src/index.js +1 -1
- package/packages/datadog-plugin-mariadb/src/index.js +18 -1
- package/packages/datadog-plugin-memcached/src/index.js +3 -2
- package/packages/datadog-plugin-microgateway-core/src/index.js +1 -1
- package/packages/datadog-plugin-mocha/src/index.js +13 -9
- package/packages/datadog-plugin-moleculer/src/client.js +1 -1
- package/packages/datadog-plugin-moleculer/src/index.js +1 -1
- package/packages/datadog-plugin-moleculer/src/server.js +1 -1
- package/packages/datadog-plugin-mongodb-core/src/index.js +1 -1
- package/packages/datadog-plugin-mysql/src/index.js +3 -2
- package/packages/datadog-plugin-mysql2/src/index.js +1 -1
- package/packages/datadog-plugin-net/src/index.js +9 -75
- package/packages/datadog-plugin-net/src/ipc.js +1 -1
- package/packages/datadog-plugin-net/src/tcp.js +3 -2
- package/packages/datadog-plugin-next/src/index.js +3 -3
- package/packages/datadog-plugin-opensearch/src/index.js +1 -1
- package/packages/datadog-plugin-oracledb/src/index.js +3 -2
- package/packages/datadog-plugin-paperplane/src/index.js +1 -1
- package/packages/datadog-plugin-paperplane/src/logger.js +1 -1
- package/packages/datadog-plugin-paperplane/src/server.js +1 -1
- package/packages/datadog-plugin-pg/src/index.js +3 -2
- package/packages/datadog-plugin-pino/src/index.js +1 -1
- package/packages/datadog-plugin-playwright/src/index.js +5 -4
- package/packages/datadog-plugin-redis/src/index.js +3 -2
- package/packages/datadog-plugin-restify/src/index.js +1 -1
- package/packages/datadog-plugin-rhea/src/consumer.js +1 -1
- package/packages/datadog-plugin-rhea/src/index.js +1 -1
- package/packages/datadog-plugin-rhea/src/producer.js +3 -2
- package/packages/datadog-plugin-router/src/index.js +8 -8
- package/packages/datadog-plugin-sharedb/src/index.js +1 -1
- package/packages/datadog-plugin-tedious/src/index.js +3 -2
- package/packages/datadog-plugin-web/src/index.js +1 -1
- package/packages/datadog-plugin-winston/src/index.js +1 -1
- package/packages/dd-trace/src/appsec/addresses.js +3 -1
- package/packages/dd-trace/src/appsec/blocking.js +35 -9
- package/packages/dd-trace/src/appsec/gateway/engine/runner.js +2 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +2 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +2 -2
- package/packages/dd-trace/src/appsec/iast/iast-context.js +6 -2
- package/packages/dd-trace/src/appsec/iast/iast-log.js +111 -0
- package/packages/dd-trace/src/appsec/iast/index.js +10 -6
- package/packages/dd-trace/src/appsec/iast/path-line.js +3 -6
- package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +2 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +2 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/origin-types.js +2 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +2 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +9 -4
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +5 -3
- package/packages/dd-trace/src/appsec/iast/telemetry/log_collector.js +96 -0
- package/packages/dd-trace/src/appsec/iast/telemetry/logs.js +87 -0
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +27 -2
- package/packages/dd-trace/src/appsec/index.js +4 -4
- package/packages/dd-trace/src/appsec/recommended.json +76 -75
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
- package/packages/dd-trace/src/appsec/remote_config/index.js +3 -0
- package/packages/dd-trace/src/appsec/sdk/index.js +19 -1
- package/packages/dd-trace/src/appsec/sdk/noop.js +6 -0
- package/packages/dd-trace/src/appsec/sdk/set_user.js +30 -0
- package/packages/dd-trace/src/appsec/sdk/track_event.js +2 -2
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +73 -0
- package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +27 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +17 -9
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +14 -8
- package/packages/dd-trace/src/ci-visibility/exporters/jest-worker/index.js +33 -0
- package/packages/dd-trace/src/ci-visibility/exporters/jest-worker/writer.js +37 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +12 -4
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +12 -4
- package/packages/dd-trace/src/config.js +24 -5
- package/packages/dd-trace/src/constants.js +2 -1
- package/packages/dd-trace/src/datastreams/encoding.js +80 -0
- package/packages/dd-trace/src/exporter.js +7 -9
- package/packages/dd-trace/src/exporters/common/agents.js +42 -0
- package/packages/dd-trace/src/exporters/common/docker.js +4 -1
- package/packages/dd-trace/src/exporters/common/request.js +1 -4
- package/packages/dd-trace/src/lambda/handler.js +19 -12
- package/packages/dd-trace/src/log/writer.js +32 -24
- package/packages/dd-trace/src/metrics.js +18 -0
- package/packages/dd-trace/src/noop/proxy.js +2 -2
- package/packages/dd-trace/src/opentracing/span.js +5 -0
- package/packages/dd-trace/src/opentracing/span_context.js +1 -1
- package/packages/dd-trace/src/plugin_manager.js +7 -7
- package/packages/dd-trace/src/plugins/ci_plugin.js +20 -17
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/log_plugin.js +1 -1
- package/packages/dd-trace/src/plugins/outgoing.js +2 -1
- package/packages/dd-trace/src/plugins/tracing.js +1 -1
- package/packages/dd-trace/src/plugins/util/ci.js +12 -0
- package/packages/dd-trace/src/plugins/util/exec.js +2 -2
- package/packages/dd-trace/src/plugins/util/git.js +16 -1
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +23 -27
- package/packages/dd-trace/src/plugins/util/test.js +26 -7
- package/packages/dd-trace/src/profiler.js +3 -0
- package/packages/dd-trace/src/profiling/config.js +92 -20
- package/packages/dd-trace/src/profiling/constants.js +16 -0
- package/packages/dd-trace/src/profiling/exporter_cli.js +62 -0
- package/packages/dd-trace/src/profiling/exporters/agent.js +2 -1
- package/packages/dd-trace/src/profiling/exporters/file.js +13 -2
- package/packages/dd-trace/src/profiling/profiler.js +42 -12
- package/packages/dd-trace/src/profiling/profilers/space.js +21 -1
- package/packages/dd-trace/src/profiling/profilers/wall.js +1 -0
- package/packages/dd-trace/src/proxy.js +1 -1
- package/packages/dd-trace/src/span_processor.js +1 -1
- package/packages/dd-trace/src/span_sampler.js +71 -54
- package/packages/dd-trace/src/startup-log.js +3 -6
- package/packages/dd-trace/src/telemetry/index.js +16 -2
- package/packages/dd-trace/src/tracer.js +0 -16
- package/packages/dd-trace/src/util.js +10 -1
- package/scripts/install_plugin_modules.js +5 -1
- package/scripts/junit_report.js +0 -25
- package/scripts/tdd.js +0 -34
|
@@ -2,48 +2,59 @@
|
|
|
2
2
|
|
|
3
3
|
const coalesce = require('koalas')
|
|
4
4
|
const os = require('os')
|
|
5
|
-
const
|
|
5
|
+
const path = require('path')
|
|
6
|
+
const { URL, format, pathToFileURL } = require('url')
|
|
6
7
|
const { AgentExporter } = require('./exporters/agent')
|
|
7
8
|
const { FileExporter } = require('./exporters/file')
|
|
8
9
|
const { ConsoleLogger } = require('./loggers/console')
|
|
9
10
|
const CpuProfiler = require('./profilers/cpu')
|
|
10
11
|
const WallProfiler = require('./profilers/wall')
|
|
11
12
|
const SpaceProfiler = require('./profilers/space')
|
|
13
|
+
const { oomExportStrategies, snapshotKinds } = require('./constants')
|
|
12
14
|
const { tagger } = require('./tagger')
|
|
13
|
-
|
|
14
|
-
const {
|
|
15
|
-
DD_PROFILING_ENABLED,
|
|
16
|
-
DD_PROFILING_PROFILERS,
|
|
17
|
-
DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
|
|
18
|
-
DD_ENV,
|
|
19
|
-
DD_TAGS,
|
|
20
|
-
DD_SERVICE,
|
|
21
|
-
DD_VERSION,
|
|
22
|
-
DD_TRACE_AGENT_URL,
|
|
23
|
-
DD_AGENT_HOST,
|
|
24
|
-
DD_TRACE_AGENT_PORT,
|
|
25
|
-
DD_PROFILING_UPLOAD_TIMEOUT,
|
|
26
|
-
DD_PROFILING_SOURCE_MAP
|
|
27
|
-
} = process.env
|
|
15
|
+
const { isTrue } = require('../util')
|
|
28
16
|
|
|
29
17
|
class Config {
|
|
30
18
|
constructor (options = {}) {
|
|
31
|
-
const
|
|
19
|
+
const {
|
|
20
|
+
DD_PROFILING_ENABLED,
|
|
21
|
+
DD_PROFILING_PROFILERS,
|
|
22
|
+
DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
|
|
23
|
+
DD_ENV,
|
|
24
|
+
DD_TAGS,
|
|
25
|
+
DD_SERVICE,
|
|
26
|
+
DD_VERSION,
|
|
27
|
+
DD_TRACE_AGENT_URL,
|
|
28
|
+
DD_AGENT_HOST,
|
|
29
|
+
DD_TRACE_AGENT_PORT,
|
|
30
|
+
DD_PROFILING_UPLOAD_TIMEOUT,
|
|
31
|
+
DD_PROFILING_SOURCE_MAP,
|
|
32
|
+
DD_PROFILING_UPLOAD_PERIOD,
|
|
33
|
+
DD_PROFILING_PPROF_PREFIX,
|
|
34
|
+
DD_PROFILING_EXPERIMENTAL_OOM_MONITORING_ENABLED,
|
|
35
|
+
DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE,
|
|
36
|
+
DD_PROFILING_EXPERIMENTAL_OOM_MAX_HEAP_EXTENSION_COUNT,
|
|
37
|
+
DD_PROFILING_EXPERIMENTAL_OOM_EXPORT_STRATEGIES
|
|
38
|
+
} = process.env
|
|
39
|
+
|
|
40
|
+
const enabled = isTrue(coalesce(options.enabled, DD_PROFILING_ENABLED, true))
|
|
32
41
|
const env = coalesce(options.env, DD_ENV)
|
|
33
42
|
const service = options.service || DD_SERVICE || 'node'
|
|
34
43
|
const host = os.hostname()
|
|
35
44
|
const version = coalesce(options.version, DD_VERSION)
|
|
36
45
|
const functionname = process.env.AWS_LAMBDA_FUNCTION_NAME
|
|
37
46
|
// Must be longer than one minute so pad with five seconds
|
|
38
|
-
const flushInterval = coalesce(options.interval, 65 * 1000)
|
|
47
|
+
const flushInterval = coalesce(options.interval, Number(DD_PROFILING_UPLOAD_PERIOD) * 1000, 65 * 1000)
|
|
39
48
|
const uploadTimeout = coalesce(options.uploadTimeout,
|
|
40
|
-
DD_PROFILING_UPLOAD_TIMEOUT, 60 * 1000)
|
|
49
|
+
Number(DD_PROFILING_UPLOAD_TIMEOUT), 60 * 1000)
|
|
41
50
|
const sourceMap = coalesce(options.sourceMap,
|
|
42
51
|
DD_PROFILING_SOURCE_MAP, true)
|
|
43
52
|
const endpointCollection = coalesce(options.endpointCollection,
|
|
44
53
|
DD_PROFILING_ENDPOINT_COLLECTION_ENABLED, false)
|
|
54
|
+
const pprofPrefix = coalesce(options.pprofPrefix,
|
|
55
|
+
DD_PROFILING_PPROF_PREFIX)
|
|
45
56
|
|
|
46
|
-
this.enabled =
|
|
57
|
+
this.enabled = enabled
|
|
47
58
|
this.service = service
|
|
48
59
|
this.env = env
|
|
49
60
|
this.host = host
|
|
@@ -60,6 +71,7 @@ class Config {
|
|
|
60
71
|
this.uploadTimeout = uploadTimeout
|
|
61
72
|
this.sourceMap = sourceMap
|
|
62
73
|
this.endpointCollection = endpointCollection
|
|
74
|
+
this.pprofPrefix = pprofPrefix
|
|
63
75
|
|
|
64
76
|
const hostname = coalesce(options.hostname, DD_AGENT_HOST) || 'localhost'
|
|
65
77
|
const port = coalesce(options.port, DD_TRACE_AGENT_PORT) || 8126
|
|
@@ -73,6 +85,23 @@ class Config {
|
|
|
73
85
|
new AgentExporter(this)
|
|
74
86
|
], this)
|
|
75
87
|
|
|
88
|
+
const oomMonitoringEnabled = isTrue(coalesce(options.oomMonitoring,
|
|
89
|
+
DD_PROFILING_EXPERIMENTAL_OOM_MONITORING_ENABLED, false))
|
|
90
|
+
const heapLimitExtensionSize = coalesce(options.oomHeapLimitExtensionSize,
|
|
91
|
+
Number(DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE), 0)
|
|
92
|
+
const maxHeapExtensionCount = coalesce(options.oomMaxHeapExtensionCount,
|
|
93
|
+
Number(DD_PROFILING_EXPERIMENTAL_OOM_MAX_HEAP_EXTENSION_COUNT), 0)
|
|
94
|
+
const exportStrategies = ensureOOMExportStrategies(coalesce(options.oomExportStrategies,
|
|
95
|
+
DD_PROFILING_EXPERIMENTAL_OOM_EXPORT_STRATEGIES), this)
|
|
96
|
+
const exportCommand = oomMonitoringEnabled ? buildExportCommand(this) : undefined
|
|
97
|
+
this.oomMonitoring = {
|
|
98
|
+
enabled: oomMonitoringEnabled,
|
|
99
|
+
heapLimitExtensionSize,
|
|
100
|
+
maxHeapExtensionCount,
|
|
101
|
+
exportStrategies,
|
|
102
|
+
exportCommand
|
|
103
|
+
}
|
|
104
|
+
|
|
76
105
|
const profilers = coalesce(options.profilers, DD_PROFILING_PROFILERS, [
|
|
77
106
|
new WallProfiler(this),
|
|
78
107
|
new SpaceProfiler(this)
|
|
@@ -84,6 +113,33 @@ class Config {
|
|
|
84
113
|
|
|
85
114
|
module.exports = { Config }
|
|
86
115
|
|
|
116
|
+
function getExportStrategy (name, options) {
|
|
117
|
+
const strategy = Object.values(oomExportStrategies).find(value => value === name)
|
|
118
|
+
if (strategy === undefined) {
|
|
119
|
+
options.logger.error(`Unknown oom export strategy "${name}"`)
|
|
120
|
+
}
|
|
121
|
+
return strategy
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function ensureOOMExportStrategies (strategies, options) {
|
|
125
|
+
if (!strategies) {
|
|
126
|
+
return []
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (typeof strategies === 'string') {
|
|
130
|
+
strategies = strategies.split(',')
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
for (let i = 0; i < strategies.length; i++) {
|
|
134
|
+
const strategy = strategies[i]
|
|
135
|
+
if (typeof strategy === 'string') {
|
|
136
|
+
strategies[i] = getExportStrategy(strategy, options)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return [ ...new Set(strategies) ]
|
|
141
|
+
}
|
|
142
|
+
|
|
87
143
|
function getExporter (name, options) {
|
|
88
144
|
switch (name) {
|
|
89
145
|
case 'agent':
|
|
@@ -149,3 +205,19 @@ function ensureLogger (logger) {
|
|
|
149
205
|
|
|
150
206
|
return logger
|
|
151
207
|
}
|
|
208
|
+
|
|
209
|
+
function buildExportCommand (options) {
|
|
210
|
+
const tags = [...Object.entries(options.tags),
|
|
211
|
+
['snapshot', snapshotKinds.ON_OUT_OF_MEMORY]].map(([key, value]) => `${key}:${value}`).join(',')
|
|
212
|
+
const urls = []
|
|
213
|
+
for (const exporter of options.exporters) {
|
|
214
|
+
if (exporter instanceof AgentExporter) {
|
|
215
|
+
urls.push(options.url.toString())
|
|
216
|
+
} else if (exporter instanceof FileExporter) {
|
|
217
|
+
urls.push(pathToFileURL(options.pprofPrefix).toString())
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return [process.execPath,
|
|
221
|
+
path.join(__dirname, 'exporter_cli.js'),
|
|
222
|
+
urls.join(','), tags, 'space']
|
|
223
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const snapshotKinds = Object.freeze({
|
|
4
|
+
PERIODIC: 'periodic',
|
|
5
|
+
ON_SHUTDOWN: 'on_shutdown',
|
|
6
|
+
ON_OUT_OF_MEMORY: 'on_oom'
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
const oomExportStrategies = Object.freeze({
|
|
10
|
+
PROCESS: 'process',
|
|
11
|
+
ASYNC_CALLBACK: 'async',
|
|
12
|
+
INTERRUPT_CALLBACK: 'interrupt',
|
|
13
|
+
LOGS: 'logs'
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
module.exports = { snapshotKinds, oomExportStrategies }
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { AgentExporter } = require('./exporters/agent')
|
|
4
|
+
const { FileExporter } = require('./exporters/file')
|
|
5
|
+
|
|
6
|
+
const { SourceMapper, heap, encode } = require('@datadog/pprof')
|
|
7
|
+
const { ConsoleLogger } = require('./loggers/console')
|
|
8
|
+
const { tagger } = require('./tagger')
|
|
9
|
+
const fs = require('fs')
|
|
10
|
+
const { fileURLToPath } = require('url')
|
|
11
|
+
|
|
12
|
+
const logger = new ConsoleLogger()
|
|
13
|
+
const timeoutMs = 10 * 1000
|
|
14
|
+
|
|
15
|
+
function exporterFromURL (url) {
|
|
16
|
+
if (url.protocol === 'file:') {
|
|
17
|
+
return new FileExporter({ pprofPrefix: fileURLToPath(url) })
|
|
18
|
+
} else {
|
|
19
|
+
return new AgentExporter({
|
|
20
|
+
url,
|
|
21
|
+
logger,
|
|
22
|
+
uploadTimeout: timeoutMs
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function exportProfile (urls, tags, profileType, profile) {
|
|
28
|
+
let mapper
|
|
29
|
+
try {
|
|
30
|
+
mapper = await SourceMapper.create([process.cwd()])
|
|
31
|
+
} catch (err) {
|
|
32
|
+
logger.error(err)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const encodedProfile = await encode(heap.convertProfile(profile, undefined, mapper))
|
|
36
|
+
const start = new Date()
|
|
37
|
+
for (const url of urls) {
|
|
38
|
+
const exporter = exporterFromURL(url)
|
|
39
|
+
|
|
40
|
+
await exporter.export({
|
|
41
|
+
profiles: {
|
|
42
|
+
[profileType]: encodedProfile
|
|
43
|
+
},
|
|
44
|
+
start,
|
|
45
|
+
end: start,
|
|
46
|
+
tags
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Expected command line arguments are:
|
|
52
|
+
* - Comma separated list of URLs (eg. "http://127.0.0.1:8126/,file:///tmp/foo.pprof")
|
|
53
|
+
* - Tags (eg. "service:nodejs_oom_test,version:1.0.0")
|
|
54
|
+
* - Profiletype (eg. space,wall,cpu)
|
|
55
|
+
* - JSON profile filepath
|
|
56
|
+
**/
|
|
57
|
+
const urls = process.argv[2].split(',').map(s => new URL(s))
|
|
58
|
+
const tags = tagger.parse(process.argv[3])
|
|
59
|
+
const profileType = process.argv[4]
|
|
60
|
+
const profile = JSON.parse(fs.readFileSync(process.argv[5]))
|
|
61
|
+
|
|
62
|
+
exportProfile(urls, tags, profileType, profile)
|
|
@@ -4,11 +4,22 @@ const fs = require('fs')
|
|
|
4
4
|
const { promisify } = require('util')
|
|
5
5
|
const writeFile = promisify(fs.writeFile)
|
|
6
6
|
|
|
7
|
+
function formatDateTime (t) {
|
|
8
|
+
const pad = (n) => String(n).padStart(2, '0')
|
|
9
|
+
return `${t.getUTCFullYear()}${pad(t.getUTCMonth() + 1)}${pad(t.getUTCDate())}` +
|
|
10
|
+
`T${pad(t.getUTCHours())}${pad(t.getUTCMinutes())}${pad(t.getUTCSeconds())}Z`
|
|
11
|
+
}
|
|
12
|
+
|
|
7
13
|
class FileExporter {
|
|
8
|
-
|
|
14
|
+
constructor ({ pprofPrefix } = {}) {
|
|
15
|
+
this._pprofPrefix = pprofPrefix || ''
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export ({ profiles, end }) {
|
|
9
19
|
const types = Object.keys(profiles)
|
|
20
|
+
const dateStr = formatDateTime(end)
|
|
10
21
|
const tasks = types.map(type => {
|
|
11
|
-
return writeFile(`${type}.
|
|
22
|
+
return writeFile(`${this._pprofPrefix}${type}_${dateStr}.pprof`, profiles[type])
|
|
12
23
|
})
|
|
13
24
|
|
|
14
25
|
return Promise.all(tasks)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { EventEmitter } = require('events')
|
|
4
4
|
const { Config } = require('./config')
|
|
5
|
+
const { snapshotKinds } = require('./constants')
|
|
5
6
|
|
|
6
7
|
function maybeSourceMap (sourceMap) {
|
|
7
8
|
if (!sourceMap) return
|
|
@@ -49,22 +50,42 @@ class Profiler extends EventEmitter {
|
|
|
49
50
|
try {
|
|
50
51
|
for (const profiler of config.profilers) {
|
|
51
52
|
// TODO: move this out of Profiler when restoring sourcemap support
|
|
52
|
-
profiler.start({
|
|
53
|
+
profiler.start({
|
|
54
|
+
mapper,
|
|
55
|
+
nearOOMCallback: this._nearOOMExport.bind(this)
|
|
56
|
+
})
|
|
53
57
|
this._logger.debug(`Started ${profiler.type} profiler`)
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
this._capture(this._timeoutInterval)
|
|
57
61
|
} catch (e) {
|
|
58
62
|
this._logger.error(e)
|
|
59
|
-
this.
|
|
63
|
+
this._stop()
|
|
60
64
|
}
|
|
61
65
|
}
|
|
62
66
|
|
|
67
|
+
_nearOOMExport (profileType, encodedProfile) {
|
|
68
|
+
const start = this._lastStart
|
|
69
|
+
const end = new Date()
|
|
70
|
+
this._submit({
|
|
71
|
+
[profileType]: encodedProfile
|
|
72
|
+
}, start, end, snapshotKinds.ON_OUT_OF_MEMORY)
|
|
73
|
+
}
|
|
74
|
+
|
|
63
75
|
_setInterval () {
|
|
64
76
|
this._timeoutInterval = this._config.flushInterval
|
|
65
77
|
}
|
|
66
78
|
|
|
67
|
-
stop () {
|
|
79
|
+
async stop () {
|
|
80
|
+
if (!this._enabled) return
|
|
81
|
+
|
|
82
|
+
// collect and export current profiles
|
|
83
|
+
// once collect returns, profilers can be safely stopped
|
|
84
|
+
this._collect(snapshotKinds.ON_SHUTDOWN)
|
|
85
|
+
this._stop()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
_stop () {
|
|
68
89
|
if (!this._enabled) return
|
|
69
90
|
|
|
70
91
|
this._enabled = false
|
|
@@ -84,24 +105,32 @@ class Profiler extends EventEmitter {
|
|
|
84
105
|
if (!this._enabled) return
|
|
85
106
|
this._lastStart = new Date()
|
|
86
107
|
if (!this._timer || timeout !== this._timeoutInterval) {
|
|
87
|
-
this._timer = setTimeout(() => this._collect(), timeout)
|
|
108
|
+
this._timer = setTimeout(() => this._collect(snapshotKinds.PERIODIC), timeout)
|
|
88
109
|
this._timer.unref()
|
|
89
110
|
} else {
|
|
90
111
|
this._timer.refresh()
|
|
91
112
|
}
|
|
92
113
|
}
|
|
93
114
|
|
|
94
|
-
async _collect () {
|
|
115
|
+
async _collect (snapshotKind) {
|
|
116
|
+
if (!this._enabled) return
|
|
117
|
+
|
|
95
118
|
const start = this._lastStart
|
|
96
119
|
const end = new Date()
|
|
97
|
-
const profiles =
|
|
120
|
+
const profiles = []
|
|
121
|
+
const encodedProfiles = {}
|
|
98
122
|
|
|
99
123
|
try {
|
|
124
|
+
// collect profiles synchronously so that profilers can be safely stopped asynchronously
|
|
100
125
|
for (const profiler of this._config.profilers) {
|
|
101
126
|
const profile = profiler.profile()
|
|
102
127
|
if (!profile) continue
|
|
128
|
+
profiles.push({ profiler, profile })
|
|
129
|
+
}
|
|
103
130
|
|
|
104
|
-
|
|
131
|
+
// encode and export asynchronously
|
|
132
|
+
for (const { profiler, profile } of profiles) {
|
|
133
|
+
encodedProfiles[profiler.type] = await profiler.encode(profile)
|
|
105
134
|
this._logger.debug(() => {
|
|
106
135
|
const profileJson = JSON.stringify(profile, (key, value) => {
|
|
107
136
|
return typeof value === 'bigint' ? value.toString() : value
|
|
@@ -111,21 +140,22 @@ class Profiler extends EventEmitter {
|
|
|
111
140
|
}
|
|
112
141
|
|
|
113
142
|
this._capture(this._timeoutInterval)
|
|
114
|
-
await this._submit(
|
|
143
|
+
await this._submit(encodedProfiles, start, end, snapshotKind)
|
|
115
144
|
this._logger.debug('Submitted profiles')
|
|
116
145
|
} catch (err) {
|
|
117
146
|
this._logger.error(err)
|
|
118
|
-
this.
|
|
147
|
+
this._stop()
|
|
119
148
|
}
|
|
120
149
|
}
|
|
121
150
|
|
|
122
|
-
_submit (profiles, start, end) {
|
|
151
|
+
_submit (profiles, start, end, snapshotKind) {
|
|
123
152
|
if (!Object.keys(profiles).length) {
|
|
124
153
|
return Promise.reject(new Error('No profiles to submit'))
|
|
125
154
|
}
|
|
126
155
|
const { tags } = this._config
|
|
127
156
|
const tasks = []
|
|
128
157
|
|
|
158
|
+
tags.snapshot = snapshotKind
|
|
129
159
|
for (const exporter of this._config.exporters) {
|
|
130
160
|
const task = exporter.export({ profiles, start, end, tags })
|
|
131
161
|
.catch(err => this._logger.error(err))
|
|
@@ -150,10 +180,10 @@ class ServerlessProfiler extends Profiler {
|
|
|
150
180
|
this._flushAfterIntervals = this._config.flushInterval / 1000
|
|
151
181
|
}
|
|
152
182
|
|
|
153
|
-
async _collect () {
|
|
183
|
+
async _collect (snapshotKind) {
|
|
154
184
|
if (this._profiledIntervals >= this._flushAfterIntervals) {
|
|
155
185
|
this._profiledIntervals = 0
|
|
156
|
-
await super._collect()
|
|
186
|
+
await super._collect(snapshotKind)
|
|
157
187
|
} else {
|
|
158
188
|
this._profiledIntervals += 1
|
|
159
189
|
this._capture(this._timeoutInterval)
|
|
@@ -1,17 +1,37 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { oomExportStrategies } = require('../constants')
|
|
4
|
+
|
|
5
|
+
function strategiesToCallbackMode (strategies, callbackMode) {
|
|
6
|
+
const hasInterrupt = strategies.includes(oomExportStrategies.INTERRUPT_CALLBACK) ? callbackMode.Interrupt : 0
|
|
7
|
+
const hasCallback = strategies.includes(oomExportStrategies.ASYNC_CALLBACK) ? callbackMode.Async : 0
|
|
8
|
+
return hasInterrupt | hasCallback
|
|
9
|
+
}
|
|
10
|
+
|
|
3
11
|
class NativeSpaceProfiler {
|
|
4
12
|
constructor (options = {}) {
|
|
5
13
|
this.type = 'space'
|
|
6
14
|
this._samplingInterval = options.samplingInterval || 512 * 1024
|
|
7
15
|
this._stackDepth = options.stackDepth || 64
|
|
8
16
|
this._pprof = undefined
|
|
17
|
+
this._oomMonitoring = options.oomMonitoring || {}
|
|
9
18
|
}
|
|
10
19
|
|
|
11
|
-
start ({ mapper } = {}) {
|
|
20
|
+
start ({ mapper, nearOOMCallback } = {}) {
|
|
12
21
|
this._mapper = mapper
|
|
13
22
|
this._pprof = require('@datadog/pprof')
|
|
14
23
|
this._pprof.heap.start(this._samplingInterval, this._stackDepth)
|
|
24
|
+
if (this._oomMonitoring.enabled) {
|
|
25
|
+
const strategies = this._oomMonitoring.exportStrategies
|
|
26
|
+
this._pprof.heap.monitorOutOfMemory(
|
|
27
|
+
this._oomMonitoring.heapLimitExtensionSize,
|
|
28
|
+
this._oomMonitoring.maxHeapExtensionCount,
|
|
29
|
+
strategies.includes(oomExportStrategies.LOGS),
|
|
30
|
+
strategies.includes(oomExportStrategies.PROCESS) ? this._oomMonitoring.exportCommand : [],
|
|
31
|
+
(profile) => nearOOMCallback(this.type, this._pprof.encodeSync(profile)),
|
|
32
|
+
strategiesToCallbackMode(strategies, this._pprof.heap.CallbackMode)
|
|
33
|
+
)
|
|
34
|
+
}
|
|
15
35
|
}
|
|
16
36
|
|
|
17
37
|
profile () {
|
|
@@ -51,7 +51,7 @@ class Tracer extends NoopProxy {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
this._tracer = new DatadogTracer(config)
|
|
54
|
-
this.appsec = new AppsecSdk(this._tracer)
|
|
54
|
+
this.appsec = new AppsecSdk(this._tracer, config)
|
|
55
55
|
|
|
56
56
|
if (config.iast.enabled) {
|
|
57
57
|
require('./appsec/iast').enable(config, this._tracer)
|
|
@@ -2,76 +2,93 @@
|
|
|
2
2
|
const { globMatch } = require('../src/util')
|
|
3
3
|
const { USER_KEEP, AUTO_KEEP } = require('../../../ext').priority
|
|
4
4
|
const RateLimiter = require('./rate_limiter')
|
|
5
|
+
const Sampler = require('./sampler')
|
|
5
6
|
|
|
6
|
-
class
|
|
7
|
-
constructor ({
|
|
8
|
-
this.
|
|
9
|
-
this.
|
|
10
|
-
}
|
|
7
|
+
class SpanSamplingRule {
|
|
8
|
+
constructor ({ service, name, sampleRate = 1.0, maxPerSecond } = {}) {
|
|
9
|
+
this.service = service
|
|
10
|
+
this.name = name
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if (decision === USER_KEEP || decision === AUTO_KEEP) return
|
|
12
|
+
this._sampler = new Sampler(sampleRate)
|
|
13
|
+
this._limiter = undefined
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const service = span.tracer()._service
|
|
19
|
-
const name = span._name
|
|
20
|
-
const rule = findRule(this._rules, service, name)
|
|
21
|
-
if (!rule) continue
|
|
22
|
-
|
|
23
|
-
const sampleRate = getSampleRate(rule.sampleRate)
|
|
24
|
-
const maxPerSecond = getMaxPerSecond(rule.maxPerSecond)
|
|
25
|
-
const sampled = sample(sampleRate)
|
|
26
|
-
if (!sampled) continue
|
|
27
|
-
|
|
28
|
-
const key = `${service}:${name}`
|
|
29
|
-
const limiter = getLimiter(this._limiters, key, maxPerSecond)
|
|
30
|
-
if (limiter.isAllowed()) {
|
|
31
|
-
span.context()._sampling.spanSampling = {
|
|
32
|
-
sampleRate,
|
|
33
|
-
maxPerSecond
|
|
34
|
-
}
|
|
35
|
-
}
|
|
15
|
+
if (Number.isFinite(maxPerSecond)) {
|
|
16
|
+
this._limiter = new RateLimiter(maxPerSecond)
|
|
36
17
|
}
|
|
37
18
|
}
|
|
38
|
-
}
|
|
39
19
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
20
|
+
get sampleRate () {
|
|
21
|
+
return this._sampler.rate()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get maxPerSecond () {
|
|
25
|
+
return this._limiter && this._limiter._rateLimit
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static from (config) {
|
|
29
|
+
return new SpanSamplingRule(config)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
match (service, name) {
|
|
33
|
+
if (this.service && !globMatch(this.service, service)) {
|
|
34
|
+
return false
|
|
46
35
|
}
|
|
36
|
+
|
|
37
|
+
if (this.name && !globMatch(this.name, name)) {
|
|
38
|
+
return false
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return true
|
|
47
42
|
}
|
|
48
|
-
}
|
|
49
43
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
44
|
+
sample () {
|
|
45
|
+
if (!this._sampler.isSampled()) {
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (this._limiter) {
|
|
50
|
+
return this._limiter.isAllowed()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return true
|
|
53
54
|
}
|
|
54
|
-
return list[key]
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
class SpanSampler {
|
|
58
|
+
constructor ({ spanSamplingRules = [] } = {}) {
|
|
59
|
+
this._rules = spanSamplingRules.map(SpanSamplingRule.from)
|
|
60
|
+
}
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
findRule (service, name) {
|
|
63
|
+
for (const rule of this._rules) {
|
|
64
|
+
if (rule.match(service, name)) {
|
|
65
|
+
return rule
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
64
69
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
sample (spanContext) {
|
|
71
|
+
const decision = spanContext._sampling.priority
|
|
72
|
+
if (decision === USER_KEEP || decision === AUTO_KEEP) return
|
|
68
73
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
74
|
+
const { started } = spanContext._trace
|
|
75
|
+
for (const span of started) {
|
|
76
|
+
const context = span.context()
|
|
77
|
+
const tags = context._tags || {}
|
|
78
|
+
const name = context._name
|
|
79
|
+
const service = tags.service ||
|
|
80
|
+
tags['service.name'] ||
|
|
81
|
+
span.tracer()._service
|
|
72
82
|
|
|
73
|
-
|
|
74
|
-
|
|
83
|
+
const rule = this.findRule(service, name)
|
|
84
|
+
if (rule && rule.sample()) {
|
|
85
|
+
span.context()._sampling.spanSampling = {
|
|
86
|
+
sampleRate: rule.sampleRate,
|
|
87
|
+
maxPerSecond: rule.maxPerSecond
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
75
92
|
}
|
|
76
93
|
|
|
77
94
|
module.exports = SpanSampler
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const { info, warn } = require('./log/writer')
|
|
4
4
|
|
|
5
5
|
const os = require('os')
|
|
6
6
|
const { inspect } = require('util')
|
|
7
7
|
const tracerVersion = require('../../../package.json').version
|
|
8
8
|
|
|
9
|
-
const logger = Object.create(mainLogger)
|
|
10
|
-
logger.toggle(true)
|
|
11
|
-
|
|
12
9
|
let config
|
|
13
10
|
let pluginManager
|
|
14
11
|
let samplingRules = []
|
|
@@ -89,9 +86,9 @@ function startupLog ({ agentError } = {}) {
|
|
|
89
86
|
// out.service_mapping
|
|
90
87
|
// out.service_mapping_error
|
|
91
88
|
|
|
92
|
-
|
|
89
|
+
info('DATADOG TRACER CONFIGURATION - ' + out)
|
|
93
90
|
if (agentError) {
|
|
94
|
-
|
|
91
|
+
warn('DATADOG TRACER DIAGNOSTIC - Agent Error: ' + agentError.message)
|
|
95
92
|
}
|
|
96
93
|
|
|
97
94
|
config = undefined
|