dd-trace 4.18.0 → 5.6.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/CONTRIBUTING.md +98 -0
- package/LICENSE-3rdparty.csv +4 -5
- package/MIGRATING.md +15 -0
- package/README.md +20 -140
- package/ci/cypress/after-run.js +1 -0
- package/ci/cypress/after-spec.js +1 -0
- package/ci/init.js +1 -4
- package/ext/kinds.d.ts +1 -0
- package/ext/kinds.js +2 -1
- package/ext/tags.d.ts +2 -1
- package/ext/tags.js +6 -1
- package/index.d.ts +1523 -1460
- package/package.json +19 -19
- package/packages/datadog-core/src/storage/async_resource.js +1 -1
- package/packages/datadog-core/src/utils/src/get.js +11 -0
- package/packages/datadog-core/src/utils/src/has.js +14 -0
- package/packages/datadog-core/src/utils/src/kebabcase.js +16 -0
- package/packages/datadog-core/src/utils/src/pick.js +11 -0
- package/packages/datadog-core/src/utils/src/set.js +16 -0
- package/packages/datadog-core/src/utils/src/uniq.js +5 -0
- package/packages/datadog-esbuild/index.js +1 -20
- package/packages/datadog-instrumentations/src/aerospike.js +47 -0
- package/packages/datadog-instrumentations/src/amqplib.js +2 -2
- package/packages/datadog-instrumentations/src/apollo-server-core.js +41 -0
- package/packages/datadog-instrumentations/src/apollo-server.js +83 -0
- package/packages/datadog-instrumentations/src/child_process.js +150 -0
- package/packages/datadog-instrumentations/src/couchbase.js +5 -4
- package/packages/datadog-instrumentations/src/crypto.js +2 -1
- package/packages/datadog-instrumentations/src/cucumber.js +163 -46
- package/packages/datadog-instrumentations/src/dns.js +2 -1
- package/packages/datadog-instrumentations/src/express.js +20 -0
- package/packages/datadog-instrumentations/src/graphql.js +18 -4
- package/packages/datadog-instrumentations/src/grpc/client.js +56 -36
- package/packages/datadog-instrumentations/src/grpc/server.js +3 -1
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +1 -2
- package/packages/datadog-instrumentations/src/helpers/hooks.js +12 -3
- package/packages/datadog-instrumentations/src/helpers/instrument.js +9 -4
- package/packages/datadog-instrumentations/src/helpers/register.js +19 -3
- package/packages/datadog-instrumentations/src/http/client.js +12 -2
- package/packages/datadog-instrumentations/src/http/server.js +7 -4
- package/packages/datadog-instrumentations/src/http2/client.js +3 -1
- package/packages/datadog-instrumentations/src/http2/server.js +3 -1
- package/packages/datadog-instrumentations/src/jest.js +239 -52
- package/packages/datadog-instrumentations/src/kafkajs.js +27 -0
- package/packages/datadog-instrumentations/src/mocha.js +154 -18
- package/packages/datadog-instrumentations/src/mongodb-core.js +34 -3
- package/packages/datadog-instrumentations/src/mongoose.js +23 -10
- package/packages/datadog-instrumentations/src/mquery.js +65 -0
- package/packages/datadog-instrumentations/src/net.js +10 -2
- package/packages/datadog-instrumentations/src/next.js +35 -9
- package/packages/datadog-instrumentations/src/playwright.js +110 -16
- package/packages/datadog-instrumentations/src/restify.js +14 -1
- package/packages/datadog-instrumentations/src/rhea.js +15 -9
- package/packages/datadog-plugin-aerospike/src/index.js +113 -0
- package/packages/datadog-plugin-amqplib/src/consumer.js +14 -1
- package/packages/datadog-plugin-amqplib/src/producer.js +13 -1
- package/packages/datadog-plugin-aws-sdk/src/base.js +3 -2
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +163 -27
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +46 -8
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +129 -22
- package/packages/datadog-plugin-child_process/src/index.js +91 -0
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +125 -0
- package/packages/datadog-plugin-cucumber/src/index.js +70 -13
- package/packages/datadog-plugin-cypress/src/after-run.js +3 -0
- package/packages/datadog-plugin-cypress/src/after-spec.js +3 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +625 -0
- package/packages/datadog-plugin-cypress/src/plugin.js +6 -454
- package/packages/datadog-plugin-cypress/src/support.js +50 -3
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -0
- package/packages/datadog-plugin-graphql/src/index.js +1 -6
- package/packages/datadog-plugin-graphql/src/resolve.js +28 -18
- package/packages/datadog-plugin-grpc/src/client.js +16 -2
- package/packages/datadog-plugin-grpc/src/util.js +1 -1
- package/packages/datadog-plugin-http/src/client.js +19 -2
- package/packages/datadog-plugin-jest/src/index.js +118 -12
- package/packages/datadog-plugin-jest/src/util.js +38 -16
- package/packages/datadog-plugin-kafkajs/src/consumer.js +76 -6
- package/packages/datadog-plugin-kafkajs/src/producer.js +64 -8
- package/packages/datadog-plugin-mocha/src/index.js +87 -17
- package/packages/datadog-plugin-next/src/index.js +40 -14
- package/packages/datadog-plugin-playwright/src/index.js +71 -8
- package/packages/datadog-plugin-rhea/src/consumer.js +16 -1
- package/packages/datadog-plugin-rhea/src/producer.js +10 -0
- package/packages/dd-trace/src/appsec/activation.js +29 -0
- package/packages/dd-trace/src/appsec/addresses.js +5 -1
- package/packages/dd-trace/src/appsec/api_security_sampler.js +61 -0
- package/packages/dd-trace/src/appsec/blocked_templates.js +4 -1
- package/packages/dd-trace/src/appsec/blocking.js +95 -43
- package/packages/dd-trace/src/appsec/channels.js +7 -3
- package/packages/dd-trace/src/appsec/graphql.js +146 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +2 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +105 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +22 -17
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +7 -28
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +10 -6
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-randomness-analyzer.js +19 -0
- package/packages/dd-trace/src/appsec/iast/context/context-plugin.js +90 -0
- package/packages/dd-trace/src/appsec/iast/context/kafka-ctx-plugin.js +14 -0
- package/packages/dd-trace/src/appsec/iast/iast-log.js +1 -1
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +13 -2
- package/packages/dd-trace/src/appsec/iast/index.js +15 -5
- package/packages/dd-trace/src/appsec/iast/overhead-controller.js +1 -1
- package/packages/dd-trace/src/appsec/iast/path-line.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +2 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +10 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +53 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +10 -46
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +13 -9
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +47 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +19 -6
- package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +3 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +41 -3
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/constants.js +7 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +12 -19
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/header-sensitive-analyzer.js +20 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/json-sensitive-analyzer.js +6 -10
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +18 -25
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +79 -85
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +27 -36
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +14 -11
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +2 -0
- package/packages/dd-trace/src/appsec/index.js +49 -33
- package/packages/dd-trace/src/appsec/recommended.json +1763 -106
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +7 -1
- package/packages/dd-trace/src/appsec/remote_config/index.js +42 -16
- package/packages/dd-trace/src/appsec/remote_config/manager.js +9 -8
- package/packages/dd-trace/src/appsec/reporter.js +51 -34
- package/packages/dd-trace/src/appsec/rule_manager.js +11 -8
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +28 -13
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +0 -1
- package/packages/dd-trace/src/ci-visibility/{intelligent-test-runner/get-itr-configuration.js → early-flake-detection/get-known-tests.js} +17 -22
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +25 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +30 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +2 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +30 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +95 -37
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +134 -61
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +37 -4
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +131 -0
- package/packages/dd-trace/src/ci-visibility/telemetry.js +130 -0
- package/packages/dd-trace/src/config.js +561 -470
- package/packages/dd-trace/src/data_streams_context.js +1 -1
- package/packages/dd-trace/src/datastreams/pathway.js +58 -1
- package/packages/dd-trace/src/datastreams/processor.js +196 -27
- package/packages/dd-trace/src/datastreams/writer.js +11 -5
- package/packages/dd-trace/src/dogstatsd.js +3 -5
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +44 -6
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +14 -0
- package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +4 -0
- package/packages/dd-trace/src/exporters/common/form-data.js +4 -0
- package/packages/dd-trace/src/exporters/common/request.js +21 -3
- package/packages/dd-trace/src/format.js +30 -2
- package/packages/dd-trace/src/id.js +12 -0
- package/packages/dd-trace/src/iitm.js +1 -1
- package/packages/dd-trace/src/log/channels.js +1 -1
- package/packages/dd-trace/src/noop/proxy.js +4 -0
- package/packages/dd-trace/src/noop/span.js +1 -0
- package/packages/dd-trace/src/opentelemetry/span.js +104 -4
- package/packages/dd-trace/src/opentelemetry/tracer.js +9 -10
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +16 -7
- package/packages/dd-trace/src/opentracing/span.js +48 -4
- package/packages/dd-trace/src/opentracing/span_context.js +15 -6
- package/packages/dd-trace/src/opentracing/tracer.js +4 -3
- package/packages/dd-trace/src/plugin_manager.js +1 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +78 -19
- package/packages/dd-trace/src/plugins/database.js +1 -1
- package/packages/dd-trace/src/plugins/index.js +7 -0
- package/packages/dd-trace/src/plugins/plugin.js +1 -1
- package/packages/dd-trace/src/plugins/util/ci.js +6 -19
- package/packages/dd-trace/src/plugins/util/git.js +104 -22
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +7 -6
- package/packages/dd-trace/src/plugins/util/test.js +60 -10
- package/packages/dd-trace/src/plugins/util/url.js +26 -0
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +4 -16
- package/packages/dd-trace/src/plugins/util/web.js +1 -1
- package/packages/dd-trace/src/priority_sampler.js +30 -38
- package/packages/dd-trace/src/profiler.js +5 -3
- package/packages/dd-trace/src/profiling/config.js +77 -24
- package/packages/dd-trace/src/profiling/exporters/agent.js +77 -31
- package/packages/dd-trace/src/profiling/exporters/file.js +2 -1
- package/packages/dd-trace/src/profiling/profiler.js +33 -22
- package/packages/dd-trace/src/profiling/profilers/events.js +270 -0
- package/packages/dd-trace/src/profiling/profilers/shared.js +45 -0
- package/packages/dd-trace/src/profiling/profilers/space.js +18 -2
- package/packages/dd-trace/src/profiling/profilers/wall.js +146 -70
- package/packages/dd-trace/src/proxy.js +56 -24
- package/packages/dd-trace/src/ritm.js +1 -1
- package/packages/dd-trace/src/sampling_rule.js +130 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +5 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +4 -0
- package/packages/dd-trace/src/span_processor.js +9 -1
- package/packages/dd-trace/src/span_sampler.js +6 -64
- package/packages/dd-trace/src/spanleak.js +98 -0
- package/packages/dd-trace/src/startup-log.js +7 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +56 -10
- package/packages/dd-trace/src/telemetry/index.js +182 -53
- package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
- package/packages/dd-trace/src/telemetry/send-data.js +65 -7
- package/packages/dd-trace/src/tracer.js +12 -5
- package/register.js +4 -0
- package/scripts/install_plugin_modules.js +11 -3
- package/scripts/st.js +105 -0
- package/packages/datadog-instrumentations/src/child-process.js +0 -30
- package/packages/dd-trace/src/plugins/util/exec.js +0 -13
- package/packages/diagnostics_channel/index.js +0 -3
- package/packages/diagnostics_channel/src/index.js +0 -121
|
@@ -2,21 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
const { storage } = require('../../../../datadog-core')
|
|
4
4
|
|
|
5
|
-
const dc = require('
|
|
5
|
+
const dc = require('dc-polyfill')
|
|
6
6
|
const { HTTP_METHOD, HTTP_ROUTE, RESOURCE_NAME, SPAN_TYPE } = require('../../../../../ext/tags')
|
|
7
7
|
const { WEB } = require('../../../../../ext/types')
|
|
8
8
|
const runtimeMetrics = require('../../runtime_metrics')
|
|
9
9
|
const telemetryMetrics = require('../../telemetry/metrics')
|
|
10
|
+
const { END_TIMESTAMP_LABEL, getNonJSThreadsLabels, getThreadLabels } = require('./shared')
|
|
10
11
|
|
|
11
12
|
const beforeCh = dc.channel('dd-trace:storage:before')
|
|
12
13
|
const enterCh = dc.channel('dd-trace:storage:enter')
|
|
14
|
+
const spanFinishCh = dc.channel('dd-trace:span:finish')
|
|
13
15
|
const profilerTelemetryMetrics = telemetryMetrics.manager.namespace('profilers')
|
|
14
16
|
|
|
15
|
-
const
|
|
16
|
-
const { isMainThread, threadId } = require('node:worker_threads')
|
|
17
|
-
const name = isMainThread ? 'Main' : `Worker #${threadId}`
|
|
18
|
-
return `${name} Event Loop`
|
|
19
|
-
})()
|
|
17
|
+
const MemoizedWebTags = Symbol('NativeWallProfiler.MemoizedWebTags')
|
|
20
18
|
|
|
21
19
|
let kSampleCount
|
|
22
20
|
|
|
@@ -29,30 +27,6 @@ function getStartedSpans (context) {
|
|
|
29
27
|
return context._trace.started
|
|
30
28
|
}
|
|
31
29
|
|
|
32
|
-
function generateLabels ({ context: { spanId, rootSpanId, webTags, endpoint }, timestamp }) {
|
|
33
|
-
const labels = { 'thread name': threadName }
|
|
34
|
-
if (spanId) {
|
|
35
|
-
labels['span id'] = spanId
|
|
36
|
-
}
|
|
37
|
-
if (rootSpanId) {
|
|
38
|
-
labels['local root span id'] = rootSpanId
|
|
39
|
-
}
|
|
40
|
-
if (webTags && Object.keys(webTags).length !== 0) {
|
|
41
|
-
labels['trace endpoint'] = endpointNameFromTags(webTags)
|
|
42
|
-
} else if (endpoint) {
|
|
43
|
-
// fallback to endpoint computed when sample was taken
|
|
44
|
-
labels['trace endpoint'] = endpoint
|
|
45
|
-
}
|
|
46
|
-
// Incoming timestamps are in microseconds, we emit nanos.
|
|
47
|
-
labels['end_timestamp_ns'] = timestamp * 1000n
|
|
48
|
-
|
|
49
|
-
return labels
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function getSpanContextTags (span) {
|
|
53
|
-
return span.context()._tags
|
|
54
|
-
}
|
|
55
|
-
|
|
56
30
|
function isWebServerSpan (tags) {
|
|
57
31
|
return tags[SPAN_TYPE] === WEB
|
|
58
32
|
}
|
|
@@ -64,6 +38,38 @@ function endpointNameFromTags (tags) {
|
|
|
64
38
|
].filter(v => v).join(' ')
|
|
65
39
|
}
|
|
66
40
|
|
|
41
|
+
function getWebTags (startedSpans, i, span) {
|
|
42
|
+
// Are web tags for this span already memoized?
|
|
43
|
+
const memoizedWebTags = span[MemoizedWebTags]
|
|
44
|
+
if (memoizedWebTags !== undefined) {
|
|
45
|
+
return memoizedWebTags
|
|
46
|
+
}
|
|
47
|
+
// No, we'll have to memoize a new value
|
|
48
|
+
function memoize (tags) {
|
|
49
|
+
span[MemoizedWebTags] = tags
|
|
50
|
+
return tags
|
|
51
|
+
}
|
|
52
|
+
// Is this span itself a web span?
|
|
53
|
+
const context = span.context()
|
|
54
|
+
const tags = context._tags
|
|
55
|
+
if (isWebServerSpan(tags)) {
|
|
56
|
+
return memoize(tags)
|
|
57
|
+
}
|
|
58
|
+
// It isn't. Get parent's web tags (memoize them too recursively.)
|
|
59
|
+
// There might be several webspans, for example with next.js, http plugin creates the first span
|
|
60
|
+
// and then next.js plugin creates a child span, and this child span has the correct endpoint
|
|
61
|
+
// information. That's why we always use the tags of the closest ancestor web span.
|
|
62
|
+
const parentId = context._parentId
|
|
63
|
+
while (--i >= 0) {
|
|
64
|
+
const ispan = startedSpans[i]
|
|
65
|
+
if (ispan.context()._spanId === parentId) {
|
|
66
|
+
return memoize(getWebTags(startedSpans, i, ispan))
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Local root span with no web span
|
|
70
|
+
return memoize(null)
|
|
71
|
+
}
|
|
72
|
+
|
|
67
73
|
class NativeWallProfiler {
|
|
68
74
|
constructor (options = {}) {
|
|
69
75
|
this.type = 'wall'
|
|
@@ -71,13 +77,29 @@ class NativeWallProfiler {
|
|
|
71
77
|
this._flushIntervalMillis = options.flushInterval || 60 * 1e3 // 60 seconds
|
|
72
78
|
this._codeHotspotsEnabled = !!options.codeHotspotsEnabled
|
|
73
79
|
this._endpointCollectionEnabled = !!options.endpointCollectionEnabled
|
|
74
|
-
this.
|
|
80
|
+
this._timelineEnabled = !!options.timelineEnabled
|
|
81
|
+
this._cpuProfilingEnabled = !!options.cpuProfilingEnabled
|
|
82
|
+
// We need to capture span data into the sample context for either code hotspots
|
|
83
|
+
// or endpoint collection.
|
|
84
|
+
this._captureSpanData = this._codeHotspotsEnabled || this._endpointCollectionEnabled
|
|
85
|
+
// We need to run the pprof wall profiler with sample contexts if we're either
|
|
86
|
+
// capturing span data or timeline is enabled (so we need sample timestamps, and for now
|
|
87
|
+
// timestamps require the sample contexts feature in the pprof wall profiler), or
|
|
88
|
+
// cpu profiling is enabled.
|
|
89
|
+
this._withContexts = this._captureSpanData || this._timelineEnabled || this._cpuProfilingEnabled
|
|
75
90
|
this._v8ProfilerBugWorkaroundEnabled = !!options.v8ProfilerBugWorkaroundEnabled
|
|
76
91
|
this._mapper = undefined
|
|
77
92
|
this._pprof = undefined
|
|
78
93
|
|
|
79
|
-
// Bind to this so
|
|
80
|
-
|
|
94
|
+
// Bind these to this so they can be used as callbacks
|
|
95
|
+
if (this._withContexts) {
|
|
96
|
+
if (this._captureSpanData) {
|
|
97
|
+
this._enter = this._enter.bind(this)
|
|
98
|
+
this._spanFinished = this._spanFinished.bind(this)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
this._generateLabels = this._generateLabels.bind(this)
|
|
102
|
+
|
|
81
103
|
this._logger = options.logger
|
|
82
104
|
this._started = false
|
|
83
105
|
}
|
|
@@ -111,19 +133,25 @@ class NativeWallProfiler {
|
|
|
111
133
|
sourceMapper: this._mapper,
|
|
112
134
|
withContexts: this._withContexts,
|
|
113
135
|
lineNumbers: false,
|
|
114
|
-
workaroundV8Bug: this._v8ProfilerBugWorkaroundEnabled
|
|
136
|
+
workaroundV8Bug: this._v8ProfilerBugWorkaroundEnabled,
|
|
137
|
+
collectCpuTime: this._cpuProfilingEnabled
|
|
115
138
|
})
|
|
116
139
|
|
|
117
140
|
if (this._withContexts) {
|
|
118
|
-
this._profilerState = this._pprof.time.getState()
|
|
119
141
|
this._currentContext = {}
|
|
120
142
|
this._pprof.time.setContext(this._currentContext)
|
|
121
|
-
this._lastSpan = undefined
|
|
122
|
-
this._lastStartedSpans = undefined
|
|
123
|
-
this._lastSampleCount = 0
|
|
124
143
|
|
|
125
|
-
|
|
126
|
-
|
|
144
|
+
if (this._captureSpanData) {
|
|
145
|
+
this._profilerState = this._pprof.time.getState()
|
|
146
|
+
this._lastSpan = undefined
|
|
147
|
+
this._lastStartedSpans = undefined
|
|
148
|
+
this._lastWebTags = undefined
|
|
149
|
+
this._lastSampleCount = 0
|
|
150
|
+
|
|
151
|
+
beforeCh.subscribe(this._enter)
|
|
152
|
+
enterCh.subscribe(this._enter)
|
|
153
|
+
spanFinishCh.subscribe(this._spanFinished)
|
|
154
|
+
}
|
|
127
155
|
}
|
|
128
156
|
|
|
129
157
|
this._started = true
|
|
@@ -144,11 +172,17 @@ class NativeWallProfiler {
|
|
|
144
172
|
|
|
145
173
|
const span = getActiveSpan()
|
|
146
174
|
if (span) {
|
|
175
|
+
const context = span.context()
|
|
147
176
|
this._lastSpan = span
|
|
148
|
-
|
|
177
|
+
const startedSpans = getStartedSpans(context)
|
|
178
|
+
this._lastStartedSpans = startedSpans
|
|
179
|
+
if (this._endpointCollectionEnabled) {
|
|
180
|
+
this._lastWebTags = getWebTags(startedSpans, startedSpans.length, span)
|
|
181
|
+
}
|
|
149
182
|
} else {
|
|
150
183
|
this._lastStartedSpans = undefined
|
|
151
184
|
this._lastSpan = undefined
|
|
185
|
+
this._lastWebTags = undefined
|
|
152
186
|
}
|
|
153
187
|
}
|
|
154
188
|
|
|
@@ -163,21 +197,17 @@ class NativeWallProfiler {
|
|
|
163
197
|
context.rootSpanId = rootSpan.context().toSpanId()
|
|
164
198
|
}
|
|
165
199
|
}
|
|
166
|
-
if (this.
|
|
167
|
-
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
context.endpoint = endpointNameFromTags(tags)
|
|
178
|
-
break
|
|
179
|
-
}
|
|
180
|
-
}
|
|
200
|
+
if (this._lastWebTags) {
|
|
201
|
+
context.webTags = this._lastWebTags
|
|
202
|
+
// endpoint may not be determined yet, but keep it as fallback
|
|
203
|
+
// if tags are not available anymore during serialization
|
|
204
|
+
context.endpoint = endpointNameFromTags(this._lastWebTags)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
_spanFinished (span) {
|
|
209
|
+
if (span[MemoizedWebTags]) {
|
|
210
|
+
span[MemoizedWebTags] = undefined
|
|
181
211
|
}
|
|
182
212
|
}
|
|
183
213
|
|
|
@@ -193,23 +223,75 @@ class NativeWallProfiler {
|
|
|
193
223
|
|
|
194
224
|
_stop (restart) {
|
|
195
225
|
if (!this._started) return
|
|
196
|
-
|
|
226
|
+
|
|
227
|
+
if (this._captureSpanData) {
|
|
197
228
|
// update last sample context if needed
|
|
198
229
|
this._enter()
|
|
199
230
|
this._lastSampleCount = 0
|
|
200
231
|
}
|
|
201
|
-
const profile = this._pprof.time.stop(restart, this.
|
|
232
|
+
const profile = this._pprof.time.stop(restart, this._generateLabels)
|
|
233
|
+
|
|
202
234
|
if (restart) {
|
|
203
235
|
const v8BugDetected = this._pprof.time.v8ProfilerStuckEventLoopDetected()
|
|
204
236
|
if (v8BugDetected !== 0) {
|
|
205
237
|
this._reportV8bug(v8BugDetected === 1)
|
|
206
238
|
}
|
|
239
|
+
} else {
|
|
240
|
+
if (this._captureSpanData) {
|
|
241
|
+
beforeCh.unsubscribe(this._enter)
|
|
242
|
+
enterCh.unsubscribe(this._enter)
|
|
243
|
+
spanFinishCh.unsubscribe(this._spanFinished)
|
|
244
|
+
this._profilerState = undefined
|
|
245
|
+
this._lastSpan = undefined
|
|
246
|
+
this._lastStartedSpans = undefined
|
|
247
|
+
this._lastWebTags = undefined
|
|
248
|
+
}
|
|
249
|
+
this._started = false
|
|
207
250
|
}
|
|
251
|
+
|
|
208
252
|
return profile
|
|
209
253
|
}
|
|
210
254
|
|
|
211
|
-
|
|
212
|
-
|
|
255
|
+
_generateLabels ({ node, context }) {
|
|
256
|
+
// check for special node that represents CPU time all non-JS threads.
|
|
257
|
+
// In that case only return a special thread name label since we cannot associate any timestamp/span/endpoint to it.
|
|
258
|
+
if (node.name === this._pprof.time.constants.NON_JS_THREADS_FUNCTION_NAME) {
|
|
259
|
+
return getNonJSThreadsLabels()
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (context == null) {
|
|
263
|
+
// generateLabels is also called for samples without context.
|
|
264
|
+
// In that case just return thread labels.
|
|
265
|
+
return getThreadLabels()
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const labels = { ...getThreadLabels() }
|
|
269
|
+
|
|
270
|
+
const { context: { spanId, rootSpanId, webTags, endpoint }, timestamp } = context
|
|
271
|
+
|
|
272
|
+
if (this._timelineEnabled) {
|
|
273
|
+
// Incoming timestamps are in microseconds, we emit nanos.
|
|
274
|
+
labels[END_TIMESTAMP_LABEL] = timestamp * 1000n
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (spanId) {
|
|
278
|
+
labels['span id'] = spanId
|
|
279
|
+
}
|
|
280
|
+
if (rootSpanId) {
|
|
281
|
+
labels['local root span id'] = rootSpanId
|
|
282
|
+
}
|
|
283
|
+
if (webTags && Object.keys(webTags).length !== 0) {
|
|
284
|
+
labels['trace endpoint'] = endpointNameFromTags(webTags)
|
|
285
|
+
} else if (endpoint) {
|
|
286
|
+
// fallback to endpoint computed when sample was taken
|
|
287
|
+
labels['trace endpoint'] = endpoint
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return labels
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
profile (restart) {
|
|
294
|
+
return this._stop(restart)
|
|
213
295
|
}
|
|
214
296
|
|
|
215
297
|
encode (profile) {
|
|
@@ -217,17 +299,11 @@ class NativeWallProfiler {
|
|
|
217
299
|
}
|
|
218
300
|
|
|
219
301
|
stop () {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const profile = this._stop(false)
|
|
223
|
-
if (this._withContexts) {
|
|
224
|
-
beforeCh.unsubscribe(this._enter)
|
|
225
|
-
enterCh.unsubscribe(this._enter)
|
|
226
|
-
this._profilerState = undefined
|
|
227
|
-
}
|
|
302
|
+
this._stop(false)
|
|
303
|
+
}
|
|
228
304
|
|
|
229
|
-
|
|
230
|
-
return
|
|
305
|
+
isStarted () {
|
|
306
|
+
return this._started
|
|
231
307
|
}
|
|
232
308
|
}
|
|
233
309
|
|
|
@@ -10,6 +10,7 @@ const PluginManager = require('./plugin_manager')
|
|
|
10
10
|
const remoteConfig = require('./appsec/remote_config')
|
|
11
11
|
const AppsecSdk = require('./appsec/sdk')
|
|
12
12
|
const dogstatsd = require('./dogstatsd')
|
|
13
|
+
const spanleak = require('./spanleak')
|
|
13
14
|
|
|
14
15
|
class Tracer extends NoopProxy {
|
|
15
16
|
constructor () {
|
|
@@ -18,6 +19,7 @@ class Tracer extends NoopProxy {
|
|
|
18
19
|
this._initialized = false
|
|
19
20
|
this._pluginManager = new PluginManager(this)
|
|
20
21
|
this.dogstatsd = new dogstatsd.NoopDogStatsDClient()
|
|
22
|
+
this._tracingInitialized = false
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
init (options) {
|
|
@@ -27,6 +29,7 @@ class Tracer extends NoopProxy {
|
|
|
27
29
|
|
|
28
30
|
try {
|
|
29
31
|
const config = new Config(options) // TODO: support dynamic code config
|
|
32
|
+
telemetry.start(config, this._pluginManager)
|
|
30
33
|
|
|
31
34
|
if (config.dogstatsd) {
|
|
32
35
|
// Custom Metrics
|
|
@@ -35,6 +38,19 @@ class Tracer extends NoopProxy {
|
|
|
35
38
|
setInterval(() => {
|
|
36
39
|
this.dogstatsd.flush()
|
|
37
40
|
}, 10 * 1000).unref()
|
|
41
|
+
|
|
42
|
+
process.once('beforeExit', () => {
|
|
43
|
+
this.dogstatsd.flush()
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (config.spanLeakDebug > 0) {
|
|
48
|
+
if (config.spanLeakDebug === spanleak.MODES.LOG) {
|
|
49
|
+
spanleak.enableLogging()
|
|
50
|
+
} else if (config.spanLeakDebug === spanleak.MODES.GC_AND_LOG) {
|
|
51
|
+
spanleak.enableGarbageCollection()
|
|
52
|
+
}
|
|
53
|
+
spanleak.startScrubber()
|
|
38
54
|
}
|
|
39
55
|
|
|
40
56
|
if (config.remoteConfig.enabled && !config.isCiVisibility) {
|
|
@@ -46,11 +62,7 @@ class Tracer extends NoopProxy {
|
|
|
46
62
|
} else {
|
|
47
63
|
config.configure(conf.lib_config, true)
|
|
48
64
|
}
|
|
49
|
-
|
|
50
|
-
if (config.tracing) {
|
|
51
|
-
this._tracer.configure(config)
|
|
52
|
-
this._pluginManager.configure(config)
|
|
53
|
-
}
|
|
65
|
+
this._enableOrDisableTracing(config)
|
|
54
66
|
})
|
|
55
67
|
}
|
|
56
68
|
|
|
@@ -62,35 +74,22 @@ class Tracer extends NoopProxy {
|
|
|
62
74
|
// do not stop tracer initialization if the profiler fails to be imported
|
|
63
75
|
try {
|
|
64
76
|
const profiler = require('./profiler')
|
|
65
|
-
profiler.start(config)
|
|
77
|
+
this._profilerStarted = profiler.start(config)
|
|
66
78
|
} catch (e) {
|
|
67
79
|
log.error(e)
|
|
68
80
|
}
|
|
69
81
|
}
|
|
82
|
+
if (!this._profilerStarted) {
|
|
83
|
+
this._profilerStarted = Promise.resolve(false)
|
|
84
|
+
}
|
|
70
85
|
|
|
71
86
|
if (config.runtimeMetrics) {
|
|
72
87
|
runtimeMetrics.start(config)
|
|
73
88
|
}
|
|
74
89
|
|
|
75
|
-
|
|
76
|
-
// TODO: This should probably not require tracing to be enabled.
|
|
77
|
-
telemetry.start(config, this._pluginManager)
|
|
78
|
-
|
|
79
|
-
// dirty require for now so zero appsec code is executed unless explicitly enabled
|
|
80
|
-
if (config.appsec.enabled) {
|
|
81
|
-
require('./appsec').enable(config)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
this._tracer = new DatadogTracer(config)
|
|
85
|
-
this.appsec = new AppsecSdk(this._tracer, config)
|
|
86
|
-
|
|
87
|
-
if (config.iast.enabled) {
|
|
88
|
-
require('./appsec/iast').enable(config, this._tracer)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
this._pluginManager.configure(config)
|
|
92
|
-
setStartupLogPluginManager(this._pluginManager)
|
|
90
|
+
this._enableOrDisableTracing(config)
|
|
93
91
|
|
|
92
|
+
if (config.tracing) {
|
|
94
93
|
if (config.isManualApiEnabled) {
|
|
95
94
|
const TestApiManualPlugin = require('./ci-visibility/test-api-manual/test-api-manual-plugin')
|
|
96
95
|
this._testApiManualPlugin = new TestApiManualPlugin(this)
|
|
@@ -104,6 +103,39 @@ class Tracer extends NoopProxy {
|
|
|
104
103
|
return this
|
|
105
104
|
}
|
|
106
105
|
|
|
106
|
+
_enableOrDisableTracing (config) {
|
|
107
|
+
if (config.tracing !== false) {
|
|
108
|
+
// dirty require for now so zero appsec code is executed unless explicitly enabled
|
|
109
|
+
if (config.appsec.enabled) {
|
|
110
|
+
require('./appsec').enable(config)
|
|
111
|
+
}
|
|
112
|
+
if (!this._tracingInitialized) {
|
|
113
|
+
this._tracer = new DatadogTracer(config)
|
|
114
|
+
this.appsec = new AppsecSdk(this._tracer, config)
|
|
115
|
+
this._tracingInitialized = true
|
|
116
|
+
}
|
|
117
|
+
if (config.iast.enabled) {
|
|
118
|
+
require('./appsec/iast').enable(config, this._tracer)
|
|
119
|
+
}
|
|
120
|
+
} else if (this._tracingInitialized) {
|
|
121
|
+
require('./appsec').disable()
|
|
122
|
+
require('./appsec/iast').disable()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (this._tracingInitialized) {
|
|
126
|
+
this._tracer.configure(config)
|
|
127
|
+
this._pluginManager.configure(config)
|
|
128
|
+
setStartupLogPluginManager(this._pluginManager)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
profilerStarted () {
|
|
133
|
+
if (!this._profilerStarted) {
|
|
134
|
+
throw new Error('profilerStarted() must be called after init()')
|
|
135
|
+
}
|
|
136
|
+
return this._profilerStarted
|
|
137
|
+
}
|
|
138
|
+
|
|
107
139
|
use () {
|
|
108
140
|
this._pluginManager.configurePlugin(...arguments)
|
|
109
141
|
return this
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { globMatch } = require('../src/util')
|
|
4
|
+
const RateLimiter = require('./rate_limiter')
|
|
5
|
+
const Sampler = require('./sampler')
|
|
6
|
+
|
|
7
|
+
class AlwaysMatcher {
|
|
8
|
+
match () {
|
|
9
|
+
return true
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
class GlobMatcher {
|
|
14
|
+
constructor (pattern, locator) {
|
|
15
|
+
this.pattern = pattern
|
|
16
|
+
this.locator = locator
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
match (span) {
|
|
20
|
+
const subject = this.locator(span)
|
|
21
|
+
if (!subject) return false
|
|
22
|
+
return globMatch(this.pattern, subject)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class RegExpMatcher {
|
|
27
|
+
constructor (pattern, locator) {
|
|
28
|
+
this.pattern = pattern
|
|
29
|
+
this.locator = locator
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
match (span) {
|
|
33
|
+
const subject = this.locator(span)
|
|
34
|
+
if (!subject) return false
|
|
35
|
+
return this.pattern.test(subject)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function matcher (pattern, locator) {
|
|
40
|
+
if (pattern instanceof RegExp) {
|
|
41
|
+
return new RegExpMatcher(pattern, locator)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (typeof pattern === 'string' && pattern !== '*') {
|
|
45
|
+
return new GlobMatcher(pattern, locator)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return new AlwaysMatcher()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function makeTagLocator (tag) {
|
|
52
|
+
return (span) => span.context()._tags[tag]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function nameLocator (span) {
|
|
56
|
+
return span.context()._name
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function serviceLocator (span) {
|
|
60
|
+
const { _tags: tags } = span.context()
|
|
61
|
+
return tags.service ||
|
|
62
|
+
tags['service.name'] ||
|
|
63
|
+
span.tracer()._service
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
class SamplingRule {
|
|
67
|
+
constructor ({ name, service, resource, tags, sampleRate = 1.0, maxPerSecond } = {}) {
|
|
68
|
+
this.matchers = []
|
|
69
|
+
|
|
70
|
+
if (name) {
|
|
71
|
+
this.matchers.push(matcher(name, nameLocator))
|
|
72
|
+
}
|
|
73
|
+
if (service) {
|
|
74
|
+
this.matchers.push(matcher(service, serviceLocator))
|
|
75
|
+
}
|
|
76
|
+
if (resource) {
|
|
77
|
+
this.matchers.push(matcher(resource, makeTagLocator('resource.name')))
|
|
78
|
+
}
|
|
79
|
+
for (const [key, value] of Object.entries(tags || {})) {
|
|
80
|
+
this.matchers.push(matcher(value, makeTagLocator(key)))
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this._sampler = new Sampler(sampleRate)
|
|
84
|
+
this._limiter = undefined
|
|
85
|
+
|
|
86
|
+
if (Number.isFinite(maxPerSecond)) {
|
|
87
|
+
this._limiter = new RateLimiter(maxPerSecond)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static from (config) {
|
|
92
|
+
return new SamplingRule(config)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
get sampleRate () {
|
|
96
|
+
return this._sampler.rate()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
get effectiveRate () {
|
|
100
|
+
return this._limiter && this._limiter.effectiveRate()
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
get maxPerSecond () {
|
|
104
|
+
return this._limiter && this._limiter._rateLimit
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
match (span) {
|
|
108
|
+
for (const matcher of this.matchers) {
|
|
109
|
+
if (!matcher.match(span)) {
|
|
110
|
+
return false
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return true
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
sample () {
|
|
118
|
+
if (!this._sampler.isSampled()) {
|
|
119
|
+
return false
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (this._limiter) {
|
|
123
|
+
return this._limiter.isAllowed()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return true
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports = SamplingRule
|
|
@@ -37,6 +37,11 @@ const redisConfig = {
|
|
|
37
37
|
|
|
38
38
|
const storage = {
|
|
39
39
|
client: {
|
|
40
|
+
aerospike: {
|
|
41
|
+
opName: () => 'aerospike.command',
|
|
42
|
+
serviceName: ({ tracerService, pluginConfig }) =>
|
|
43
|
+
pluginConfig.service || `${tracerService}-aerospike`
|
|
44
|
+
},
|
|
40
45
|
'cassandra-driver': {
|
|
41
46
|
opName: () => 'cassandra.query',
|
|
42
47
|
serviceName: ({ tracerService, pluginConfig, system }) =>
|
|
@@ -22,6 +22,10 @@ function withFunction ({ tracerService, pluginConfig, params }) {
|
|
|
22
22
|
|
|
23
23
|
const storage = {
|
|
24
24
|
client: {
|
|
25
|
+
aerospike: {
|
|
26
|
+
opName: () => 'aerospike.command',
|
|
27
|
+
serviceName: configWithFallback
|
|
28
|
+
},
|
|
25
29
|
'cassandra-driver': {
|
|
26
30
|
opName: () => 'cassandra.query',
|
|
27
31
|
serviceName: configWithFallback
|
|
@@ -27,10 +27,14 @@ class SpanProcessor {
|
|
|
27
27
|
const active = []
|
|
28
28
|
const formatted = []
|
|
29
29
|
const trace = spanContext._trace
|
|
30
|
-
const { flushMinSpans } = this._config
|
|
30
|
+
const { flushMinSpans, tracing } = this._config
|
|
31
31
|
const { started, finished } = trace
|
|
32
32
|
|
|
33
33
|
if (trace.record === false) return
|
|
34
|
+
if (tracing === false) {
|
|
35
|
+
this._erase(trace, active)
|
|
36
|
+
return
|
|
37
|
+
}
|
|
34
38
|
if (started.length === finished.length || finished.length >= flushMinSpans) {
|
|
35
39
|
this._prioritySampler.sample(spanContext)
|
|
36
40
|
this._spanSampler.sample(spanContext)
|
|
@@ -138,6 +142,10 @@ class SpanProcessor {
|
|
|
138
142
|
}
|
|
139
143
|
}
|
|
140
144
|
|
|
145
|
+
for (const span of trace.finished) {
|
|
146
|
+
span.context()._tags = {}
|
|
147
|
+
}
|
|
148
|
+
|
|
141
149
|
trace.started = active
|
|
142
150
|
trace.finished = []
|
|
143
151
|
}
|