dd-trace 2.14.0 → 2.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 -0
- package/README.md +4 -0
- package/ext/tags.d.ts +2 -1
- package/ext/tags.js +2 -1
- package/index.d.ts +43 -20
- package/package.json +6 -4
- package/packages/datadog-instrumentations/src/crypto.js +32 -0
- package/packages/datadog-instrumentations/src/grpc/server.js +15 -7
- package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
- package/packages/datadog-instrumentations/src/http/server.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +136 -14
- package/packages/datadog-instrumentations/src/mocha.js +77 -31
- package/packages/datadog-instrumentations/src/net.js +13 -0
- package/packages/datadog-instrumentations/src/next.js +7 -3
- package/packages/datadog-plugin-jest/src/index.js +106 -6
- package/packages/datadog-plugin-mocha/src/index.js +15 -7
- package/packages/datadog-plugin-mongodb-core/src/index.js +19 -10
- package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +4 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/index.js +20 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +48 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-cipher-analyzer.js +27 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +24 -0
- package/packages/dd-trace/src/appsec/iast/iast-context.js +50 -0
- package/packages/dd-trace/src/appsec/iast/index.js +59 -0
- package/packages/dd-trace/src/appsec/iast/overhead-controller.js +94 -0
- package/packages/dd-trace/src/appsec/iast/path-line.js +70 -0
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +121 -0
- package/packages/dd-trace/src/appsec/recommended.json +1144 -275
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +3 -3
- package/packages/dd-trace/src/config.js +90 -10
- package/packages/dd-trace/src/constants.js +9 -1
- package/packages/dd-trace/src/encode/0.4.js +7 -1
- package/packages/dd-trace/src/encode/0.5.js +7 -1
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +2 -2
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +32 -20
- package/packages/dd-trace/src/encode/span-stats.js +155 -0
- package/packages/dd-trace/src/exporters/agent/index.js +14 -2
- package/packages/dd-trace/src/exporters/agent/writer.js +6 -3
- package/packages/dd-trace/src/exporters/common/request.js +9 -5
- package/packages/dd-trace/src/exporters/span-stats/index.js +20 -0
- package/packages/dd-trace/src/exporters/span-stats/writer.js +54 -0
- package/packages/dd-trace/src/format.js +2 -0
- package/packages/dd-trace/src/iitm.js +11 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +71 -0
- package/packages/dd-trace/src/opentracing/tracer.js +1 -1
- package/packages/dd-trace/src/plugin_manager.js +12 -2
- package/packages/dd-trace/src/plugins/index.js +3 -0
- package/packages/dd-trace/src/plugins/log_plugin.js +16 -9
- package/packages/dd-trace/src/plugins/util/ip_blocklist.js +51 -0
- package/packages/dd-trace/src/plugins/util/web.js +99 -2
- package/packages/dd-trace/src/priority_sampler.js +36 -1
- package/packages/dd-trace/src/proxy.js +3 -0
- package/packages/dd-trace/src/ritm.js +10 -1
- package/packages/dd-trace/src/span_processor.js +7 -1
- package/packages/dd-trace/src/span_stats.js +210 -0
- package/packages/dd-trace/src/telemetry/dependencies.js +83 -0
- package/packages/dd-trace/src/{telemetry.js → telemetry/index.js} +10 -65
- package/packages/dd-trace/src/telemetry/send-data.js +35 -0
- package/packages/dd-trace/src/plugins/util/redis.js +0 -74
- package/packages/dd-trace/src/plugins/util/tx.js +0 -75
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
const os = require('os')
|
|
2
|
+
const { version } = require('./pkg')
|
|
3
|
+
const pkg = require('../../../package.json')
|
|
4
|
+
|
|
5
|
+
const { LogCollapsingLowestDenseDDSketch } = require('@datadog/sketches-js')
|
|
6
|
+
const { ORIGIN_KEY, TOP_LEVEL_KEY } = require('./constants')
|
|
7
|
+
const {
|
|
8
|
+
MEASURED,
|
|
9
|
+
HTTP_STATUS_CODE
|
|
10
|
+
} = require('../../../ext/tags')
|
|
11
|
+
|
|
12
|
+
const { SpanStatsExporter } = require('./exporters/span-stats')
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
DEFAULT_SPAN_NAME,
|
|
16
|
+
DEFAULT_SERVICE_NAME
|
|
17
|
+
} = require('./encode/tags-processors')
|
|
18
|
+
|
|
19
|
+
class SpanAggStats {
|
|
20
|
+
constructor (aggKey) {
|
|
21
|
+
this.aggKey = aggKey
|
|
22
|
+
this.hits = 0
|
|
23
|
+
this.topLevelHits = 0
|
|
24
|
+
this.errors = 0
|
|
25
|
+
this.duration = 0
|
|
26
|
+
this.okDistribution = new LogCollapsingLowestDenseDDSketch(0.00775)
|
|
27
|
+
this.errorDistribution = new LogCollapsingLowestDenseDDSketch(0.00775)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
record (span) {
|
|
31
|
+
const durationNs = span._duration * 1e6
|
|
32
|
+
this.hits++
|
|
33
|
+
this.duration += durationNs
|
|
34
|
+
|
|
35
|
+
if (span.metrics[TOP_LEVEL_KEY]) {
|
|
36
|
+
this.topLevelHits++
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (span.error) {
|
|
40
|
+
this.errors++
|
|
41
|
+
this.errorDistribution.accept(durationNs)
|
|
42
|
+
} else {
|
|
43
|
+
this.okDistribution.accept(durationNs)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
toJSON () {
|
|
48
|
+
const {
|
|
49
|
+
name,
|
|
50
|
+
service,
|
|
51
|
+
resource,
|
|
52
|
+
type,
|
|
53
|
+
statusCode,
|
|
54
|
+
synthetics
|
|
55
|
+
} = this.aggKey
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
Name: name,
|
|
59
|
+
Service: service,
|
|
60
|
+
Resource: resource,
|
|
61
|
+
Type: type,
|
|
62
|
+
HTTPStatusCode: statusCode,
|
|
63
|
+
Synthetics: synthetics,
|
|
64
|
+
Hits: this.hits,
|
|
65
|
+
TopLevelHits: this.topLevelHits,
|
|
66
|
+
Errors: this.errors,
|
|
67
|
+
Duration: this.duration,
|
|
68
|
+
OkSummary: this.okDistribution.toProto(),
|
|
69
|
+
ErrorSummary: this.errorDistribution.toProto()
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
class SpanAggKey {
|
|
75
|
+
constructor (span) {
|
|
76
|
+
this.name = span.name || DEFAULT_SPAN_NAME
|
|
77
|
+
this.service = span.service || DEFAULT_SERVICE_NAME
|
|
78
|
+
this.resource = span.resource || ''
|
|
79
|
+
this.type = span.type || ''
|
|
80
|
+
this.statusCode = span.meta[HTTP_STATUS_CODE] || 0
|
|
81
|
+
this.synthetics = span.meta[ORIGIN_KEY] === 'synthetics'
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
toString () {
|
|
85
|
+
return [
|
|
86
|
+
this.name,
|
|
87
|
+
this.service,
|
|
88
|
+
this.resource,
|
|
89
|
+
this.type,
|
|
90
|
+
this.statusCode,
|
|
91
|
+
this.synthetics
|
|
92
|
+
].join(',')
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
class SpanBuckets extends Map {
|
|
97
|
+
forSpan (span) {
|
|
98
|
+
const aggKey = new SpanAggKey(span)
|
|
99
|
+
const key = aggKey.toString()
|
|
100
|
+
|
|
101
|
+
if (!this.has(key)) {
|
|
102
|
+
this.set(key, new SpanAggStats(aggKey))
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return this.get(key)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
class TimeBuckets extends Map {
|
|
110
|
+
forTime (time) {
|
|
111
|
+
if (!this.has(time)) {
|
|
112
|
+
this.set(time, new SpanBuckets())
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return this.get(time)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
class SpanStatsProcessor {
|
|
120
|
+
constructor ({
|
|
121
|
+
stats: {
|
|
122
|
+
enabled = false,
|
|
123
|
+
interval = 10
|
|
124
|
+
},
|
|
125
|
+
hostname,
|
|
126
|
+
port,
|
|
127
|
+
url,
|
|
128
|
+
env,
|
|
129
|
+
tags
|
|
130
|
+
} = {}) {
|
|
131
|
+
this.exporter = new SpanStatsExporter({
|
|
132
|
+
hostname,
|
|
133
|
+
port,
|
|
134
|
+
tags,
|
|
135
|
+
url
|
|
136
|
+
})
|
|
137
|
+
this.interval = interval
|
|
138
|
+
this.bucketSizeNs = interval * 1e9
|
|
139
|
+
this.buckets = new TimeBuckets()
|
|
140
|
+
this.hostname = os.hostname()
|
|
141
|
+
this.enabled = enabled
|
|
142
|
+
this.env = env
|
|
143
|
+
this.tags = tags || {}
|
|
144
|
+
this.sequence = 0
|
|
145
|
+
|
|
146
|
+
if (enabled) {
|
|
147
|
+
this.timer = setInterval(this.onInterval.bind(this), interval * 1e3)
|
|
148
|
+
this.timer.unref()
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
onInterval () {
|
|
153
|
+
const serialized = this._serializeBuckets()
|
|
154
|
+
if (!serialized) return
|
|
155
|
+
|
|
156
|
+
this.exporter.export({
|
|
157
|
+
Hostname: this.hostname,
|
|
158
|
+
Env: this.env,
|
|
159
|
+
Version: version,
|
|
160
|
+
Stats: serialized,
|
|
161
|
+
Lang: 'javascript',
|
|
162
|
+
TracerVersion: pkg.version,
|
|
163
|
+
RuntimeID: this.tags['runtime-id'],
|
|
164
|
+
Sequence: ++this.sequence
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
onSpanFinished (span) {
|
|
169
|
+
if (!this.enabled) return
|
|
170
|
+
if (!span.metrics[TOP_LEVEL_KEY] && !span.metrics[MEASURED]) return
|
|
171
|
+
|
|
172
|
+
const spanEndNs = span.startTime + span.duration
|
|
173
|
+
const bucketTime = spanEndNs - (spanEndNs % this.bucketSizeNs)
|
|
174
|
+
|
|
175
|
+
this.buckets.forTime(bucketTime)
|
|
176
|
+
.forSpan(span)
|
|
177
|
+
.record(span)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
_serializeBuckets () {
|
|
181
|
+
const { bucketSizeNs } = this
|
|
182
|
+
const serializedBuckets = []
|
|
183
|
+
|
|
184
|
+
for (const [ timeNs, bucket ] of this.buckets.entries()) {
|
|
185
|
+
const bucketAggStats = []
|
|
186
|
+
|
|
187
|
+
for (const stats of bucket.values()) {
|
|
188
|
+
bucketAggStats.push(stats.toJSON())
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
serializedBuckets.push({
|
|
192
|
+
Start: timeNs,
|
|
193
|
+
Duration: bucketSizeNs,
|
|
194
|
+
Stats: bucketAggStats
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
this.buckets.clear()
|
|
199
|
+
|
|
200
|
+
return serializedBuckets
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
module.exports = {
|
|
205
|
+
SpanAggStats,
|
|
206
|
+
SpanAggKey,
|
|
207
|
+
SpanBuckets,
|
|
208
|
+
TimeBuckets,
|
|
209
|
+
SpanStatsProcessor
|
|
210
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const parse = require('module-details-from-path')
|
|
5
|
+
const requirePackageJson = require('../require-package-json')
|
|
6
|
+
const { sendData } = require('./send-data')
|
|
7
|
+
const dc = require('diagnostics_channel')
|
|
8
|
+
const { fileURLToPath } = require('url')
|
|
9
|
+
|
|
10
|
+
const savedDependencies = []
|
|
11
|
+
const detectedDependencyNames = new Set()
|
|
12
|
+
const FILE_URI_START = `file://`
|
|
13
|
+
const moduleLoadStartChannel = dc.channel('dd-trace:moduleLoadStart')
|
|
14
|
+
|
|
15
|
+
let immediate, config, application, host
|
|
16
|
+
|
|
17
|
+
function waitAndSend (config, application, host) {
|
|
18
|
+
if (!immediate) {
|
|
19
|
+
immediate = setImmediate(() => {
|
|
20
|
+
immediate = null
|
|
21
|
+
if (savedDependencies.length > 0) {
|
|
22
|
+
const dependencies = savedDependencies.splice(0, 1000)
|
|
23
|
+
sendData(config, application, host, 'app-dependencies-loaded', { dependencies })
|
|
24
|
+
if (savedDependencies.length > 0) {
|
|
25
|
+
waitAndSend(config, application, host)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
immediate.unref()
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function onModuleLoad (data) {
|
|
34
|
+
if (data) {
|
|
35
|
+
let filename = data.filename
|
|
36
|
+
if (filename && filename.startsWith(FILE_URI_START)) {
|
|
37
|
+
try {
|
|
38
|
+
filename = fileURLToPath(filename)
|
|
39
|
+
} catch (e) {
|
|
40
|
+
// cannot transform url to path
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const parseResult = filename && parse(filename)
|
|
44
|
+
const request = data.request || (parseResult && parseResult.name)
|
|
45
|
+
if (filename && request && isDependency(filename, request) && !detectedDependencyNames.has(request)) {
|
|
46
|
+
detectedDependencyNames.add(request)
|
|
47
|
+
if (parseResult) {
|
|
48
|
+
const { name, basedir } = parseResult
|
|
49
|
+
if (basedir) {
|
|
50
|
+
try {
|
|
51
|
+
const { version } = requirePackageJson(basedir, module)
|
|
52
|
+
savedDependencies.push({ name, version })
|
|
53
|
+
waitAndSend(config, application, host)
|
|
54
|
+
} catch (e) {
|
|
55
|
+
// can not read the package.json, do nothing
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function start (_config, _application, _host) {
|
|
63
|
+
config = _config
|
|
64
|
+
application = _application
|
|
65
|
+
host = _host
|
|
66
|
+
moduleLoadStartChannel.subscribe(onModuleLoad)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function isDependency (filename, request) {
|
|
70
|
+
return request.indexOf(`.${path.sep}`) !== 0 && request.indexOf(path.sep) !== 0
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function stop () {
|
|
74
|
+
config = null
|
|
75
|
+
application = null
|
|
76
|
+
host = null
|
|
77
|
+
detectedDependencyNames.clear()
|
|
78
|
+
savedDependencies.splice(0, savedDependencies.length)
|
|
79
|
+
if (moduleLoadStartChannel.hasSubscribers) {
|
|
80
|
+
moduleLoadStartChannel.unsubscribe(onModuleLoad)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
module.exports = { start, stop }
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const tracerVersion = require('
|
|
4
|
-
const
|
|
5
|
-
const containerId = require('./exporters/common/docker').id()
|
|
6
|
-
const requirePackageJson = require('./require-package-json')
|
|
7
|
-
const path = require('path')
|
|
3
|
+
const tracerVersion = require('../../../../package.json').version
|
|
4
|
+
const containerId = require('../exporters/common/docker').id()
|
|
8
5
|
const os = require('os')
|
|
9
|
-
const
|
|
6
|
+
const dependencies = require('./dependencies')
|
|
7
|
+
const { sendData } = require('./send-data')
|
|
10
8
|
|
|
11
9
|
let config
|
|
12
10
|
let pluginManager
|
|
13
11
|
|
|
14
|
-
let seqId = 0
|
|
15
12
|
let application
|
|
16
13
|
let host
|
|
17
14
|
let interval
|
|
@@ -33,27 +30,6 @@ function getIntegrations () {
|
|
|
33
30
|
return newIntegrations
|
|
34
31
|
}
|
|
35
32
|
|
|
36
|
-
function getDependencies () {
|
|
37
|
-
const deps = []
|
|
38
|
-
const { dependencies } = pkg
|
|
39
|
-
if (!dependencies) {
|
|
40
|
-
return deps
|
|
41
|
-
}
|
|
42
|
-
const rootDir = pkg.findRoot()
|
|
43
|
-
for (const [name, version] of Object.entries(dependencies)) {
|
|
44
|
-
const dep = { name }
|
|
45
|
-
try {
|
|
46
|
-
dep.version = requirePackageJson(
|
|
47
|
-
path.join(rootDir, 'node_modules', name.replace('/', path.sep))
|
|
48
|
-
).version
|
|
49
|
-
} catch (e) {
|
|
50
|
-
dep.version = version
|
|
51
|
-
}
|
|
52
|
-
deps.push(dep)
|
|
53
|
-
}
|
|
54
|
-
return deps
|
|
55
|
-
}
|
|
56
|
-
|
|
57
33
|
function flatten (input, result = [], prefix = [], traversedObjects = null) {
|
|
58
34
|
traversedObjects = traversedObjects || new WeakSet()
|
|
59
35
|
if (traversedObjects.has(input)) {
|
|
@@ -73,7 +49,7 @@ function flatten (input, result = [], prefix = [], traversedObjects = null) {
|
|
|
73
49
|
function appStarted () {
|
|
74
50
|
return {
|
|
75
51
|
integrations: getIntegrations(),
|
|
76
|
-
dependencies:
|
|
52
|
+
dependencies: [],
|
|
77
53
|
configuration: flatten(config),
|
|
78
54
|
additional_payload: []
|
|
79
55
|
}
|
|
@@ -81,7 +57,7 @@ function appStarted () {
|
|
|
81
57
|
|
|
82
58
|
function onBeforeExit () {
|
|
83
59
|
process.removeListener('beforeExit', onBeforeExit)
|
|
84
|
-
sendData('app-closing')
|
|
60
|
+
sendData(config, application, host, 'app-closing')
|
|
85
61
|
}
|
|
86
62
|
|
|
87
63
|
function createAppObject () {
|
|
@@ -102,38 +78,6 @@ function createHostObject () {
|
|
|
102
78
|
}
|
|
103
79
|
}
|
|
104
80
|
|
|
105
|
-
function sendData (reqType, payload = {}) {
|
|
106
|
-
const {
|
|
107
|
-
hostname,
|
|
108
|
-
port
|
|
109
|
-
} = config
|
|
110
|
-
const options = {
|
|
111
|
-
hostname,
|
|
112
|
-
port,
|
|
113
|
-
method: 'POST',
|
|
114
|
-
path: '/telemetry/proxy/api/v2/apmtelemetry',
|
|
115
|
-
headers: {
|
|
116
|
-
'content-type': 'application/json',
|
|
117
|
-
'dd-telemetry-api-version': 'v1',
|
|
118
|
-
'dd-telemetry-request-type': reqType
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
const data = JSON.stringify({
|
|
122
|
-
api_version: 'v1',
|
|
123
|
-
request_type: reqType,
|
|
124
|
-
tracer_time: Math.floor(Date.now() / 1000),
|
|
125
|
-
runtime_id: config.tags['runtime-id'],
|
|
126
|
-
seq_id: ++seqId,
|
|
127
|
-
payload,
|
|
128
|
-
application,
|
|
129
|
-
host
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
request(data, options, () => {
|
|
133
|
-
// ignore errors
|
|
134
|
-
})
|
|
135
|
-
}
|
|
136
|
-
|
|
137
81
|
function start (aConfig, thePluginManager) {
|
|
138
82
|
if (!aConfig.telemetryEnabled) {
|
|
139
83
|
return
|
|
@@ -142,8 +86,9 @@ function start (aConfig, thePluginManager) {
|
|
|
142
86
|
pluginManager = thePluginManager
|
|
143
87
|
application = createAppObject()
|
|
144
88
|
host = createHostObject()
|
|
145
|
-
|
|
146
|
-
|
|
89
|
+
dependencies.start(config, application, host)
|
|
90
|
+
sendData(config, application, host, 'app-started', appStarted())
|
|
91
|
+
interval = setInterval(() => sendData(config, application, host, 'app-heartbeat'), 60000)
|
|
147
92
|
interval.unref()
|
|
148
93
|
process.on('beforeExit', onBeforeExit)
|
|
149
94
|
}
|
|
@@ -164,7 +109,7 @@ function updateIntegrations () {
|
|
|
164
109
|
if (integrations.length === 0) {
|
|
165
110
|
return
|
|
166
111
|
}
|
|
167
|
-
sendData('app-integrations-change', { integrations })
|
|
112
|
+
sendData(config, application, host, 'app-integrations-change', { integrations })
|
|
168
113
|
}
|
|
169
114
|
|
|
170
115
|
module.exports = {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const request = require('../exporters/common/request')
|
|
2
|
+
let seqId = 0
|
|
3
|
+
function sendData (config, application, host, reqType, payload = {}) {
|
|
4
|
+
const {
|
|
5
|
+
hostname,
|
|
6
|
+
port
|
|
7
|
+
} = config
|
|
8
|
+
const options = {
|
|
9
|
+
hostname,
|
|
10
|
+
port,
|
|
11
|
+
method: 'POST',
|
|
12
|
+
path: '/telemetry/proxy/api/v2/apmtelemetry',
|
|
13
|
+
headers: {
|
|
14
|
+
'content-type': 'application/json',
|
|
15
|
+
'dd-telemetry-api-version': 'v1',
|
|
16
|
+
'dd-telemetry-request-type': reqType
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const data = JSON.stringify({
|
|
20
|
+
api_version: 'v1',
|
|
21
|
+
request_type: reqType,
|
|
22
|
+
tracer_time: Math.floor(Date.now() / 1000),
|
|
23
|
+
runtime_id: config.tags['runtime-id'],
|
|
24
|
+
seq_id: ++seqId,
|
|
25
|
+
payload,
|
|
26
|
+
application,
|
|
27
|
+
host
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
request(data, options, () => {
|
|
31
|
+
// ignore errors
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = { sendData }
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const analyticsSampler = require('../../analytics_sampler')
|
|
4
|
-
const urlFilter = require('../util/urlfilter')
|
|
5
|
-
const tx = require('./tx')
|
|
6
|
-
|
|
7
|
-
const redis = {
|
|
8
|
-
// Ensure the configuration has the correct structure and defaults.
|
|
9
|
-
normalizeConfig (config) {
|
|
10
|
-
const filter = urlFilter.getFilter(config)
|
|
11
|
-
|
|
12
|
-
return Object.assign({}, config, {
|
|
13
|
-
filter
|
|
14
|
-
})
|
|
15
|
-
},
|
|
16
|
-
|
|
17
|
-
// Start a span for a Redis command.
|
|
18
|
-
instrument (tracer, config, db, command, args) {
|
|
19
|
-
const childOf = tracer.scope().active()
|
|
20
|
-
const span = tracer.startSpan('redis.command', {
|
|
21
|
-
childOf,
|
|
22
|
-
tags: {
|
|
23
|
-
'span.kind': 'client',
|
|
24
|
-
'resource.name': command,
|
|
25
|
-
'span.type': 'redis',
|
|
26
|
-
'db.type': 'redis',
|
|
27
|
-
'db.name': db || '0',
|
|
28
|
-
'redis.raw_command': formatCommand(command, args)
|
|
29
|
-
}
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
span.setTag('service.name', config.service || `${span.context()._tags['service.name']}-redis`)
|
|
33
|
-
|
|
34
|
-
analyticsSampler.sample(span, config.measured)
|
|
35
|
-
|
|
36
|
-
return span
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function formatCommand (command, args) {
|
|
41
|
-
command = command.toUpperCase()
|
|
42
|
-
|
|
43
|
-
if (!args || command === 'AUTH') return command
|
|
44
|
-
|
|
45
|
-
for (let i = 0, l = args.length; i < l; i++) {
|
|
46
|
-
if (typeof args[i] === 'function') continue
|
|
47
|
-
|
|
48
|
-
command = `${command} ${formatArg(args[i])}`
|
|
49
|
-
|
|
50
|
-
if (command.length > 1000) return trim(command, 1000)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return command
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function formatArg (arg) {
|
|
57
|
-
switch (typeof arg) {
|
|
58
|
-
case 'string':
|
|
59
|
-
case 'number':
|
|
60
|
-
return trim(String(arg), 100)
|
|
61
|
-
default:
|
|
62
|
-
return '?'
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function trim (str, maxlen) {
|
|
67
|
-
if (str.length > maxlen) {
|
|
68
|
-
str = str.substr(0, maxlen - 3) + '...'
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return str
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
module.exports = Object.assign({}, tx, redis)
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const tx = {
|
|
4
|
-
// Set the outgoing host.
|
|
5
|
-
setHost (span, hostname, port) {
|
|
6
|
-
hostname && span.setTag('out.host', hostname)
|
|
7
|
-
port && span.setTag('out.port', port)
|
|
8
|
-
},
|
|
9
|
-
|
|
10
|
-
// Wrap a promise or a callback to also finish the span.
|
|
11
|
-
wrap (span, done) {
|
|
12
|
-
if (typeof done === 'function' || !done) {
|
|
13
|
-
return wrapCallback(span, done)
|
|
14
|
-
} else if (isPromise(done)) {
|
|
15
|
-
return wrapPromise(span, done)
|
|
16
|
-
} else if (done && done.length) {
|
|
17
|
-
return wrapArguments(span, done)
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function wrapCallback (span, callback) {
|
|
23
|
-
const scope = span.tracer().scope()
|
|
24
|
-
const previous = scope.active()
|
|
25
|
-
|
|
26
|
-
return function (err) {
|
|
27
|
-
finish(span, err)
|
|
28
|
-
|
|
29
|
-
if (callback) {
|
|
30
|
-
return scope.activate(previous, () => callback.apply(this, arguments))
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function wrapPromise (span, promise) {
|
|
36
|
-
promise.then(
|
|
37
|
-
() => finish(span),
|
|
38
|
-
err => finish(span, err)
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
return promise
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function wrapArguments (span, args) {
|
|
45
|
-
const lastIndex = args.length - 1
|
|
46
|
-
const callback = args[lastIndex]
|
|
47
|
-
|
|
48
|
-
if (typeof callback === 'function') {
|
|
49
|
-
args[lastIndex] = wrapCallback(span, args[lastIndex])
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return args
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function finish (span, error) {
|
|
56
|
-
if (error) {
|
|
57
|
-
span.addTags({
|
|
58
|
-
'error.type': error.name,
|
|
59
|
-
'error.msg': error.message,
|
|
60
|
-
'error.stack': error.stack
|
|
61
|
-
})
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
span.finish()
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function isPromise (obj) {
|
|
68
|
-
return isObject(obj) && typeof obj.then === 'function'
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function isObject (obj) {
|
|
72
|
-
return typeof obj === 'object' && obj !== null
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
module.exports = tx
|