dd-trace 5.23.0 → 5.24.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.
Files changed (84) hide show
  1. package/LICENSE-3rdparty.csv +0 -1
  2. package/ext/types.d.ts +1 -0
  3. package/ext/types.js +1 -0
  4. package/index.d.ts +26 -0
  5. package/package.json +6 -7
  6. package/packages/datadog-code-origin/index.js +38 -0
  7. package/packages/datadog-core/index.js +2 -2
  8. package/packages/datadog-instrumentations/src/avsc.js +37 -0
  9. package/packages/datadog-instrumentations/src/azure-functions.js +48 -0
  10. package/packages/datadog-instrumentations/src/child_process.js +17 -8
  11. package/packages/datadog-instrumentations/src/express.js +37 -4
  12. package/packages/datadog-instrumentations/src/fastify.js +12 -1
  13. package/packages/datadog-instrumentations/src/fs.js +27 -7
  14. package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
  15. package/packages/datadog-instrumentations/src/jest.js +2 -1
  16. package/packages/datadog-instrumentations/src/mocha/common.js +1 -1
  17. package/packages/datadog-instrumentations/src/mysql2.js +220 -1
  18. package/packages/datadog-instrumentations/src/protobufjs.js +127 -0
  19. package/packages/datadog-instrumentations/src/winston.js +22 -0
  20. package/packages/datadog-plugin-avsc/src/index.js +9 -0
  21. package/packages/datadog-plugin-avsc/src/schema_iterator.js +169 -0
  22. package/packages/datadog-plugin-azure-functions/src/index.js +77 -0
  23. package/packages/datadog-plugin-fastify/src/code_origin.js +31 -0
  24. package/packages/datadog-plugin-fastify/src/index.js +10 -12
  25. package/packages/datadog-plugin-fastify/src/tracing.js +19 -0
  26. package/packages/datadog-plugin-protobufjs/src/index.js +14 -0
  27. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +180 -0
  28. package/packages/dd-trace/src/appsec/addresses.js +6 -1
  29. package/packages/dd-trace/src/appsec/channels.js +5 -1
  30. package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +13 -1
  31. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +8 -1
  32. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +1 -1
  33. package/packages/dd-trace/src/appsec/iast/index.js +3 -0
  34. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +1 -0
  35. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +15 -0
  36. package/packages/dd-trace/src/appsec/index.js +58 -43
  37. package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +99 -0
  38. package/packages/dd-trace/src/appsec/rasp/index.js +24 -10
  39. package/packages/dd-trace/src/appsec/rasp/lfi.js +112 -0
  40. package/packages/dd-trace/src/appsec/rasp/sql_injection.js +24 -4
  41. package/packages/dd-trace/src/appsec/rasp/utils.js +2 -1
  42. package/packages/dd-trace/src/appsec/recommended.json +2 -4
  43. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +5 -1
  44. package/packages/dd-trace/src/appsec/remote_config/index.js +8 -0
  45. package/packages/dd-trace/src/appsec/reporter.js +12 -5
  46. package/packages/dd-trace/src/appsec/sdk/track_event.js +5 -0
  47. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +1 -1
  48. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +2 -14
  49. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +53 -0
  50. package/packages/dd-trace/src/config.js +12 -1
  51. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +25 -17
  52. package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -0
  53. package/packages/dd-trace/src/debugger/devtools_client/index.js +56 -5
  54. package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +4 -4
  55. package/packages/dd-trace/src/debugger/devtools_client/send.js +14 -1
  56. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +153 -0
  57. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +30 -0
  58. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +241 -0
  59. package/packages/dd-trace/src/debugger/devtools_client/state.js +10 -4
  60. package/packages/dd-trace/src/exporters/common/request.js +8 -34
  61. package/packages/dd-trace/src/exporters/common/url-to-http-options-polyfill.js +31 -0
  62. package/packages/dd-trace/src/payload-tagging/index.js +1 -1
  63. package/packages/dd-trace/src/payload-tagging/jsonpath-plus.js +2094 -0
  64. package/packages/dd-trace/src/plugin_manager.js +4 -2
  65. package/packages/dd-trace/src/plugins/ci_plugin.js +2 -0
  66. package/packages/dd-trace/src/plugins/index.js +3 -0
  67. package/packages/dd-trace/src/plugins/log_plugin.js +1 -1
  68. package/packages/dd-trace/src/plugins/schema.js +35 -0
  69. package/packages/dd-trace/src/plugins/util/ci.js +23 -1
  70. package/packages/dd-trace/src/plugins/util/serverless.js +7 -0
  71. package/packages/dd-trace/src/plugins/util/stacktrace.js +94 -0
  72. package/packages/dd-trace/src/plugins/util/tags.js +7 -0
  73. package/packages/dd-trace/src/plugins/util/test.js +20 -22
  74. package/packages/dd-trace/src/plugins/util/web.js +6 -4
  75. package/packages/dd-trace/src/profiling/profiler.js +24 -14
  76. package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
  77. package/packages/dd-trace/src/profiling/profilers/wall.js +94 -66
  78. package/packages/dd-trace/src/proxy.js +12 -0
  79. package/packages/dd-trace/src/service-naming/schemas/v0/index.js +2 -1
  80. package/packages/dd-trace/src/service-naming/schemas/v0/serverless.js +12 -0
  81. package/packages/dd-trace/src/service-naming/schemas/v1/index.js +2 -1
  82. package/packages/dd-trace/src/service-naming/schemas/v1/serverless.js +12 -0
  83. package/packages/datadog-core/src/storage/async_resource.js +0 -108
  84. package/packages/datadog-core/src/storage/index.js +0 -5
