dd-trace 3.38.1 → 3.40.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 +3 -3
- 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 +9 -1
- package/package.json +8 -8
- package/packages/datadog-core/src/storage/async_resource.js +1 -1
- package/packages/datadog-esbuild/index.js +1 -20
- package/packages/datadog-instrumentations/src/cucumber.js +5 -0
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +1 -2
- package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +39 -10
- package/packages/datadog-instrumentations/src/knex.js +24 -17
- package/packages/datadog-instrumentations/src/mocha.js +16 -1
- package/packages/datadog-instrumentations/src/next.js +58 -23
- package/packages/datadog-instrumentations/src/playwright.js +11 -6
- package/packages/datadog-instrumentations/src/restify.js +14 -1
- package/packages/datadog-plugin-http/src/client.js +2 -0
- package/packages/datadog-plugin-jest/src/index.js +11 -3
- package/packages/datadog-plugin-kafkajs/src/consumer.js +8 -6
- package/packages/datadog-plugin-kafkajs/src/producer.js +9 -6
- package/packages/datadog-plugin-mocha/src/index.js +7 -1
- package/packages/datadog-plugin-next/src/index.js +4 -3
- package/packages/datadog-plugin-playwright/src/index.js +4 -1
- package/packages/dd-trace/src/appsec/channels.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-analyzer.js +60 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +269 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +5 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/missing-header-analyzer.js +22 -4
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +9 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/xcontenttype-header-missing-analyzer.js +2 -2
- package/packages/dd-trace/src/appsec/iast/iast-log.js +9 -4
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +1 -1
- package/packages/dd-trace/src/appsec/iast/index.js +1 -1
- package/packages/dd-trace/src/appsec/iast/path-line.js +7 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +13 -2
- package/packages/dd-trace/src/appsec/iast/telemetry/index.js +1 -14
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +19 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +2 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +5 -1
- package/packages/dd-trace/src/appsec/recommended.json +272 -48
- package/packages/dd-trace/src/appsec/reporter.js +31 -34
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +16 -4
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -0
- package/packages/dd-trace/src/config.js +35 -17
- package/packages/dd-trace/src/datastreams/processor.js +60 -15
- package/packages/dd-trace/src/format.js +6 -1
- package/packages/dd-trace/src/git_properties.js +16 -15
- package/packages/dd-trace/src/iitm.js +1 -1
- package/packages/dd-trace/src/log/channels.js +1 -1
- package/packages/dd-trace/src/opentelemetry/span.js +95 -2
- package/packages/dd-trace/src/opentelemetry/tracer.js +9 -10
- package/packages/dd-trace/src/opentracing/span.js +4 -0
- package/packages/dd-trace/src/opentracing/span_context.js +5 -2
- package/packages/dd-trace/src/plugin_manager.js +1 -1
- package/packages/dd-trace/src/plugins/database.js +1 -1
- 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 +2 -1
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +7 -6
- package/packages/dd-trace/src/plugins/util/test.js +29 -1
- package/packages/dd-trace/src/plugins/util/url.js +26 -0
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +1 -14
- package/packages/dd-trace/src/profiling/config.js +23 -20
- package/packages/dd-trace/src/profiling/profilers/events.js +161 -0
- package/packages/dd-trace/src/profiling/profilers/shared.js +9 -0
- package/packages/dd-trace/src/profiling/profilers/wall.js +84 -47
- package/packages/dd-trace/src/ritm.js +1 -1
- package/packages/dd-trace/src/span_processor.js +4 -0
- package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
- package/packages/dd-trace/src/telemetry/index.js +5 -1
- package/packages/dd-trace/src/telemetry/logs/index.js +65 -0
- package/packages/dd-trace/src/{appsec/iast/telemetry/log → telemetry/logs}/log-collector.js +9 -22
- package/packages/dd-trace/src/tracer.js +4 -2
- package/packages/dd-trace/src/appsec/iast/telemetry/log/index.js +0 -87
- package/packages/diagnostics_channel/index.js +0 -3
- package/packages/diagnostics_channel/src/index.js +0 -121
|
@@ -2,15 +2,20 @@
|
|
|
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, THREAD_NAME, threadNamePrefix } = 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')
|
|
16
|
+
const threadName = `${threadNamePrefix} Event Loop`
|
|
17
|
+
|
|
18
|
+
const CachedWebTags = Symbol('NativeWallProfiler.CachedWebTags')
|
|
14
19
|
|
|
15
20
|
let kSampleCount
|
|
16
21
|
|
|
@@ -24,7 +29,11 @@ function getStartedSpans (context) {
|
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
function generateLabels ({ context: { spanId, rootSpanId, webTags, endpoint }, timestamp }) {
|
|
27
|
-
const labels = {
|
|
32
|
+
const labels = {
|
|
33
|
+
[THREAD_NAME]: threadName,
|
|
34
|
+
// Incoming timestamps are in microseconds, we emit nanos.
|
|
35
|
+
[END_TIMESTAMP]: timestamp * 1000n
|
|
36
|
+
}
|
|
28
37
|
if (spanId) {
|
|
29
38
|
labels['span id'] = spanId
|
|
30
39
|
}
|
|
@@ -37,16 +46,10 @@ function generateLabels ({ context: { spanId, rootSpanId, webTags, endpoint }, t
|
|
|
37
46
|
// fallback to endpoint computed when sample was taken
|
|
38
47
|
labels['trace endpoint'] = endpoint
|
|
39
48
|
}
|
|
40
|
-
// Incoming timestamps are in microseconds, we emit nanos.
|
|
41
|
-
labels['end_timestamp_ns'] = timestamp * 1000n
|
|
42
49
|
|
|
43
50
|
return labels
|
|
44
51
|
}
|
|
45
52
|
|
|
46
|
-
function getSpanContextTags (span) {
|
|
47
|
-
return span.context()._tags
|
|
48
|
-
}
|
|
49
|
-
|
|
50
53
|
function isWebServerSpan (tags) {
|
|
51
54
|
return tags[SPAN_TYPE] === WEB
|
|
52
55
|
}
|
|
@@ -58,29 +61,6 @@ function endpointNameFromTags (tags) {
|
|
|
58
61
|
].filter(v => v).join(' ')
|
|
59
62
|
}
|
|
60
63
|
|
|
61
|
-
function updateContext (context, span, startedSpans, endpointCollectionEnabled) {
|
|
62
|
-
context.spanId = span.context().toSpanId()
|
|
63
|
-
const rootSpan = startedSpans[0]
|
|
64
|
-
if (rootSpan) {
|
|
65
|
-
context.rootSpanId = rootSpan.context().toSpanId()
|
|
66
|
-
if (endpointCollectionEnabled) {
|
|
67
|
-
// Find the first webspan starting from the end:
|
|
68
|
-
// There might be several webspans, for example with next.js, http plugin creates a first span
|
|
69
|
-
// and then next.js plugin creates a child span, and this child span haves the correct endpoint information.
|
|
70
|
-
for (let i = startedSpans.length - 1; i >= 0; i--) {
|
|
71
|
-
const tags = getSpanContextTags(startedSpans[i])
|
|
72
|
-
if (isWebServerSpan(tags)) {
|
|
73
|
-
context.webTags = tags
|
|
74
|
-
// endpoint may not be determined yet, but keep it as fallback
|
|
75
|
-
// if tags are not available anymore during serialization
|
|
76
|
-
context.endpoint = endpointNameFromTags(tags)
|
|
77
|
-
break
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
64
|
class NativeWallProfiler {
|
|
85
65
|
constructor (options = {}) {
|
|
86
66
|
this.type = 'wall'
|
|
@@ -88,12 +68,14 @@ class NativeWallProfiler {
|
|
|
88
68
|
this._flushIntervalMillis = options.flushInterval || 60 * 1e3 // 60 seconds
|
|
89
69
|
this._codeHotspotsEnabled = !!options.codeHotspotsEnabled
|
|
90
70
|
this._endpointCollectionEnabled = !!options.endpointCollectionEnabled
|
|
71
|
+
this._withContexts = this._codeHotspotsEnabled || this._endpointCollectionEnabled
|
|
91
72
|
this._v8ProfilerBugWorkaroundEnabled = !!options.v8ProfilerBugWorkaroundEnabled
|
|
92
73
|
this._mapper = undefined
|
|
93
74
|
this._pprof = undefined
|
|
94
75
|
|
|
95
76
|
// Bind to this so the same value can be used to unsubscribe later
|
|
96
77
|
this._enter = this._enter.bind(this)
|
|
78
|
+
this._spanFinished = this._spanFinished.bind(this)
|
|
97
79
|
this._logger = options.logger
|
|
98
80
|
this._started = false
|
|
99
81
|
}
|
|
@@ -109,12 +91,6 @@ class NativeWallProfiler {
|
|
|
109
91
|
start ({ mapper } = {}) {
|
|
110
92
|
if (this._started) return
|
|
111
93
|
|
|
112
|
-
if (this._codeHotspotsEnabled && !this._emittedFFMessage && this._logger) {
|
|
113
|
-
this._logger.debug(
|
|
114
|
-
`Wall profiler: Enable trace_show_breakdown_profiling_for_node feature flag to see code hotspots.`)
|
|
115
|
-
this._emittedFFMessage = true
|
|
116
|
-
}
|
|
117
|
-
|
|
118
94
|
this._mapper = mapper
|
|
119
95
|
this._pprof = require('@datadog/pprof')
|
|
120
96
|
kSampleCount = this._pprof.time.constants.kSampleCount
|
|
@@ -131,21 +107,23 @@ class NativeWallProfiler {
|
|
|
131
107
|
intervalMicros: this._samplingIntervalMicros,
|
|
132
108
|
durationMillis: this._flushIntervalMillis,
|
|
133
109
|
sourceMapper: this._mapper,
|
|
134
|
-
withContexts: this.
|
|
110
|
+
withContexts: this._withContexts,
|
|
135
111
|
lineNumbers: false,
|
|
136
112
|
workaroundV8Bug: this._v8ProfilerBugWorkaroundEnabled
|
|
137
113
|
})
|
|
138
114
|
|
|
139
|
-
if (this.
|
|
115
|
+
if (this._withContexts) {
|
|
140
116
|
this._profilerState = this._pprof.time.getState()
|
|
141
117
|
this._currentContext = {}
|
|
142
118
|
this._pprof.time.setContext(this._currentContext)
|
|
143
119
|
this._lastSpan = undefined
|
|
144
120
|
this._lastStartedSpans = undefined
|
|
121
|
+
this._lastWebTags = undefined
|
|
145
122
|
this._lastSampleCount = 0
|
|
146
123
|
|
|
147
124
|
beforeCh.subscribe(this._enter)
|
|
148
125
|
enterCh.subscribe(this._enter)
|
|
126
|
+
spanFinishCh.subscribe(this._spanFinished)
|
|
149
127
|
}
|
|
150
128
|
|
|
151
129
|
this._started = true
|
|
@@ -161,18 +139,73 @@ class NativeWallProfiler {
|
|
|
161
139
|
this._currentContext = {}
|
|
162
140
|
this._pprof.time.setContext(this._currentContext)
|
|
163
141
|
|
|
164
|
-
|
|
165
|
-
updateContext(context, this._lastSpan, this._lastStartedSpans, this._endpointCollectionEnabled)
|
|
166
|
-
}
|
|
142
|
+
this._updateContext(context)
|
|
167
143
|
}
|
|
168
144
|
|
|
169
145
|
const span = getActiveSpan()
|
|
170
146
|
if (span) {
|
|
147
|
+
const context = span.context()
|
|
171
148
|
this._lastSpan = span
|
|
172
|
-
|
|
149
|
+
const startedSpans = getStartedSpans(context)
|
|
150
|
+
this._lastStartedSpans = startedSpans
|
|
151
|
+
if (this._endpointCollectionEnabled) {
|
|
152
|
+
const cachedWebTags = span[CachedWebTags]
|
|
153
|
+
if (cachedWebTags === undefined) {
|
|
154
|
+
let found = false
|
|
155
|
+
// Find the first webspan starting from the end:
|
|
156
|
+
// There might be several webspans, for example with next.js, http plugin creates a first span
|
|
157
|
+
// and then next.js plugin creates a child span, and this child span has the correct endpoint information.
|
|
158
|
+
let nextSpanId = context._spanId
|
|
159
|
+
for (let i = startedSpans.length - 1; i >= 0; i--) {
|
|
160
|
+
const nextContext = startedSpans[i].context()
|
|
161
|
+
if (nextContext._spanId === nextSpanId) {
|
|
162
|
+
const tags = nextContext._tags
|
|
163
|
+
if (isWebServerSpan(tags)) {
|
|
164
|
+
this._lastWebTags = tags
|
|
165
|
+
span[CachedWebTags] = tags
|
|
166
|
+
found = true
|
|
167
|
+
break
|
|
168
|
+
}
|
|
169
|
+
nextSpanId = nextContext._parentId
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (!found) {
|
|
173
|
+
this._lastWebTags = undefined
|
|
174
|
+
span[CachedWebTags] = null // cache negative lookup result
|
|
175
|
+
}
|
|
176
|
+
} else {
|
|
177
|
+
this._lastWebTags = cachedWebTags
|
|
178
|
+
}
|
|
179
|
+
}
|
|
173
180
|
} else {
|
|
174
181
|
this._lastStartedSpans = undefined
|
|
175
182
|
this._lastSpan = undefined
|
|
183
|
+
this._lastWebTags = undefined
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
_updateContext (context) {
|
|
188
|
+
if (!this._lastSpan) {
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
if (this._codeHotspotsEnabled) {
|
|
192
|
+
context.spanId = this._lastSpan.context().toSpanId()
|
|
193
|
+
const rootSpan = this._lastStartedSpans[0]
|
|
194
|
+
if (rootSpan) {
|
|
195
|
+
context.rootSpanId = rootSpan.context().toSpanId()
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (this._lastWebTags) {
|
|
199
|
+
context.webTags = this._lastWebTags
|
|
200
|
+
// endpoint may not be determined yet, but keep it as fallback
|
|
201
|
+
// if tags are not available anymore during serialization
|
|
202
|
+
context.endpoint = endpointNameFromTags(this._lastWebTags)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
_spanFinished (span) {
|
|
207
|
+
if (span[CachedWebTags]) {
|
|
208
|
+
span[CachedWebTags] = undefined
|
|
176
209
|
}
|
|
177
210
|
}
|
|
178
211
|
|
|
@@ -188,12 +221,12 @@ class NativeWallProfiler {
|
|
|
188
221
|
|
|
189
222
|
_stop (restart) {
|
|
190
223
|
if (!this._started) return
|
|
191
|
-
if (this.
|
|
224
|
+
if (this._withContexts) {
|
|
192
225
|
// update last sample context if needed
|
|
193
226
|
this._enter()
|
|
194
227
|
this._lastSampleCount = 0
|
|
195
228
|
}
|
|
196
|
-
const profile = this._pprof.time.stop(restart, this.
|
|
229
|
+
const profile = this._pprof.time.stop(restart, this._withContexts ? generateLabels : undefined)
|
|
197
230
|
if (restart) {
|
|
198
231
|
const v8BugDetected = this._pprof.time.v8ProfilerStuckEventLoopDetected()
|
|
199
232
|
if (v8BugDetected !== 0) {
|
|
@@ -215,10 +248,14 @@ class NativeWallProfiler {
|
|
|
215
248
|
if (!this._started) return
|
|
216
249
|
|
|
217
250
|
const profile = this._stop(false)
|
|
218
|
-
if (this.
|
|
251
|
+
if (this._withContexts) {
|
|
219
252
|
beforeCh.unsubscribe(this._enter)
|
|
220
|
-
enterCh.
|
|
253
|
+
enterCh.unsubscribe(this._enter)
|
|
254
|
+
spanFinishCh.unsubscribe(this._spanFinished)
|
|
221
255
|
this._profilerState = undefined
|
|
256
|
+
this._lastSpan = undefined
|
|
257
|
+
this._lastStartedSpans = undefined
|
|
258
|
+
this._lastWebTags = undefined
|
|
222
259
|
}
|
|
223
260
|
|
|
224
261
|
this._started = false
|
|
@@ -4,7 +4,7 @@ const path = require('path')
|
|
|
4
4
|
const parse = require('module-details-from-path')
|
|
5
5
|
const requirePackageJson = require('../require-package-json')
|
|
6
6
|
const { sendData } = require('./send-data')
|
|
7
|
-
const dc = require('
|
|
7
|
+
const dc = require('dc-polyfill')
|
|
8
8
|
const { fileURLToPath } = require('url')
|
|
9
9
|
|
|
10
10
|
const savedDependenciesToSend = new Set()
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const tracerVersion = require('../../../../package.json').version
|
|
4
|
-
const dc = require('
|
|
4
|
+
const dc = require('dc-polyfill')
|
|
5
5
|
const os = require('os')
|
|
6
6
|
const dependencies = require('./dependencies')
|
|
7
7
|
const { sendData } = require('./send-data')
|
|
8
8
|
|
|
9
9
|
const { manager: metricsManager } = require('./metrics')
|
|
10
|
+
const logs = require('./logs')
|
|
10
11
|
|
|
11
12
|
const telemetryStartChannel = dc.channel('datadog:telemetry:start')
|
|
12
13
|
const telemetryStopChannel = dc.channel('datadog:telemetry:stop')
|
|
@@ -139,10 +140,13 @@ function start (aConfig, thePluginManager) {
|
|
|
139
140
|
heartbeatInterval = config.telemetry.heartbeatInterval
|
|
140
141
|
|
|
141
142
|
dependencies.start(config, application, host)
|
|
143
|
+
logs.start(config)
|
|
144
|
+
|
|
142
145
|
sendData(config, application, host, 'app-started', appStarted())
|
|
143
146
|
heartbeat(config, application, host)
|
|
144
147
|
interval = setInterval(() => {
|
|
145
148
|
metricsManager.send(config, application, host)
|
|
149
|
+
logs.send(config, application, host)
|
|
146
150
|
}, heartbeatInterval)
|
|
147
151
|
interval.unref()
|
|
148
152
|
process.on('beforeExit', onBeforeExit)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const dc = require('dc-polyfill')
|
|
4
|
+
const logCollector = require('./log-collector')
|
|
5
|
+
const { sendData } = require('../send-data')
|
|
6
|
+
|
|
7
|
+
const telemetryLog = dc.channel('datadog:telemetry:log')
|
|
8
|
+
|
|
9
|
+
let enabled = false
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Telemetry logs api defines only ERROR, WARN and DEBUG levels:
|
|
13
|
+
* - WARN level is enabled by default
|
|
14
|
+
* - DEBUG level will be possible to activate with an env var or telemetry config property
|
|
15
|
+
*/
|
|
16
|
+
function isLevelEnabled (level) {
|
|
17
|
+
return isValidLevel(level)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function isValidLevel (level) {
|
|
21
|
+
switch (level) {
|
|
22
|
+
case 'ERROR':
|
|
23
|
+
case 'WARN':
|
|
24
|
+
return true
|
|
25
|
+
default:
|
|
26
|
+
return false
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function onLog (log) {
|
|
31
|
+
if (isLevelEnabled(log?.level?.toUpperCase())) {
|
|
32
|
+
logCollector.add(log)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function start (config) {
|
|
37
|
+
if (!config.telemetry.logCollection || enabled) return
|
|
38
|
+
|
|
39
|
+
enabled = true
|
|
40
|
+
|
|
41
|
+
telemetryLog.subscribe(onLog)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function stop () {
|
|
45
|
+
enabled = false
|
|
46
|
+
|
|
47
|
+
if (telemetryLog.hasSubscribers) {
|
|
48
|
+
telemetryLog.unsubscribe(onLog)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function send (config, application, host) {
|
|
53
|
+
if (!enabled) return
|
|
54
|
+
|
|
55
|
+
const logs = logCollector.drain()
|
|
56
|
+
if (logs) {
|
|
57
|
+
sendData(config, application, host, 'logs', logs)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = {
|
|
62
|
+
start,
|
|
63
|
+
stop,
|
|
64
|
+
send
|
|
65
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const log = require('
|
|
3
|
+
const log = require('../../log')
|
|
4
4
|
|
|
5
5
|
const logs = new Map()
|
|
6
6
|
|
|
@@ -18,37 +18,21 @@ function hashCode (hashSource) {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
function createHash (logEntry) {
|
|
21
|
-
if (!logEntry) return 0
|
|
22
|
-
|
|
23
21
|
const prime = 31
|
|
24
22
|
let result = ((!logEntry.level) ? 0 : hashCode(logEntry.level))
|
|
25
23
|
result = (((prime * result) | 0) + ((!logEntry.message) ? 0 : hashCode(logEntry.message))) | 0
|
|
26
|
-
|
|
27
|
-
// NOTE: tags are not used at the moment
|
|
28
|
-
// result = (((prime * result) | 0) + ((!logEntry.tags) ? 0 : hashCode(logEntry.tags))) | 0
|
|
29
24
|
result = (((prime * result) | 0) + ((!logEntry.stack_trace) ? 0 : hashCode(logEntry.stack_trace))) | 0
|
|
30
25
|
return result
|
|
31
26
|
}
|
|
32
27
|
|
|
33
|
-
function newLogEntry (message, level, tags) {
|
|
34
|
-
return {
|
|
35
|
-
message,
|
|
36
|
-
level,
|
|
37
|
-
tags
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
28
|
function isValid (logEntry) {
|
|
42
|
-
return logEntry
|
|
29
|
+
return logEntry?.level && logEntry.message
|
|
43
30
|
}
|
|
44
31
|
|
|
45
32
|
const logCollector = {
|
|
46
33
|
add (logEntry) {
|
|
47
34
|
try {
|
|
48
|
-
if (!isValid(logEntry))
|
|
49
|
-
log.info('IAST log collector discarding invalid log')
|
|
50
|
-
return
|
|
51
|
-
}
|
|
35
|
+
if (!isValid(logEntry)) return false
|
|
52
36
|
|
|
53
37
|
// NOTE: should errors have higher priority? and discard log entries with lower priority?
|
|
54
38
|
if (logs.size >= maxEntries) {
|
|
@@ -70,11 +54,13 @@ const logCollector = {
|
|
|
70
54
|
drain () {
|
|
71
55
|
if (logs.size === 0) return
|
|
72
56
|
|
|
73
|
-
const drained = []
|
|
74
|
-
drained.push(...logs.values())
|
|
57
|
+
const drained = [...logs.values()]
|
|
75
58
|
|
|
76
59
|
if (overflowedCount > 0) {
|
|
77
|
-
drained.push(
|
|
60
|
+
drained.push({
|
|
61
|
+
message: `Omitted ${overflowedCount} entries due to overflowing`,
|
|
62
|
+
level: 'ERROR'
|
|
63
|
+
})
|
|
78
64
|
}
|
|
79
65
|
|
|
80
66
|
this.reset()
|
|
@@ -85,6 +71,7 @@ const logCollector = {
|
|
|
85
71
|
reset (max) {
|
|
86
72
|
logs.clear()
|
|
87
73
|
overflowedCount = 0
|
|
74
|
+
|
|
88
75
|
if (max) {
|
|
89
76
|
maxEntries = max
|
|
90
77
|
}
|
|
@@ -31,8 +31,10 @@ class DatadogTracer extends Tracer {
|
|
|
31
31
|
|
|
32
32
|
// todo[piochelepiotr] These two methods are not related to the tracer, but to data streams monitoring.
|
|
33
33
|
// They should be moved outside of the tracer in the future.
|
|
34
|
-
setCheckpoint (edgeTags) {
|
|
35
|
-
const ctx = this._dataStreamsProcessor.setCheckpoint(
|
|
34
|
+
setCheckpoint (edgeTags, span, payloadSize = 0) {
|
|
35
|
+
const ctx = this._dataStreamsProcessor.setCheckpoint(
|
|
36
|
+
edgeTags, span, DataStreamsContext.getDataStreamsContext(), payloadSize
|
|
37
|
+
)
|
|
36
38
|
DataStreamsContext.setDataStreamsContext(ctx)
|
|
37
39
|
return ctx
|
|
38
40
|
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const dc = require('../../../../../../diagnostics_channel')
|
|
4
|
-
const logCollector = require('./log-collector')
|
|
5
|
-
const { sendData } = require('../../../../telemetry/send-data')
|
|
6
|
-
const log = require('../../../../log')
|
|
7
|
-
|
|
8
|
-
const telemetryStartChannel = dc.channel('datadog:telemetry:start')
|
|
9
|
-
const telemetryStopChannel = dc.channel('datadog:telemetry:stop')
|
|
10
|
-
|
|
11
|
-
let config, application, host, interval
|
|
12
|
-
|
|
13
|
-
function publish (log) {
|
|
14
|
-
if (log && isLevelEnabled(log.level)) {
|
|
15
|
-
logCollector.add(log)
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function sendLogs () {
|
|
20
|
-
try {
|
|
21
|
-
const logs = logCollector.drain()
|
|
22
|
-
if (logs) {
|
|
23
|
-
sendData(config, application, host, 'logs', logs)
|
|
24
|
-
}
|
|
25
|
-
} catch (e) {
|
|
26
|
-
log.error(e)
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function isLevelEnabled (level) {
|
|
31
|
-
return isLogCollectionEnabled(config) && level !== 'DEBUG'
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function isLogCollectionEnabled (config) {
|
|
35
|
-
return config && config.telemetry && config.telemetry.logCollection
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function onTelemetryStart (msg) {
|
|
39
|
-
if (!msg || !isLogCollectionEnabled(msg.config)) {
|
|
40
|
-
log.info('IAST telemetry logs start event received but log collection is not enabled or configuration is incorrect')
|
|
41
|
-
return false
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
log.info('IAST telemetry logs starting')
|
|
45
|
-
|
|
46
|
-
config = msg.config
|
|
47
|
-
application = msg.application
|
|
48
|
-
host = msg.host
|
|
49
|
-
|
|
50
|
-
if (msg.heartbeatInterval) {
|
|
51
|
-
interval = setInterval(sendLogs, msg.heartbeatInterval)
|
|
52
|
-
interval.unref()
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return true
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function onTelemetryStop () {
|
|
59
|
-
stop()
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function start () {
|
|
63
|
-
telemetryStartChannel.subscribe(onTelemetryStart)
|
|
64
|
-
telemetryStopChannel.subscribe(onTelemetryStop)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function stop () {
|
|
68
|
-
if (!isLogCollectionEnabled(config)) return
|
|
69
|
-
|
|
70
|
-
log.info('IAST telemetry logs stopping')
|
|
71
|
-
|
|
72
|
-
config = null
|
|
73
|
-
application = null
|
|
74
|
-
host = null
|
|
75
|
-
|
|
76
|
-
if (telemetryStartChannel.hasSubscribers) {
|
|
77
|
-
telemetryStartChannel.unsubscribe(onTelemetryStart)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (telemetryStopChannel.hasSubscribers) {
|
|
81
|
-
telemetryStopChannel.unsubscribe(onTelemetryStop)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
clearInterval(interval)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
module.exports = { start, stop, publish, isLevelEnabled }
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const {
|
|
4
|
-
Channel,
|
|
5
|
-
channel
|
|
6
|
-
} = require('diagnostics_channel') // eslint-disable-line n/no-restricted-require
|
|
7
|
-
|
|
8
|
-
const [major, minor] = process.versions.node.split('.')
|
|
9
|
-
const channels = new WeakSet()
|
|
10
|
-
|
|
11
|
-
// Our own DC with a limited subset of functionality stable across Node versions.
|
|
12
|
-
// TODO: Move the rest of the polyfill here.
|
|
13
|
-
// TODO: Switch to using global subscribe/unsubscribe/hasSubscribers.
|
|
14
|
-
const dc = { channel }
|
|
15
|
-
|
|
16
|
-
// Prevent going to 0 subscribers to avoid bug in Node.
|
|
17
|
-
// See https://github.com/nodejs/node/pull/47520
|
|
18
|
-
if (major === '19' && minor === '9') {
|
|
19
|
-
dc.channel = function () {
|
|
20
|
-
const ch = channel.apply(this, arguments)
|
|
21
|
-
|
|
22
|
-
if (!channels.has(ch)) {
|
|
23
|
-
const subscribe = ch.subscribe
|
|
24
|
-
const unsubscribe = ch.unsubscribe
|
|
25
|
-
|
|
26
|
-
ch.subscribe = function () {
|
|
27
|
-
delete ch.subscribe
|
|
28
|
-
delete ch.unsubscribe
|
|
29
|
-
|
|
30
|
-
const result = subscribe.apply(this, arguments)
|
|
31
|
-
|
|
32
|
-
this.subscribe(() => {}) // Keep it active forever.
|
|
33
|
-
|
|
34
|
-
return result
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (ch.unsubscribe === Channel.prototype.unsubscribe) {
|
|
38
|
-
// Needed because another subscriber could have subscribed to something
|
|
39
|
-
// that we unsubscribe to before the library is loaded.
|
|
40
|
-
ch.unsubscribe = function () {
|
|
41
|
-
delete ch.subscribe
|
|
42
|
-
delete ch.unsubscribe
|
|
43
|
-
|
|
44
|
-
this.subscribe(() => {}) // Keep it active forever.
|
|
45
|
-
|
|
46
|
-
return unsubscribe.apply(this, arguments)
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
channels.add(ch)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return ch
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (!Channel.prototype.runStores) {
|
|
58
|
-
const ActiveChannelPrototype = getActiveChannelPrototype()
|
|
59
|
-
|
|
60
|
-
Channel.prototype.bindStore = ActiveChannelPrototype.bindStore = function (store, transform) {
|
|
61
|
-
if (!this._stores) {
|
|
62
|
-
this._stores = new Map()
|
|
63
|
-
}
|
|
64
|
-
this._stores.set(store, transform)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
Channel.prototype.unbindStore = ActiveChannelPrototype.unbindStore = function (store) {
|
|
68
|
-
if (!this._stores) return
|
|
69
|
-
this._stores.delete(store)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
Channel.prototype.runStores = ActiveChannelPrototype.runStores = function (data, fn, thisArg, ...args) {
|
|
73
|
-
if (!this._stores) return Reflect.apply(fn, thisArg, args)
|
|
74
|
-
|
|
75
|
-
let run = () => {
|
|
76
|
-
this.publish(data)
|
|
77
|
-
return Reflect.apply(fn, thisArg, args)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
for (const entry of this._stores.entries()) {
|
|
81
|
-
const store = entry[0]
|
|
82
|
-
const transform = entry[1]
|
|
83
|
-
run = wrapStoreRun(store, data, run, transform)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return run()
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function defaultTransform (data) {
|
|
91
|
-
return data
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function wrapStoreRun (store, data, next, transform = defaultTransform) {
|
|
95
|
-
return () => {
|
|
96
|
-
let context
|
|
97
|
-
try {
|
|
98
|
-
context = transform(data)
|
|
99
|
-
} catch (err) {
|
|
100
|
-
process.nextTick(() => {
|
|
101
|
-
throw err
|
|
102
|
-
})
|
|
103
|
-
return next()
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return store.run(context, next)
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function getActiveChannelPrototype () {
|
|
111
|
-
const dummyChannel = channel('foo')
|
|
112
|
-
const listener = () => {}
|
|
113
|
-
|
|
114
|
-
dummyChannel.subscribe(listener)
|
|
115
|
-
const ActiveChannelPrototype = Object.getPrototypeOf(dummyChannel)
|
|
116
|
-
dummyChannel.unsubscribe(listener)
|
|
117
|
-
|
|
118
|
-
return ActiveChannelPrototype
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
module.exports = dc
|