dd-trace 3.24.0 → 3.26.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 +4 -3
- package/index.d.ts +27 -0
- package/package.json +4 -4
- package/packages/datadog-instrumentations/src/aws-sdk.js +5 -0
- package/packages/datadog-instrumentations/src/cassandra-driver.js +6 -3
- package/packages/datadog-instrumentations/src/elasticsearch.js +39 -1
- package/packages/datadog-instrumentations/src/express.js +23 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
- package/packages/datadog-instrumentations/src/kafkajs.js +2 -2
- package/packages/datadog-instrumentations/src/openai.js +50 -0
- package/packages/datadog-instrumentations/src/opensearch.js +2 -1
- package/packages/datadog-instrumentations/src/passport-http.js +22 -0
- package/packages/datadog-instrumentations/src/passport-local.js +22 -0
- package/packages/datadog-instrumentations/src/passport-utils.js +36 -0
- package/packages/datadog-instrumentations/src/pg.js +17 -4
- package/packages/datadog-plugin-aws-sdk/src/base.js +3 -3
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -0
- package/packages/datadog-plugin-cassandra-driver/src/index.js +6 -6
- package/packages/datadog-plugin-dns/src/lookup.js +1 -1
- package/packages/datadog-plugin-elasticsearch/src/index.js +2 -2
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +1 -1
- package/packages/datadog-plugin-graphql/src/parse.js +1 -1
- package/packages/datadog-plugin-graphql/src/resolve.js +0 -5
- package/packages/datadog-plugin-graphql/src/validate.js +1 -1
- package/packages/datadog-plugin-grpc/src/client.js +9 -3
- package/packages/datadog-plugin-grpc/src/server.js +3 -3
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-http/src/server.js +38 -34
- package/packages/datadog-plugin-http2/src/client.js +0 -5
- package/packages/datadog-plugin-http2/src/server.js +23 -23
- package/packages/datadog-plugin-kafkajs/src/consumer.js +6 -1
- package/packages/datadog-plugin-kafkajs/src/producer.js +8 -1
- package/packages/datadog-plugin-mocha/src/index.js +3 -3
- package/packages/datadog-plugin-moleculer/src/client.js +3 -3
- package/packages/datadog-plugin-moleculer/src/server.js +2 -2
- package/packages/datadog-plugin-mongodb-core/src/index.js +15 -4
- package/packages/datadog-plugin-next/src/index.js +50 -52
- package/packages/datadog-plugin-openai/src/index.js +685 -0
- package/packages/datadog-plugin-openai/src/services.js +43 -0
- package/packages/datadog-plugin-oracledb/src/index.js +3 -10
- package/packages/datadog-plugin-pg/src/index.js +3 -11
- package/packages/datadog-plugin-sharedb/src/index.js +1 -1
- package/packages/dd-trace/src/appsec/channels.js +1 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/origin-types.js +3 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +12 -2
- package/packages/dd-trace/src/appsec/index.js +20 -0
- package/packages/dd-trace/src/appsec/passport.js +110 -0
- package/packages/dd-trace/src/appsec/sdk/track_event.js +14 -5
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +17 -4
- package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +45 -0
- package/packages/dd-trace/src/config.js +38 -1
- package/packages/dd-trace/src/constants.js +2 -0
- package/packages/dd-trace/src/data_streams_context.js +15 -0
- package/packages/dd-trace/src/datastreams/pathway.js +58 -0
- package/packages/dd-trace/src/datastreams/processor.js +194 -0
- package/packages/dd-trace/src/datastreams/writer.js +66 -0
- package/packages/dd-trace/src/dogstatsd.js +12 -4
- package/packages/dd-trace/src/external-logger/src/index.js +4 -0
- package/packages/dd-trace/src/opentelemetry/span.js +1 -0
- package/packages/dd-trace/src/opentracing/span.js +32 -0
- package/packages/dd-trace/src/opentracing/tracer.js +3 -1
- package/packages/dd-trace/src/plugin_manager.js +7 -2
- package/packages/dd-trace/src/plugins/client.js +1 -0
- package/packages/dd-trace/src/plugins/database.js +2 -1
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/outbound.js +59 -1
- package/packages/dd-trace/src/plugins/server.js +2 -0
- package/packages/dd-trace/src/plugins/tracing.js +5 -1
- package/packages/dd-trace/src/plugins/util/exec.js +2 -0
- package/packages/dd-trace/src/plugins/util/git.js +38 -10
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +36 -2
- package/packages/dd-trace/src/profiling/config.js +34 -7
- package/packages/dd-trace/src/proxy.js +6 -0
- package/packages/dd-trace/src/service-naming/index.js +13 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/index.js +2 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +34 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/web.js +27 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/index.js +2 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +31 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +26 -0
- package/packages/dd-trace/src/telemetry/index.js +3 -0
- package/packages/dd-trace/src/telemetry/metrics.js +281 -0
- package/packages/dd-trace/src/tracer.js +19 -1
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
const os = require('os')
|
|
2
|
+
const pkg = require('../../../../package.json')
|
|
3
|
+
// Message pack int encoding is done in big endian, but data streams uses little endian
|
|
4
|
+
const Uint64 = require('int64-buffer').Uint64BE
|
|
5
|
+
|
|
6
|
+
const { LogCollapsingLowestDenseDDSketch } = require('@datadog/sketches-js')
|
|
7
|
+
|
|
8
|
+
const { DataStreamsWriter } = require('./writer')
|
|
9
|
+
const { computePathwayHash } = require('./pathway')
|
|
10
|
+
const ENTRY_PARENT_HASH = Buffer.from('0000000000000000', 'hex')
|
|
11
|
+
|
|
12
|
+
const HIGH_ACCURACY_DISTRIBUTION = 0.0075
|
|
13
|
+
|
|
14
|
+
class StatsPoint {
|
|
15
|
+
constructor (hash, parentHash, edgeTags) {
|
|
16
|
+
this.hash = new Uint64(hash)
|
|
17
|
+
this.parentHash = new Uint64(parentHash)
|
|
18
|
+
this.edgeTags = edgeTags
|
|
19
|
+
this.edgeLatency = new LogCollapsingLowestDenseDDSketch(HIGH_ACCURACY_DISTRIBUTION)
|
|
20
|
+
this.pathwayLatency = new LogCollapsingLowestDenseDDSketch(HIGH_ACCURACY_DISTRIBUTION)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
addLatencies (checkpoint) {
|
|
24
|
+
const edgeLatencySec = checkpoint.edgeLatencyNs / 1e9
|
|
25
|
+
const pathwayLatencySec = checkpoint.pathwayLatencyNs / 1e9
|
|
26
|
+
this.edgeLatency.accept(edgeLatencySec)
|
|
27
|
+
this.pathwayLatency.accept(pathwayLatencySec)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
encode () {
|
|
31
|
+
return {
|
|
32
|
+
Hash: this.hash,
|
|
33
|
+
ParentHash: this.parentHash,
|
|
34
|
+
EdgeTags: this.edgeTags,
|
|
35
|
+
EdgeLatency: this.edgeLatency.toProto(),
|
|
36
|
+
PathwayLatency: this.pathwayLatency.toProto()
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
class StatsBucket extends Map {
|
|
42
|
+
forCheckpoint (checkpoint) {
|
|
43
|
+
const key = checkpoint.hash
|
|
44
|
+
if (!this.has(key)) {
|
|
45
|
+
this.set(key, new StatsPoint(checkpoint.hash, checkpoint.parentHash, checkpoint.edgeTags)) // StatsPoint
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return this.get(key)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
class TimeBuckets extends Map {
|
|
53
|
+
forTime (time) {
|
|
54
|
+
if (!this.has(time)) {
|
|
55
|
+
this.set(time, new StatsBucket())
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return this.get(time)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
class DataStreamsProcessor {
|
|
63
|
+
constructor ({
|
|
64
|
+
dsmEnabled,
|
|
65
|
+
hostname,
|
|
66
|
+
port,
|
|
67
|
+
url,
|
|
68
|
+
env,
|
|
69
|
+
tags
|
|
70
|
+
} = {}) {
|
|
71
|
+
this.writer = new DataStreamsWriter({
|
|
72
|
+
hostname,
|
|
73
|
+
port,
|
|
74
|
+
url
|
|
75
|
+
})
|
|
76
|
+
this.bucketSizeNs = 1e10
|
|
77
|
+
this.buckets = new TimeBuckets()
|
|
78
|
+
this.hostname = os.hostname()
|
|
79
|
+
this.enabled = dsmEnabled
|
|
80
|
+
this.env = env
|
|
81
|
+
this.tags = tags || {}
|
|
82
|
+
this.service = this.tags.service || 'unnamed-nodejs-service'
|
|
83
|
+
this.sequence = 0
|
|
84
|
+
|
|
85
|
+
if (this.enabled) {
|
|
86
|
+
this.timer = setInterval(this.onInterval.bind(this), 10000)
|
|
87
|
+
this.timer.unref()
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
onInterval () {
|
|
92
|
+
const serialized = this._serializeBuckets()
|
|
93
|
+
if (!serialized) return
|
|
94
|
+
const payload = {
|
|
95
|
+
Env: this.env,
|
|
96
|
+
Service: this.service,
|
|
97
|
+
Stats: serialized,
|
|
98
|
+
TracerVersion: pkg.version,
|
|
99
|
+
Lang: 'javascript'
|
|
100
|
+
}
|
|
101
|
+
this.writer.flush(payload)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
recordCheckpoint (checkpoint) {
|
|
105
|
+
if (!this.enabled) return
|
|
106
|
+
const bucketTime = Math.round(checkpoint.currentTimestamp - (checkpoint.currentTimestamp % this.bucketSizeNs))
|
|
107
|
+
this.buckets.forTime(bucketTime)
|
|
108
|
+
.forCheckpoint(checkpoint)
|
|
109
|
+
.addLatencies(checkpoint)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
setCheckpoint (edgeTags, ctx = null) {
|
|
113
|
+
if (!this.enabled) return null
|
|
114
|
+
const nowNs = Date.now() * 1e6
|
|
115
|
+
const direction = edgeTags.find(t => t.startsWith('direction:'))
|
|
116
|
+
let pathwayStartNs = nowNs
|
|
117
|
+
let edgeStartNs = nowNs
|
|
118
|
+
let parentHash = ENTRY_PARENT_HASH
|
|
119
|
+
let closestOppositeDirectionHash = ENTRY_PARENT_HASH
|
|
120
|
+
let closestOppositeDirectionEdgeStart = nowNs
|
|
121
|
+
if (ctx != null) {
|
|
122
|
+
pathwayStartNs = ctx.pathwayStartNs
|
|
123
|
+
edgeStartNs = ctx.edgeStartNs
|
|
124
|
+
parentHash = ctx.hash
|
|
125
|
+
closestOppositeDirectionHash = ctx.closestOppositeDirectionHash || ENTRY_PARENT_HASH
|
|
126
|
+
closestOppositeDirectionEdgeStart = ctx.closestOppositeDirectionEdgeStart || nowNs
|
|
127
|
+
if (direction === ctx.previousDirection) {
|
|
128
|
+
parentHash = ctx.closestOppositeDirectionHash
|
|
129
|
+
if (parentHash === ENTRY_PARENT_HASH) {
|
|
130
|
+
// if the closest hash from opposite direction is the entry hash, that means
|
|
131
|
+
// we produce in a loop, without consuming
|
|
132
|
+
// in that case, we don't want the pathway to be longer and longer, but we want to restart a new pathway.
|
|
133
|
+
edgeStartNs = nowNs
|
|
134
|
+
pathwayStartNs = nowNs
|
|
135
|
+
} else {
|
|
136
|
+
edgeStartNs = ctx.closestOppositeDirectionEdgeStart
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
closestOppositeDirectionHash = parentHash
|
|
140
|
+
closestOppositeDirectionEdgeStart = edgeStartNs
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const hash = computePathwayHash(this.service, this.env, edgeTags, parentHash)
|
|
144
|
+
const edgeLatencyNs = nowNs - edgeStartNs
|
|
145
|
+
const pathwayLatencyNs = nowNs - pathwayStartNs
|
|
146
|
+
const checkpoint = {
|
|
147
|
+
currentTimestamp: nowNs,
|
|
148
|
+
parentHash: parentHash,
|
|
149
|
+
hash: hash,
|
|
150
|
+
edgeTags: edgeTags,
|
|
151
|
+
edgeLatencyNs: edgeLatencyNs,
|
|
152
|
+
pathwayLatencyNs: pathwayLatencyNs
|
|
153
|
+
}
|
|
154
|
+
this.recordCheckpoint(checkpoint)
|
|
155
|
+
return {
|
|
156
|
+
hash: hash,
|
|
157
|
+
edgeStartNs: edgeStartNs,
|
|
158
|
+
pathwayStartNs: pathwayStartNs,
|
|
159
|
+
previousDirection: direction,
|
|
160
|
+
closestOppositeDirectionHash: closestOppositeDirectionHash,
|
|
161
|
+
closestOppositeDirectionEdgeStart: closestOppositeDirectionEdgeStart
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
_serializeBuckets () {
|
|
166
|
+
const serializedBuckets = []
|
|
167
|
+
|
|
168
|
+
for (const [ timeNs, bucket ] of this.buckets.entries()) {
|
|
169
|
+
const points = []
|
|
170
|
+
|
|
171
|
+
for (const stats of bucket.values()) {
|
|
172
|
+
points.push(stats.encode())
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
serializedBuckets.push({
|
|
176
|
+
Start: new Uint64(timeNs),
|
|
177
|
+
Duration: new Uint64(this.bucketSizeNs),
|
|
178
|
+
Stats: points
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
this.buckets.clear()
|
|
183
|
+
|
|
184
|
+
return serializedBuckets
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
module.exports = {
|
|
189
|
+
DataStreamsProcessor: DataStreamsProcessor,
|
|
190
|
+
StatsPoint: StatsPoint,
|
|
191
|
+
StatsBucket: StatsBucket,
|
|
192
|
+
TimeBuckets,
|
|
193
|
+
ENTRY_PARENT_HASH
|
|
194
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const pkg = require('../../../../package.json')
|
|
2
|
+
const log = require('../log')
|
|
3
|
+
const request = require('../exporters/common/request')
|
|
4
|
+
const { URL, format } = require('url')
|
|
5
|
+
const msgpack = require('msgpack-lite')
|
|
6
|
+
const zlib = require('zlib')
|
|
7
|
+
const codec = msgpack.createCodec({ int64: true })
|
|
8
|
+
|
|
9
|
+
function makeRequest (data, url, cb) {
|
|
10
|
+
const options = {
|
|
11
|
+
path: '/v0.1/pipeline_stats',
|
|
12
|
+
method: 'POST',
|
|
13
|
+
headers: {
|
|
14
|
+
'Datadog-Meta-Lang': 'javascript',
|
|
15
|
+
'Datadog-Meta-Tracer-Version': pkg.version,
|
|
16
|
+
'Content-Type': 'application/msgpack',
|
|
17
|
+
'Content-Encoding': 'gzip'
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
options.protocol = url.protocol
|
|
22
|
+
options.hostname = url.hostname
|
|
23
|
+
options.port = url.port
|
|
24
|
+
|
|
25
|
+
log.debug(() => `Request to the intake: ${JSON.stringify(options)}`)
|
|
26
|
+
|
|
27
|
+
request(data, options, (err, res) => {
|
|
28
|
+
cb(err, res)
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class DataStreamsWriter {
|
|
33
|
+
constructor (config) {
|
|
34
|
+
const { hostname = '127.0.0.1', port = 8126, url } = config
|
|
35
|
+
this._url = url || new URL(format({
|
|
36
|
+
protocol: 'http:',
|
|
37
|
+
hostname: hostname || 'localhost',
|
|
38
|
+
port
|
|
39
|
+
}))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
flush (payload) {
|
|
43
|
+
if (!request.writable) {
|
|
44
|
+
log.debug(() => `Maximum number of active requests reached. Payload discarded: ${JSON.stringify(payload)}`)
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
const encodedPayload = msgpack.encode(payload, { codec })
|
|
48
|
+
|
|
49
|
+
zlib.gzip(encodedPayload, { level: 1 }, (err, compressedData) => {
|
|
50
|
+
if (err) {
|
|
51
|
+
log.error(err)
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
makeRequest(compressedData, this._url, (err, res) => {
|
|
55
|
+
log.debug(`Response from the agent: ${res}`)
|
|
56
|
+
if (err) {
|
|
57
|
+
log.error(err)
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = {
|
|
65
|
+
DataStreamsWriter
|
|
66
|
+
}
|
|
@@ -8,7 +8,11 @@ const log = require('./log')
|
|
|
8
8
|
|
|
9
9
|
const MAX_BUFFER_SIZE = 1024 // limit from the agent
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
const TYPE_COUNTER = 'c'
|
|
12
|
+
const TYPE_GAUGE = 'g'
|
|
13
|
+
const TYPE_DISTRIBUTION = 'd'
|
|
14
|
+
|
|
15
|
+
class DogStatsDClient {
|
|
12
16
|
constructor (options) {
|
|
13
17
|
options = options || {}
|
|
14
18
|
|
|
@@ -32,11 +36,15 @@ class Client {
|
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
gauge (stat, value, tags) {
|
|
35
|
-
this._add(stat, value,
|
|
39
|
+
this._add(stat, value, TYPE_GAUGE, tags)
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
increment (stat, value, tags) {
|
|
39
|
-
this._add(stat, value,
|
|
43
|
+
this._add(stat, value, TYPE_COUNTER, tags)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
distribution (stat, value, tags) {
|
|
47
|
+
this._add(stat, value, TYPE_DISTRIBUTION, tags)
|
|
40
48
|
}
|
|
41
49
|
|
|
42
50
|
flush () {
|
|
@@ -135,4 +143,4 @@ class Client {
|
|
|
135
143
|
}
|
|
136
144
|
}
|
|
137
145
|
|
|
138
|
-
module.exports =
|
|
146
|
+
module.exports = DogStatsDClient
|
|
@@ -7,6 +7,8 @@ class ExternalLogger {
|
|
|
7
7
|
constructor ({
|
|
8
8
|
ddsource, hostname, service, apiKey, site = 'datadoghq.com', interval = 10000, timeout = 2000, limit = 1000
|
|
9
9
|
}) {
|
|
10
|
+
this.enabled = !!apiKey
|
|
11
|
+
|
|
10
12
|
this.ddsource = ddsource
|
|
11
13
|
this.hostname = hostname
|
|
12
14
|
this.service = service
|
|
@@ -38,6 +40,8 @@ class ExternalLogger {
|
|
|
38
40
|
|
|
39
41
|
// Parses and enqueues a log
|
|
40
42
|
log (log, span, tags) {
|
|
43
|
+
if (!this.enabled) return
|
|
44
|
+
|
|
41
45
|
const logTags = ExternalLogger.tagString(tags)
|
|
42
46
|
|
|
43
47
|
if (span) {
|
|
@@ -11,6 +11,9 @@ const tagger = require('../tagger')
|
|
|
11
11
|
const metrics = require('../metrics')
|
|
12
12
|
const log = require('../log')
|
|
13
13
|
const { storage } = require('../../../datadog-core')
|
|
14
|
+
const telemetryMetrics = require('../telemetry/metrics')
|
|
15
|
+
|
|
16
|
+
const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
|
|
14
17
|
|
|
15
18
|
const {
|
|
16
19
|
DD_TRACE_EXPERIMENTAL_STATE_TRACKING,
|
|
@@ -20,6 +23,30 @@ const {
|
|
|
20
23
|
const unfinishedRegistry = createRegistry('unfinished')
|
|
21
24
|
const finishedRegistry = createRegistry('finished')
|
|
22
25
|
|
|
26
|
+
const OTEL_ENABLED = !!process.env.DD_TRACE_OTEL_ENABLED
|
|
27
|
+
|
|
28
|
+
const integrationCounters = {
|
|
29
|
+
span_created: {},
|
|
30
|
+
span_finished: {}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function getIntegrationCounter (event, integration) {
|
|
34
|
+
const counters = integrationCounters[event]
|
|
35
|
+
|
|
36
|
+
if (integration in counters) {
|
|
37
|
+
return counters[integration]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const counter = tracerMetrics.count(event, [
|
|
41
|
+
`integration_name:${integration.toLowerCase()}`,
|
|
42
|
+
`otel_enabled:${OTEL_ENABLED}`
|
|
43
|
+
])
|
|
44
|
+
|
|
45
|
+
integrationCounters[event][integration] = counter
|
|
46
|
+
|
|
47
|
+
return counter
|
|
48
|
+
}
|
|
49
|
+
|
|
23
50
|
class DatadogSpan {
|
|
24
51
|
constructor (tracer, processor, prioritySampler, fields, debug) {
|
|
25
52
|
const operationName = fields.operationName
|
|
@@ -38,6 +65,9 @@ class DatadogSpan {
|
|
|
38
65
|
// This name property is not updated when the span name changes.
|
|
39
66
|
// This is necessary for span count metrics.
|
|
40
67
|
this._name = operationName
|
|
68
|
+
this._integrationName = fields.integrationName || 'opentracing'
|
|
69
|
+
|
|
70
|
+
getIntegrationCounter('span_created', this._integrationName).inc()
|
|
41
71
|
|
|
42
72
|
this._spanContext = this._createContext(parent, fields)
|
|
43
73
|
this._spanContext._name = operationName
|
|
@@ -126,6 +156,8 @@ class DatadogSpan {
|
|
|
126
156
|
}
|
|
127
157
|
}
|
|
128
158
|
|
|
159
|
+
getIntegrationCounter('span_finished', this._integrationName).inc()
|
|
160
|
+
|
|
129
161
|
if (DD_TRACE_EXPERIMENTAL_SPAN_COUNTS && finishedRegistry) {
|
|
130
162
|
metrics.decrement('runtime.node.spans.unfinished')
|
|
131
163
|
metrics.decrement('runtime.node.spans.unfinished.by.name', `span_name:${this._name}`)
|
|
@@ -26,6 +26,7 @@ class DatadogTracer {
|
|
|
26
26
|
this._version = config.version
|
|
27
27
|
this._env = config.env
|
|
28
28
|
this._tags = config.tags
|
|
29
|
+
this._computePeerService = config.spanComputePeerService
|
|
29
30
|
this._logInjection = config.logInjection
|
|
30
31
|
this._debug = config.debug
|
|
31
32
|
this._prioritySampler = new PrioritySampler(config.env, config.sampler)
|
|
@@ -60,7 +61,8 @@ class DatadogTracer {
|
|
|
60
61
|
tags,
|
|
61
62
|
startTime: options.startTime,
|
|
62
63
|
hostname: this._hostname,
|
|
63
|
-
traceId128BitGenerationEnabled: this._traceId128BitGenerationEnabled
|
|
64
|
+
traceId128BitGenerationEnabled: this._traceId128BitGenerationEnabled,
|
|
65
|
+
integrationName: options.integrationName
|
|
64
66
|
}, this._debug)
|
|
65
67
|
|
|
66
68
|
span.addTags(this._tags)
|
|
@@ -21,7 +21,7 @@ const disabledPlugins = new Set(
|
|
|
21
21
|
DD_TRACE_DISABLED_PLUGINS && DD_TRACE_DISABLED_PLUGINS.split(',').map(plugin => plugin.trim())
|
|
22
22
|
)
|
|
23
23
|
|
|
24
|
-
// TODO actually ... should we be looking at
|
|
24
|
+
// TODO actually ... should we be looking at environment variables this deep down in the code?
|
|
25
25
|
|
|
26
26
|
const pluginClasses = {}
|
|
27
27
|
|
|
@@ -133,7 +133,9 @@ module.exports = class PluginManager {
|
|
|
133
133
|
serviceMapping,
|
|
134
134
|
queryStringObfuscation,
|
|
135
135
|
site,
|
|
136
|
-
url
|
|
136
|
+
url,
|
|
137
|
+
dbmPropagationMode,
|
|
138
|
+
dsmEnabled
|
|
137
139
|
} = this._tracerConfig
|
|
138
140
|
|
|
139
141
|
const sharedConfig = {}
|
|
@@ -146,6 +148,9 @@ module.exports = class PluginManager {
|
|
|
146
148
|
sharedConfig.queryStringObfuscation = queryStringObfuscation
|
|
147
149
|
}
|
|
148
150
|
|
|
151
|
+
sharedConfig.dbmPropagationMode = dbmPropagationMode
|
|
152
|
+
sharedConfig.dsmEnabled = dsmEnabled
|
|
153
|
+
|
|
149
154
|
if (serviceMapping && serviceMapping[name]) {
|
|
150
155
|
sharedConfig.service = serviceMapping[name]
|
|
151
156
|
}
|
|
@@ -5,6 +5,7 @@ const OutboundPlugin = require('./outbound')
|
|
|
5
5
|
class ClientPlugin extends OutboundPlugin {
|
|
6
6
|
static get operation () { return 'request' }
|
|
7
7
|
static get kind () { return 'client' }
|
|
8
|
+
static get type () { return 'web' } // overridden by storage and other client type plugins
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
module.exports = ClientPlugin
|
|
@@ -4,6 +4,7 @@ const StoragePlugin = require('./storage')
|
|
|
4
4
|
|
|
5
5
|
class DatabasePlugin extends StoragePlugin {
|
|
6
6
|
static get operation () { return 'query' }
|
|
7
|
+
static get peerServicePrecursors () { return ['db.name'] }
|
|
7
8
|
|
|
8
9
|
constructor (...args) {
|
|
9
10
|
super(...args)
|
|
@@ -38,7 +39,7 @@ class DatabasePlugin extends StoragePlugin {
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
injectDbmQuery (query, serviceName, isPreparedStatement = false) {
|
|
41
|
-
const mode = this.config.dbmPropagationMode
|
|
42
|
+
const mode = this.config.dbmPropagationMode
|
|
42
43
|
|
|
43
44
|
if (mode === 'disabled') {
|
|
44
45
|
return query
|
|
@@ -14,6 +14,7 @@ module.exports = {
|
|
|
14
14
|
get '@node-redis/client' () { return require('../../../datadog-plugin-redis/src') },
|
|
15
15
|
get '@opensearch-project/opensearch' () { return require('../../../datadog-plugin-opensearch/src') },
|
|
16
16
|
get '@redis/client' () { return require('../../../datadog-plugin-redis/src') },
|
|
17
|
+
get '@smithy/smithy-client' () { return require('../../../datadog-plugin-aws-sdk/src') },
|
|
17
18
|
get 'amqp10' () { return require('../../../datadog-plugin-amqp10/src') },
|
|
18
19
|
get 'amqplib' () { return require('../../../datadog-plugin-amqplib/src') },
|
|
19
20
|
get 'aws-sdk' () { return require('../../../datadog-plugin-aws-sdk/src') },
|
|
@@ -56,6 +57,7 @@ module.exports = {
|
|
|
56
57
|
get 'net' () { return require('../../../datadog-plugin-net/src') },
|
|
57
58
|
get 'next' () { return require('../../../datadog-plugin-next/src') },
|
|
58
59
|
get 'oracledb' () { return require('../../../datadog-plugin-oracledb/src') },
|
|
60
|
+
get 'openai' () { return require('../../../datadog-plugin-openai/src') },
|
|
59
61
|
get 'paperplane' () { return require('../../../datadog-plugin-paperplane/src') },
|
|
60
62
|
get 'pg' () { return require('../../../datadog-plugin-pg/src') },
|
|
61
63
|
get 'pino' () { return require('../../../datadog-plugin-pino/src') },
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const {
|
|
4
|
+
CLIENT_PORT_KEY,
|
|
5
|
+
PEER_SERVICE_KEY,
|
|
6
|
+
PEER_SERVICE_SOURCE_KEY
|
|
7
|
+
} = require('../constants')
|
|
4
8
|
const TracingPlugin = require('./tracing')
|
|
5
9
|
|
|
10
|
+
const COMMON_PEER_SVC_SOURCE_TAGS = [
|
|
11
|
+
'net.peer.name',
|
|
12
|
+
'out.host'
|
|
13
|
+
]
|
|
14
|
+
|
|
6
15
|
// TODO: Exit span on finish when AsyncResource instances are removed.
|
|
7
16
|
class OutboundPlugin extends TracingPlugin {
|
|
17
|
+
static get peerServicePrecursors () { return [] }
|
|
18
|
+
|
|
8
19
|
constructor (...args) {
|
|
9
20
|
super(...args)
|
|
10
21
|
|
|
@@ -13,6 +24,53 @@ class OutboundPlugin extends TracingPlugin {
|
|
|
13
24
|
})
|
|
14
25
|
}
|
|
15
26
|
|
|
27
|
+
getPeerService (tags) {
|
|
28
|
+
/**
|
|
29
|
+
* Compute `peer.service` and associated metadata from available tags, based
|
|
30
|
+
* on defined precursor tags names.
|
|
31
|
+
*
|
|
32
|
+
* - The `peer.service` tag is set from the first precursor available (based on list ordering)
|
|
33
|
+
* - The `_dd.peer.service.source` tag is set from the precursor's name
|
|
34
|
+
* - If `peer.service` was defined _before_ we compute it (for example in custom instrumentation),
|
|
35
|
+
* `_dd.peer.service.source`'s value is `peer.service`
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
if (tags['peer.service'] !== undefined) {
|
|
39
|
+
return { [PEER_SERVICE_SOURCE_KEY]: 'peer.service' }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const sourceTags = [
|
|
43
|
+
...this.constructor.peerServicePrecursors,
|
|
44
|
+
...COMMON_PEER_SVC_SOURCE_TAGS
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
for (const sourceTag of sourceTags) {
|
|
48
|
+
if (tags[sourceTag]) {
|
|
49
|
+
return {
|
|
50
|
+
[PEER_SERVICE_KEY]: tags[sourceTag],
|
|
51
|
+
[PEER_SERVICE_SOURCE_KEY]: sourceTag
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return {}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
startSpan (name, options) {
|
|
59
|
+
const span = super.startSpan(name, options)
|
|
60
|
+
return span
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
finish () {
|
|
64
|
+
const span = this.activeSpan
|
|
65
|
+
if (this.tracer._computePeerService) {
|
|
66
|
+
const peerData = this.getPeerService(span.context()._tags)
|
|
67
|
+
if (peerData) {
|
|
68
|
+
span.addTags(peerData)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
super.finish(...arguments)
|
|
72
|
+
}
|
|
73
|
+
|
|
16
74
|
connect (url) {
|
|
17
75
|
this.addHost(url.hostname, url.port)
|
|
18
76
|
}
|
|
@@ -4,6 +4,8 @@ const InboundPlugin = require('./inbound')
|
|
|
4
4
|
|
|
5
5
|
class ServerPlugin extends InboundPlugin {
|
|
6
6
|
static get operation () { return 'request' }
|
|
7
|
+
static get kind () { return 'server' }
|
|
8
|
+
static get type () { return 'web' } // a default that may eventually be overriden by nonweb servers
|
|
7
9
|
}
|
|
8
10
|
|
|
9
11
|
module.exports = ServerPlugin
|
|
@@ -33,6 +33,9 @@ class TracingPlugin extends Plugin {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
serviceName (...serviceArgs) {
|
|
36
|
+
if (Nomenclature.shouldUseConsistentServiceNaming) {
|
|
37
|
+
return Nomenclature.shortCircuitServiceName(this.config, ...serviceArgs)
|
|
38
|
+
}
|
|
36
39
|
const { type, id, kind } = this.constructor
|
|
37
40
|
return Nomenclature.serviceName(type, kind, id, ...serviceArgs)
|
|
38
41
|
}
|
|
@@ -91,7 +94,8 @@ class TracingPlugin extends Plugin {
|
|
|
91
94
|
'span.type': type,
|
|
92
95
|
...meta,
|
|
93
96
|
...metrics
|
|
94
|
-
}
|
|
97
|
+
},
|
|
98
|
+
integrationName: type
|
|
95
99
|
})
|
|
96
100
|
|
|
97
101
|
analyticsSampler.sample(span, this.config.measured)
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
const cp = require('child_process')
|
|
2
|
+
const log = require('../../log')
|
|
2
3
|
|
|
3
4
|
const sanitizedExec = (cmd, flags, options = { stdio: 'pipe' }) => {
|
|
4
5
|
try {
|
|
5
6
|
return cp.execFileSync(cmd, flags, options).toString().replace(/(\r\n|\n|\r)/gm, '')
|
|
6
7
|
} catch (e) {
|
|
8
|
+
log.error(e)
|
|
7
9
|
return ''
|
|
8
10
|
}
|
|
9
11
|
}
|
|
@@ -35,13 +35,41 @@ function isShallowRepository () {
|
|
|
35
35
|
return sanitizedExec('git', ['rev-parse', '--is-shallow-repository']) === 'true'
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
function
|
|
38
|
+
function getGitVersion () {
|
|
39
|
+
const gitVersionString = sanitizedExec('git', ['version'])
|
|
40
|
+
const gitVersionMatches = gitVersionString.match(/git version (\d+)\.(\d+)\.(\d+)/)
|
|
39
41
|
try {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
return {
|
|
43
|
+
major: parseInt(gitVersionMatches[1]),
|
|
44
|
+
minor: parseInt(gitVersionMatches[2]),
|
|
45
|
+
patch: parseInt(gitVersionMatches[3])
|
|
46
|
+
}
|
|
47
|
+
} catch (e) {
|
|
48
|
+
return null
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function unshallowRepository () {
|
|
53
|
+
const gitVersion = getGitVersion()
|
|
54
|
+
if (!gitVersion) {
|
|
55
|
+
log.warn('Git version could not be extracted, so git unshallow will not proceed')
|
|
56
|
+
return
|
|
44
57
|
}
|
|
58
|
+
if (gitVersion.major < 2 || (gitVersion.major === 2 && gitVersion.minor < 27)) {
|
|
59
|
+
log.warn('Git version is <2.27, so git unshallow will not proceed')
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
const defaultRemoteName = sanitizedExec('git', ['config', '--default', 'origin', '--get', 'clone.defaultRemoteName'])
|
|
63
|
+
const revParseHead = sanitizedExec('git', ['rev-parse', 'HEAD'])
|
|
64
|
+
sanitizedExec('git', [
|
|
65
|
+
'fetch',
|
|
66
|
+
'--shallow-since="1 month ago"',
|
|
67
|
+
'--update-shallow',
|
|
68
|
+
'--filter=blob:none',
|
|
69
|
+
'--recurse-submodules=no',
|
|
70
|
+
defaultRemoteName,
|
|
71
|
+
revParseHead
|
|
72
|
+
])
|
|
45
73
|
}
|
|
46
74
|
|
|
47
75
|
function getRepositoryUrl () {
|
|
@@ -55,12 +83,12 @@ function getLatestCommits () {
|
|
|
55
83
|
.split('\n')
|
|
56
84
|
.filter(commit => commit)
|
|
57
85
|
} catch (err) {
|
|
58
|
-
log.error(err)
|
|
86
|
+
log.error(`Get latest commits failed: ${err.message}`)
|
|
59
87
|
return []
|
|
60
88
|
}
|
|
61
89
|
}
|
|
62
90
|
|
|
63
|
-
function getCommitsToUpload (commitsToExclude) {
|
|
91
|
+
function getCommitsToUpload (commitsToExclude, commitsToInclude) {
|
|
64
92
|
const commitsToExcludeString = commitsToExclude.map(commit => `^${commit}`)
|
|
65
93
|
|
|
66
94
|
try {
|
|
@@ -72,15 +100,15 @@ function getCommitsToUpload (commitsToExclude) {
|
|
|
72
100
|
'--no-object-names',
|
|
73
101
|
'--filter=blob:none',
|
|
74
102
|
'--since="1 month ago"',
|
|
75
|
-
|
|
76
|
-
...
|
|
103
|
+
...commitsToExcludeString,
|
|
104
|
+
...commitsToInclude
|
|
77
105
|
],
|
|
78
106
|
{ stdio: 'pipe', maxBuffer: GIT_REV_LIST_MAX_BUFFER })
|
|
79
107
|
.toString()
|
|
80
108
|
.split('\n')
|
|
81
109
|
.filter(commit => commit)
|
|
82
110
|
} catch (err) {
|
|
83
|
-
log.error(err)
|
|
111
|
+
log.error(`Get commits to upload failed: ${err.message}`)
|
|
84
112
|
return []
|
|
85
113
|
}
|
|
86
114
|
}
|