@@ -6,11 +6,14 @@ const {
6
6
  AsyncResource
7
7
  } = require('./helpers/instrument')
8
8
  const shimmer = require('../../datadog-shimmer')
9
+ const semver = require('semver')
9
10
 
10
- addHook({ name: 'mysql2', file: 'lib/connection.js', versions: ['>=1'] }, Connection => {
11
+ addHook({ name: 'mysql2', file: 'lib/connection.js', versions: ['>=1'] }, (Connection, version) => {
11
12
  const startCh = channel('apm:mysql2:query:start')
12
13
  const finishCh = channel('apm:mysql2:query:finish')
13
14
  const errorCh = channel('apm:mysql2:query:error')
15
+ const startOuterQueryCh = channel('datadog:mysql2:outerquery:start')
16
+ const shouldEmitEndAfterQueryAbort = semver.intersects(version, '>=1.3.3')
14
17
 
15
18
  shimmer.wrap(Connection.prototype, 'addCommand', addCommand => function (cmd) {
16
19
  if (!startCh.hasSubscribers) return addCommand.apply(this, arguments)
@@ -28,6 +31,76 @@ addHook({ name: 'mysql2', file: 'lib/connection.js', versions: ['>=1'] }, Connec
28
31
  return asyncResource.bind(addCommand, this).apply(this, arguments)
29
32
  })
30
33
 
34
+ shimmer.wrap(Connection.prototype, 'query', query => function (sql, values, cb) {
35
+ if (!startOuterQueryCh.hasSubscribers) return query.apply(this, arguments)
36
+
37
+ if (typeof sql === 'object') sql = sql?.sql
38
+
39
+ if (!sql) return query.apply(this, arguments)
40
+
41
+ const abortController = new AbortController()
42
+ startOuterQueryCh.publish({ sql, abortController })
43
+
44
+ if (abortController.signal.aborted) {
45
+ const addCommand = this.addCommand
46
+ this.addCommand = function (cmd) { return cmd }
47
+
48
+ let queryCommand
49
+ try {
50
+ queryCommand = query.apply(this, arguments)
51
+ } finally {
52
+ this.addCommand = addCommand
53
+ }
54
+
55
+ cb = queryCommand.onResult
56
+
57
+ process.nextTick(() => {
58
+ if (cb) {
59
+ cb(abortController.signal.reason)
60
+ } else {
61
+ queryCommand.emit('error', abortController.signal.reason)
62
+ }
63
+
64
+ if (shouldEmitEndAfterQueryAbort) {
65
+ queryCommand.emit('end')
66
+ }
67
+ })
68
+
69
+ return queryCommand
70
+ }
71
+
72
+ return query.apply(this, arguments)
73
+ })
74
+
75
+ shimmer.wrap(Connection.prototype, 'execute', execute => function (sql, values, cb) {
76
+ if (!startOuterQueryCh.hasSubscribers) return execute.apply(this, arguments)
77
+
78
+ if (typeof sql === 'object') sql = sql?.sql
79
+
80
+ if (!sql) return execute.apply(this, arguments)
81
+
82
+ const abortController = new AbortController()
83
+ startOuterQueryCh.publish({ sql, abortController })
84
+
85
+ if (abortController.signal.aborted) {
86
+ const addCommand = this.addCommand
87
+ this.addCommand = function (cmd) { return cmd }
88
+
89
+ let result
90
+ try {
91
+ result = execute.apply(this, arguments)
92
+ } finally {
93
+ this.addCommand = addCommand
94
+ }
95
+
96
+ result?.onResult(abortController.signal.reason)
97
+
98
+ return result
99
+ }
100
+
101
+ return execute.apply(this, arguments)
102
+ })
103
+
31
104
  return Connection
32
105
 
33
106
  function bindExecute (cmd, execute, asyncResource) {
@@ -79,3 +152,149 @@ addHook({ name: 'mysql2', file: 'lib/connection.js', versions: ['>=1'] }, Connec
79
152
  }, cmd))
80
153
  }
81
154
  })
155
+
156
+ addHook({ name: 'mysql2', file: 'lib/pool.js', versions: ['>=1'] }, (Pool, version) => {
157
+ const startOuterQueryCh = channel('datadog:mysql2:outerquery:start')
158
+ const shouldEmitEndAfterQueryAbort = semver.intersects(version, '>=1.3.3')
159
+
160
+ shimmer.wrap(Pool.prototype, 'query', query => function (sql, values, cb) {
161
+ if (!startOuterQueryCh.hasSubscribers) return query.apply(this, arguments)
162
+
163
+ if (typeof sql === 'object') sql = sql?.sql
164
+
165
+ if (!sql) return query.apply(this, arguments)
166
+
167
+ const abortController = new AbortController()
168
+ startOuterQueryCh.publish({ sql, abortController })
169
+
170
+ if (abortController.signal.aborted) {
171
+ const getConnection = this.getConnection
172
+ this.getConnection = function () {}
173
+
174
+ let queryCommand
175
+ try {
176
+ queryCommand = query.apply(this, arguments)
177
+ } finally {
178
+ this.getConnection = getConnection
179
+ }
180
+
181
+ process.nextTick(() => {
182
+ if (queryCommand.onResult) {
183
+ queryCommand.onResult(abortController.signal.reason)
184
+ } else {
185
+ queryCommand.emit('error', abortController.signal.reason)
186
+ }
187
+
188
+ if (shouldEmitEndAfterQueryAbort) {
189
+ queryCommand.emit('end')
190
+ }
191
+ })
192
+
193
+ return queryCommand
194
+ }
195
+
196
+ return query.apply(this, arguments)
197
+ })
198
+
199
+ shimmer.wrap(Pool.prototype, 'execute', execute => function (sql, values, cb) {
200
+ if (!startOuterQueryCh.hasSubscribers) return execute.apply(this, arguments)
201
+
202
+ if (typeof sql === 'object') sql = sql?.sql
203
+
204
+ if (!sql) return execute.apply(this, arguments)
205
+
206
+ const abortController = new AbortController()
207
+ startOuterQueryCh.publish({ sql, abortController })
208
+
209
+ if (abortController.signal.aborted) {
210
+ if (typeof values === 'function') {
211
+ cb = values
212
+ }
213
+
214
+ process.nextTick(() => {
215
+ cb(abortController.signal.reason)
216
+ })
217
+ return
218
+ }
219
+
220
+ return execute.apply(this, arguments)
221
+ })
222
+
223
+ return Pool
224
+ })
225
+
226
+ // PoolNamespace.prototype.query does not exist in mysql2<2.3.0
227
+ addHook({ name: 'mysql2', file: 'lib/pool_cluster.js', versions: ['>=2.3.0'] }, PoolCluster => {
228
+ const startOuterQueryCh = channel('datadog:mysql2:outerquery:start')
229
+ const wrappedPoolNamespaces = new WeakSet()
230
+
231
+ shimmer.wrap(PoolCluster.prototype, 'of', of => function () {
232
+ const poolNamespace = of.apply(this, arguments)
233
+
234
+ if (startOuterQueryCh.hasSubscribers && !wrappedPoolNamespaces.has(poolNamespace)) {
235
+ shimmer.wrap(poolNamespace, 'query', query => function (sql, values, cb) {
236
+ if (typeof sql === 'object') sql = sql?.sql
237
+
238
+ if (!sql) return query.apply(this, arguments)
239
+
240
+ const abortController = new AbortController()
241
+ startOuterQueryCh.publish({ sql, abortController })
242
+
243
+ if (abortController.signal.aborted) {
244
+ const getConnection = this.getConnection
245
+ this.getConnection = function () {}
246
+
247
+ let queryCommand
248
+ try {
249
+ queryCommand = query.apply(this, arguments)
250
+ } finally {
251
+ this.getConnection = getConnection
252
+ }
253
+
254
+ process.nextTick(() => {
255
+ if (queryCommand.onResult) {
256
+ queryCommand.onResult(abortController.signal.reason)
257
+ } else {
258
+ queryCommand.emit('error', abortController.signal.reason)
259
+ }
260
+
261
+ queryCommand.emit('end')
262
+ })
263
+
264
+ return queryCommand
265
+ }
266
+
267
+ return query.apply(this, arguments)
268
+ })
269
+
270
+ shimmer.wrap(poolNamespace, 'execute', execute => function (sql, values, cb) {
271
+ if (typeof sql === 'object') sql = sql?.sql
272
+
273
+ if (!sql) return execute.apply(this, arguments)
274
+
275
+ const abortController = new AbortController()
276
+ startOuterQueryCh.publish({ sql, abortController })
277
+
278
+ if (abortController.signal.aborted) {
279
+ if (typeof values === 'function') {
280
+ cb = values
281
+ }
282
+
283
+ process.nextTick(() => {
284
+ cb(abortController.signal.reason)
285
+ })
286
+
287
+ return
288
+ }
289
+
290
+ return execute.apply(this, arguments)
291
+ })
292
+
293
+ wrappedPoolNamespaces.add(poolNamespace)
294
+ }
295
+
296
+ return poolNamespace
297
+ })
298
+
299
+ return PoolCluster
300
+ })
@@ -0,0 +1,127 @@
1
+ const shimmer = require('../../datadog-shimmer')
2
+ const { addHook } = require('./helpers/instrument')
3
+
4
+ const dc = require('dc-polyfill')
5
+ const serializeChannel = dc.channel('apm:protobufjs:serialize-start')
6
+ const deserializeChannel = dc.channel('apm:protobufjs:deserialize-end')
7
+
8
+ function wrapSerialization (messageClass) {
9
+ if (messageClass?.encode) {
10
+ shimmer.wrap(messageClass, 'encode', original => function () {
11
+ if (!serializeChannel.hasSubscribers) {
12
+ return original.apply(this, arguments)
13
+ }
14
+ serializeChannel.publish({ messageClass: this })
15
+ return original.apply(this, arguments)
16
+ })
17
+ }
18
+ }
19
+
20
+ function wrapDeserialization (messageClass) {
21
+ if (messageClass?.decode) {
22
+ shimmer.wrap(messageClass, 'decode', original => function () {
23
+ if (!deserializeChannel.hasSubscribers) {
24
+ return original.apply(this, arguments)
25
+ }
26
+ const result = original.apply(this, arguments)
27
+ deserializeChannel.publish({ messageClass: result })
28
+ return result
29
+ })
30
+ }
31
+ }
32
+
33
+ function wrapSetup (messageClass) {
34
+ if (messageClass?.setup) {
35
+ shimmer.wrap(messageClass, 'setup', original => function () {
36
+ const result = original.apply(this, arguments)
37
+
38
+ wrapSerialization(messageClass)
39
+ wrapDeserialization(messageClass)
40
+
41
+ return result
42
+ })
43
+ }
44
+ }
45
+
46
+ function wrapProtobufClasses (root) {
47
+ if (!root) {
48
+ return
49
+ }
50
+
51
+ if (root.decode) {
52
+ wrapSetup(root)
53
+ }
54
+
55
+ if (root.nestedArray) {
56
+ for (const subRoot of root.nestedArray) {
57
+ wrapProtobufClasses(subRoot)
58
+ }
59
+ }
60
+ }
61
+
62
+ function wrapReflection (protobuf) {
63
+ const reflectionMethods = [
64
+ {
65
+ target: protobuf.Root,
66
+ name: 'fromJSON'
67
+ },
68
+ {
69
+ target: protobuf.Type.prototype,
70
+ name: 'fromObject'
71
+ }
72
+ ]
73
+
74
+ reflectionMethods.forEach(method => {
75
+ shimmer.wrap(method.target, method.name, original => function () {
76
+ const result = original.apply(this, arguments)
77
+ if (result.nested) {
78
+ for (const type in result.nested) {
79
+ wrapSetup(result.nested[type])
80
+ }
81
+ }
82
+ if (result.$type) {
83
+ wrapSetup(result.$type)
84
+ }
85
+ return result
86
+ })
87
+ })
88
+ }
89
+
90
+ function isPromise (obj) {
91
+ return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'
92
+ }
93
+
94
+ addHook({
95
+ name: 'protobufjs',
96
+ versions: ['>=6.8.0']
97
+ }, protobuf => {
98
+ shimmer.wrap(protobuf.Root.prototype, 'load', original => function () {
99
+ const result = original.apply(this, arguments)
100
+ if (isPromise(result)) {
101
+ return result.then(root => {
102
+ wrapProtobufClasses(root)
103
+ return root
104
+ })
105
+ } else {
106
+ // If result is not a promise, directly wrap the protobuf classes
107
+ wrapProtobufClasses(result)
108
+ return result
109
+ }
110
+ })
111
+
112
+ shimmer.wrap(protobuf.Root.prototype, 'loadSync', original => function () {
113
+ const root = original.apply(this, arguments)
114
+ wrapProtobufClasses(root)
115
+ return root
116
+ })
117
+
118
+ shimmer.wrap(protobuf, 'Type', Original => function () {
119
+ const typeInstance = new Original(...arguments)
120
+ wrapSetup(typeInstance)
121
+ return typeInstance
122
+ })
123
+
124
+ wrapReflection(protobuf)
125
+
126
+ return protobuf
127
+ })
@@ -8,6 +8,18 @@ const shimmer = require('../../datadog-shimmer')
8
8
 
9
9
  const patched = new WeakSet()
10
10
 
11
+ // Test Visibility log submission channels
12
+ const configureCh = channel('ci:log-submission:winston:configure')
13
+ const addTransport = channel('ci:log-submission:winston:add-transport')
14
+
15
+ addHook({ name: 'winston', file: 'lib/winston/transports/index.js', versions: ['>=3'] }, transportsPackage => {
16
+ if (configureCh.hasSubscribers) {
17
+ configureCh.publish(transportsPackage.Http)
18
+ }
19
+
20
+ return transportsPackage
21
+ })
22
+
11
23
  addHook({ name: 'winston', file: 'lib/winston/logger.js', versions: ['>=3'] }, Logger => {
12
24
  const logCh = channel('apm:winston:log')
13
25
  shimmer.wrap(Logger.prototype, 'write', write => {
@@ -20,6 +32,16 @@ addHook({ name: 'winston', file: 'lib/winston/logger.js', versions: ['>=3'] }, L
20
32
  return write.apply(this, arguments)
21
33
  }
22
34
  })
35
+
36
+ shimmer.wrap(Logger.prototype, 'configure', configure => function () {
37
+ const configureResponse = configure.apply(this, arguments)
38
+ // After the original `configure`, because it resets transports
39
+ if (addTransport.hasSubscribers) {
40
+ addTransport.publish(this)
41
+ }
42
+ return configureResponse
43
+ })
44
+
23
45
  return Logger
24
46
  })
25
47
 
@@ -0,0 +1,9 @@
1
+ const SchemaPlugin = require('../../dd-trace/src/plugins/schema')
2
+ const SchemaExtractor = require('./schema_iterator')
3
+
4
+ class AvscPlugin extends SchemaPlugin {
5
+ static get id () { return 'avsc' }
6
+ static get schemaExtractor () { return SchemaExtractor }
7
+ }
8
+
9
+ module.exports = AvscPlugin
@@ -0,0 +1,169 @@
1
+ const AVRO = 'avro'
2
+ const {
3
+ SCHEMA_DEFINITION,
4
+ SCHEMA_ID,
5
+ SCHEMA_NAME,
6
+ SCHEMA_OPERATION,
7
+ SCHEMA_WEIGHT,
8
+ SCHEMA_TYPE
9
+ } = require('../../dd-trace/src/constants')
10
+ const log = require('../../dd-trace/src/log')
11
+ const {
12
+ SchemaBuilder
13
+ } = require('../../dd-trace/src/datastreams/schemas/schema_builder')
14
+
15
+ class SchemaExtractor {
16
+ constructor (schema) {
17
+ this.schema = schema
18
+ }
19
+
20
+ static getType (type) {
21
+ const typeMapping = {
22
+ string: 'string',
23
+ int: 'integer',
24
+ long: 'integer',
25
+ float: 'number',
26
+ double: 'number',
27
+ boolean: 'boolean',
28
+ bytes: 'string',
29
+ record: 'object',
30
+ enum: 'string',
31
+ array: 'array',
32
+ map: 'object',
33
+ fixed: 'string'
34
+ }
35
+ const typeName = type.typeName ?? type.name ?? type
36
+ return typeName === 'null' ? typeName : typeMapping[typeName] || 'string'
37
+ }
38
+
39
+ static extractProperty (field, schemaName, fieldName, builder, depth) {
40
+ let array = false
41
+ let type
42
+ let format
43
+ let enumValues
44
+ let description
45
+ let ref
46
+
47
+ const fieldType = field.type?.types ?? field.type?.typeName ?? field.type
48
+
49
+ if (Array.isArray(fieldType)) {
50
+ // Union Type
51
+ type = 'union[' + fieldType.map(t => SchemaExtractor.getType(t.type || t)).join(',') + ']'
52
+ } else if (fieldType === 'array') {
53
+ // Array Type
54
+ array = true
55
+ const nestedType = field.type.itemsType.typeName
56
+ type = SchemaExtractor.getType(nestedType)
57
+ } else if (fieldType === 'record') {
58
+ // Nested Record Type
59
+ type = 'object'
60
+ ref = `#/components/schemas/${field.type.name}`
61
+ if (!SchemaExtractor.extractSchema(field.type, builder, depth + 1, this)) {
62
+ return false
63
+ }
64
+ } else if (fieldType === 'enum') {
65
+ enumValues = []
66
+ let i = 0
67
+ type = 'string'
68
+ while (field.type.symbols[i]) {
69
+ enumValues.push(field.type.symbols[i])
70
+ i += 1
71
+ }
72
+ } else {
73
+ // Primitive type
74
+ type = SchemaExtractor.getType(fieldType.type || fieldType)
75
+ if (fieldType === 'bytes') {
76
+ format = 'byte'
77
+ } else if (fieldType === 'int') {
78
+ format = 'int32'
79
+ } else if (fieldType === 'long') {
80
+ format = 'int64'
81
+ } else if (fieldType === 'float') {
82
+ format = 'float'
83
+ } else if (fieldType === 'double') {
84
+ format = 'double'
85
+ }
86
+ }
87
+
88
+ return builder.addProperty(schemaName, fieldName, array, type, description, ref, format, enumValues)
89
+ }
90
+
91
+ static extractSchema (schema, builder, depth, extractor) {
92
+ depth += 1
93
+ const schemaName = schema.name
94
+ if (extractor) {
95
+ // if we already have a defined extractor, this is a nested schema. create a new extractor for the nested
96
+ // schema, ensure it is added to our schema builder's cache, and replace the builders iterator with our
97
+ // nested schema iterator / extractor. Once complete, add the new schema to our builder's schemas.
98
+ const nestedSchemaExtractor = new SchemaExtractor(schema)
99
+ builder.iterator = nestedSchemaExtractor
100
+ const nestedSchema = SchemaBuilder.getSchema(schemaName, nestedSchemaExtractor, builder)
101
+ for (const nestedSubSchemaName in nestedSchema.components.schemas) {
102
+ if (nestedSchema.components.schemas.hasOwnProperty(nestedSubSchemaName)) {
103
+ builder.schema.components.schemas[nestedSubSchemaName] = nestedSchema.components.schemas[nestedSubSchemaName]
104
+ }
105
+ }
106
+ return true
107
+ } else {
108
+ if (!builder.shouldExtractSchema(schemaName, depth)) {
109
+ return false
110
+ }
111
+ for (const field of schema.fields) {
112
+ if (!this.extractProperty(field, schemaName, field.name, builder, depth)) {
113
+ log.warn(`DSM: Unable to extract field with name: ${field.name} from Avro schema with name: ${schemaName}`)
114
+ }
115
+ }
116
+ }
117
+ return true
118
+ }
119
+
120
+ static extractSchemas (descriptor, dataStreamsProcessor) {
121
+ return dataStreamsProcessor.getSchema(descriptor.name, new SchemaExtractor(descriptor))
122
+ }
123
+
124
+ iterateOverSchema (builder) {
125
+ this.constructor.extractSchema(this.schema, builder, 0)
126
+ }
127
+
128
+ static attachSchemaOnSpan (args, span, operation, tracer) {
129
+ const { messageClass } = args
130
+ const descriptor = messageClass?.constructor?.type ?? messageClass
131
+
132
+ if (!descriptor || !span) {
133
+ return
134
+ }
135
+
136
+ if (span.context()._tags[SCHEMA_TYPE] && operation === 'serialization') {
137
+ // we have already added a schema to this span, this call is an encode of nested schema types
138
+ return
139
+ }
140
+
141
+ span.setTag(SCHEMA_TYPE, AVRO)
142
+ span.setTag(SCHEMA_NAME, descriptor.name)
143
+ span.setTag(SCHEMA_OPERATION, operation)
144
+
145
+ if (!tracer._dataStreamsProcessor.canSampleSchema(operation)) {
146
+ return
147
+ }
148
+
149
+ // if the span is unsampled, do not sample the schema
150
+ if (!tracer._prioritySampler.isSampled(span)) {
151
+ return
152
+ }
153
+
154
+ const weight = tracer._dataStreamsProcessor.trySampleSchema(operation)
155
+ if (weight === 0) {
156
+ return
157
+ }
158
+
159
+ const schemaData = SchemaBuilder.getSchemaDefinition(
160
+ this.extractSchemas(descriptor, tracer._dataStreamsProcessor)
161
+ )
162
+
163
+ span.setTag(SCHEMA_DEFINITION, schemaData.definition)
164
+ span.setTag(SCHEMA_WEIGHT, weight)
165
+ span.setTag(SCHEMA_ID, schemaData.id)
166
+ }
167
+ }
168
+
169
+ module.exports = SchemaExtractor
@@ -0,0 +1,77 @@
1
+ 'use strict'
2
+
3
+ const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
4
+ const { storage } = require('../../datadog-core')
5
+ const serverless = require('../../dd-trace/src/plugins/util/serverless')
6
+ const web = require('../../dd-trace/src/plugins/util/web')
7
+
8
+ const triggerMap = {
9
+ deleteRequest: 'Http',
10
+ http: 'Http',
11
+ get: 'Http',
12
+ patch: 'Http',
13
+ post: 'Http',
14
+ put: 'Http'
15
+ }
16
+
17
+ class AzureFunctionsPlugin extends TracingPlugin {
18
+ static get id () { return 'azure-functions' }
19
+ static get operation () { return 'invoke' }
20
+ static get kind () { return 'server' }
21
+ static get type () { return 'serverless' }
22
+
23
+ static get prefix () { return 'tracing:datadog:azure-functions:invoke' }
24
+
25
+ bindStart (ctx) {
26
+ const { functionName, methodName } = ctx
27
+ const store = storage.getStore()
28
+
29
+ const span = this.startSpan(this.operationName(), {
30
+ service: this.serviceName(),
31
+ type: 'serverless',
32
+ meta: {
33
+ 'aas.function.name': functionName,
34
+ 'aas.function.trigger': mapTriggerTag(methodName)
35
+ }
36
+ }, false)
37
+
38
+ ctx.span = span
39
+ ctx.parentStore = store
40
+ ctx.currentStore = { ...store, span }
41
+
42
+ return ctx.currentStore
43
+ }
44
+
45
+ error (ctx) {
46
+ this.addError(ctx.error)
47
+ ctx.currentStore.span.setTag('error.message', ctx.error)
48
+ }
49
+
50
+ asyncEnd (ctx) {
51
+ const { httpRequest, result = {} } = ctx
52
+ const path = (new URL(httpRequest.url)).pathname
53
+ const req = {
54
+ method: httpRequest.method,
55
+ headers: Object.fromEntries(httpRequest.headers.entries()),
56
+ url: path
57
+ }
58
+
59
+ const context = web.patch(req)
60
+ context.config = this.config
61
+ context.paths = [path]
62
+ context.res = { statusCode: result.status }
63
+ context.span = ctx.currentStore.span
64
+
65
+ serverless.finishSpan(context)
66
+ }
67
+
68
+ configure (config) {
69
+ return super.configure(web.normalizeConfig(config))
70
+ }
71
+ }
72
+
73
+ function mapTriggerTag (methodName) {
74
+ return triggerMap[methodName] || 'Unknown'
75
+ }
76
+
77
+ module.exports = AzureFunctionsPlugin
@@ -0,0 +1,31 @@
1
+ 'use strict'
2
+
3
+ const { entryTag } = require('../../datadog-code-origin')
4
+ const Plugin = require('../../dd-trace/src/plugins/plugin')
5
+ const web = require('../../dd-trace/src/plugins/util/web')
6
+
7
+ const kCodeOriginForSpansTagsSym = Symbol('datadog.codeOriginForSpansTags')
8
+
9
+ class FastifyCodeOriginForSpansPlugin extends Plugin {
10
+ static get id () {
11
+ return 'fastify'
12
+ }
13
+
14
+ constructor (...args) {
15
+ super(...args)
16
+
17
+ this.addSub('apm:fastify:request:handle', ({ req, routeConfig }) => {
18
+ const tags = routeConfig?.[kCodeOriginForSpansTagsSym]
19
+ if (!tags) return
20
+ const context = web.getContext(req)
21
+ context.span?.addTags(tags)
22
+ })
23
+
24
+ this.addSub('apm:fastify:route:added', ({ routeOptions, onRoute }) => {
25
+ if (!routeOptions.config) routeOptions.config = {}
26
+ routeOptions.config[kCodeOriginForSpansTagsSym] = entryTag(onRoute)
27
+ })
28
+ }
29
+ }
30
+
31
+ module.exports = FastifyCodeOriginForSpansPlugin