dd-trace 2.10.0 → 2.12.1

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 (49) hide show
  1. package/LICENSE-3rdparty.csv +0 -1
  2. package/index.d.ts +3 -3
  3. package/package.json +5 -6
  4. package/packages/datadog-core/src/storage/async_hooks.js +4 -4
  5. package/packages/datadog-core/src/storage/async_resource.js +14 -4
  6. package/packages/datadog-instrumentations/index.js +4 -0
  7. package/packages/datadog-instrumentations/src/connect.js +7 -4
  8. package/packages/datadog-instrumentations/src/couchbase.js +166 -61
  9. package/packages/datadog-instrumentations/src/fastify.js +14 -25
  10. package/packages/datadog-instrumentations/src/graphql.js +23 -9
  11. package/packages/datadog-instrumentations/src/grpc/client.js +250 -0
  12. package/packages/datadog-instrumentations/src/grpc/server.js +144 -0
  13. package/packages/{datadog-plugin-grpc/src/kinds.js → datadog-instrumentations/src/grpc/types.js} +0 -0
  14. package/packages/datadog-instrumentations/src/grpc.js +4 -0
  15. package/packages/datadog-instrumentations/src/http2/client.js +67 -0
  16. package/packages/datadog-instrumentations/src/http2/server.js +3 -0
  17. package/packages/datadog-instrumentations/src/http2.js +4 -0
  18. package/packages/datadog-instrumentations/src/koa.js +15 -6
  19. package/packages/datadog-instrumentations/src/microgateway-core.js +66 -0
  20. package/packages/datadog-instrumentations/src/mocha.js +198 -62
  21. package/packages/datadog-instrumentations/src/next.js +140 -0
  22. package/packages/datadog-instrumentations/src/restify.js +4 -8
  23. package/packages/datadog-instrumentations/src/router.js +7 -4
  24. package/packages/datadog-plugin-couchbase/src/index.js +8 -10
  25. package/packages/datadog-plugin-graphql/src/resolve.js +14 -17
  26. package/packages/datadog-plugin-grpc/src/client.js +54 -283
  27. package/packages/datadog-plugin-grpc/src/index.js +31 -3
  28. package/packages/datadog-plugin-grpc/src/server.js +50 -145
  29. package/packages/datadog-plugin-http/src/server.js +3 -8
  30. package/packages/datadog-plugin-http2/src/client.js +72 -120
  31. package/packages/datadog-plugin-http2/src/index.js +32 -3
  32. package/packages/datadog-plugin-http2/src/server.js +6 -214
  33. package/packages/datadog-plugin-microgateway-core/src/index.js +14 -145
  34. package/packages/datadog-plugin-mocha/src/index.js +88 -39
  35. package/packages/datadog-plugin-next/src/index.js +56 -168
  36. package/packages/datadog-plugin-router/src/index.js +46 -13
  37. package/packages/dd-trace/src/config.js +2 -1
  38. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +111 -15
  39. package/packages/dd-trace/src/opentracing/span.js +6 -3
  40. package/packages/dd-trace/src/plugin_manager.js +49 -33
  41. package/packages/dd-trace/src/plugins/plugin.js +3 -1
  42. package/packages/dd-trace/src/plugins/util/test.js +32 -1
  43. package/packages/dd-trace/src/plugins/util/web.js +25 -24
  44. package/packages/dd-trace/src/profiling/config.js +10 -2
  45. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -2
  46. package/packages/dd-trace/src/profiling/exporters/form-data.js +53 -0
  47. package/packages/dd-trace/src/profiling/index.js +2 -0
  48. package/packages/dd-trace/src/profiling/profiler.js +6 -1
  49. package/packages/dd-trace/src/profiling/profilers/cpu.js +126 -0
@@ -0,0 +1,250 @@
1
+ 'use strict'
2
+
3
+ const types = require('./types')
4
+ const { addHook, channel, AsyncResource } = require('../helpers/instrument')
5
+ const shimmer = require('../../../datadog-shimmer')
6
+
7
+ const patched = new WeakSet()
8
+ const instances = new WeakMap()
9
+
10
+ const startChannel = channel('apm:grpc:client:request:start')
11
+ const errorChannel = channel('apm:grpc:client:request:error')
12
+ const finishChannel = channel('apm:grpc:client:request:finish')
13
+
14
+ function createWrapMakeRequest (type) {
15
+ return function wrapMakeRequest (makeRequest) {
16
+ return function (path) {
17
+ const args = ensureMetadata(this, arguments, 4)
18
+
19
+ return callMethod(this, makeRequest, args, path, args[4], type)
20
+ }
21
+ }
22
+ }
23
+
24
+ function createWrapLoadPackageDefinition () {
25
+ return function wrapLoadPackageDefinition (loadPackageDefinition) {
26
+ return function (packageDef) {
27
+ const result = loadPackageDefinition.apply(this, arguments)
28
+
29
+ if (!result) return result
30
+
31
+ wrapPackageDefinition(result)
32
+
33
+ return result
34
+ }
35
+ }
36
+ }
37
+
38
+ function createWrapMakeClientConstructor () {
39
+ return function wrapMakeClientConstructor (makeClientConstructor) {
40
+ return function (methods) {
41
+ const ServiceClient = makeClientConstructor.apply(this, arguments)
42
+
43
+ wrapClientConstructor(ServiceClient, methods)
44
+
45
+ return ServiceClient
46
+ }
47
+ }
48
+ }
49
+
50
+ function wrapPackageDefinition (def) {
51
+ for (const name in def) {
52
+ if (def[name].format) continue
53
+ if (def[name].service && def[name].prototype) {
54
+ wrapClientConstructor(def[name], def[name].service)
55
+ } else {
56
+ wrapPackageDefinition(def[name])
57
+ }
58
+ }
59
+ }
60
+
61
+ function wrapClientConstructor (ServiceClient, methods) {
62
+ const proto = ServiceClient.prototype
63
+
64
+ if (typeof methods !== 'object' || 'format' in methods) return
65
+
66
+ Object.keys(methods)
67
+ .forEach(name => {
68
+ if (!methods[name]) return
69
+
70
+ const originalName = methods[name].originalName
71
+ const path = methods[name].path
72
+ const type = getType(methods[name])
73
+
74
+ if (methods[name]) {
75
+ proto[name] = wrapMethod(proto[name], path, type)
76
+ }
77
+
78
+ if (originalName) {
79
+ proto[originalName] = wrapMethod(proto[originalName], path, type)
80
+ }
81
+ })
82
+ }
83
+
84
+ function wrapMethod (method, path, type) {
85
+ if (typeof method !== 'function' || patched.has(method)) {
86
+ return method
87
+ }
88
+
89
+ const wrapped = function () {
90
+ const args = ensureMetadata(this, arguments, 1)
91
+
92
+ return callMethod(this, method, args, path, args[1], type)
93
+ }
94
+
95
+ Object.assign(wrapped, method)
96
+
97
+ patched.add(wrapped)
98
+
99
+ return wrapped
100
+ }
101
+
102
+ function wrapCallback (requestResource, parentResource, callback) {
103
+ return function (err) {
104
+ if (err) {
105
+ requestResource.runInAsyncScope(() => {
106
+ errorChannel.publish(err)
107
+ })
108
+ }
109
+
110
+ if (callback) {
111
+ return parentResource.runInAsyncScope(() => {
112
+ return callback.apply(this, arguments)
113
+ })
114
+ }
115
+ }
116
+ }
117
+
118
+ function wrapStream (call, requestResource, parentResource) {
119
+ if (!call || typeof call.emit !== 'function') return
120
+
121
+ shimmer.wrap(call, 'emit', emit => {
122
+ return function (eventName, ...args) {
123
+ requestResource.runInAsyncScope(() => {
124
+ switch (eventName) {
125
+ case 'error':
126
+ errorChannel.publish(args[0])
127
+
128
+ break
129
+ case 'status':
130
+ finishChannel.publish(args[0])
131
+
132
+ break
133
+ }
134
+ })
135
+
136
+ return parentResource.runInAsyncScope(() => {
137
+ return emit.apply(this, arguments)
138
+ })
139
+ }
140
+ })
141
+ }
142
+
143
+ function callMethod (client, method, args, path, metadata, type) {
144
+ if (!startChannel.hasSubscribers) return method.apply(client, args)
145
+
146
+ const length = args.length
147
+ const callback = args[length - 1]
148
+ const parentResource = new AsyncResource('bound-anonymous-fn')
149
+ const requestResource = new AsyncResource('bound-anonymous-fn')
150
+
151
+ return requestResource.runInAsyncScope(() => {
152
+ startChannel.publish({ metadata, path, type })
153
+
154
+ if (type === types.unary || type === types.client_stream) {
155
+ if (typeof callback === 'function') {
156
+ args[length - 1] = wrapCallback(requestResource, parentResource, callback)
157
+ } else {
158
+ args[length] = wrapCallback(requestResource, parentResource)
159
+ }
160
+ }
161
+
162
+ const call = method.apply(client, args)
163
+
164
+ wrapStream(call, requestResource, parentResource)
165
+
166
+ return call
167
+ })
168
+ }
169
+
170
+ function ensureMetadata (client, args, index) {
171
+ const grpc = getGrpc(client)
172
+
173
+ if (!client || !grpc) return args
174
+
175
+ const meta = args[index]
176
+ const normalized = []
177
+
178
+ for (let i = 0; i < index; i++) {
179
+ normalized.push(args[i])
180
+ }
181
+
182
+ if (!meta || !meta.constructor || meta.constructor.name !== 'Metadata') {
183
+ normalized.push(new grpc.Metadata())
184
+ }
185
+
186
+ if (meta) {
187
+ normalized.push(meta)
188
+ }
189
+
190
+ for (let i = index + 1; i < args.length; i++) {
191
+ normalized.push(args[i])
192
+ }
193
+
194
+ return normalized
195
+ }
196
+
197
+ function getType (definition) {
198
+ if (definition.requestStream) {
199
+ if (definition.responseStream) {
200
+ return types.bidi
201
+ }
202
+
203
+ return types.client_stream
204
+ }
205
+
206
+ if (definition.responseStream) {
207
+ return types.server_stream
208
+ }
209
+
210
+ return types.unary
211
+ }
212
+
213
+ function getGrpc (client) {
214
+ let proto = client
215
+
216
+ do {
217
+ const instance = instances.get(proto)
218
+ if (instance) return instance
219
+ } while ((proto = Object.getPrototypeOf(proto)))
220
+ }
221
+
222
+ function patch (grpc) {
223
+ const proto = grpc.Client.prototype
224
+
225
+ instances.set(proto, grpc)
226
+
227
+ shimmer.wrap(proto, 'makeBidiStreamRequest', createWrapMakeRequest(types.bidi))
228
+ shimmer.wrap(proto, 'makeClientStreamRequest', createWrapMakeRequest(types.clientStream))
229
+ shimmer.wrap(proto, 'makeServerStreamRequest', createWrapMakeRequest(types.serverStream))
230
+ shimmer.wrap(proto, 'makeUnaryRequest', createWrapMakeRequest(types.unary))
231
+
232
+ return grpc
233
+ }
234
+
235
+ addHook({ name: 'grpc', versions: ['>=1.20.2'] }, patch)
236
+
237
+ addHook({ name: 'grpc', versions: ['>=1.20.2'], file: 'src/client.js' }, client => {
238
+ shimmer.wrap(client, 'makeClientConstructor', createWrapMakeClientConstructor())
239
+
240
+ return client
241
+ })
242
+
243
+ addHook({ name: '@grpc/grpc-js', versions: ['>=1.0.3'] }, patch)
244
+
245
+ addHook({ name: '@grpc/grpc-js', versions: ['>=1.0.3'], file: 'build/src/make-client.js' }, client => {
246
+ shimmer.wrap(client, 'makeClientConstructor', createWrapMakeClientConstructor())
247
+ shimmer.wrap(client, 'loadPackageDefinition', createWrapLoadPackageDefinition())
248
+
249
+ return client
250
+ })
@@ -0,0 +1,144 @@
1
+ 'use strict'
2
+
3
+ const types = require('./types')
4
+ const { channel, addHook, AsyncResource } = require('../helpers/instrument')
5
+ const shimmer = require('../../../datadog-shimmer')
6
+
7
+ const startChannel = channel('apm:grpc:server:request:start')
8
+ const errorChannel = channel('apm:grpc:server:request:error')
9
+ const updateChannel = channel('apm:grpc:server:request:update')
10
+ const finishChannel = channel('apm:grpc:server:request:finish')
11
+
12
+ // https://github.com/grpc/grpc/blob/master/doc/statuscodes.md
13
+ const OK = 0
14
+ const CANCELLED = 1
15
+
16
+ function wrapHandler (func, name) {
17
+ const isValid = (server, args) => {
18
+ if (!startChannel.hasSubscribers) return false
19
+ if (!server || !server.type) return false
20
+ if (!args[0]) return false
21
+ if (server.type !== 'unary' && !isEmitter(args[0])) return false
22
+ if (server.type === 'unary' && typeof args[1] !== 'function') return false
23
+
24
+ return true
25
+ }
26
+
27
+ return function (call, callback) {
28
+ if (!isValid(this, arguments)) return func.apply(this, arguments)
29
+
30
+ const metadata = call.metadata
31
+ const type = types[this.type]
32
+ const isStream = type !== 'unary'
33
+
34
+ const parentResource = new AsyncResource('bound-anonymous-fn')
35
+ const requestResource = new AsyncResource('bound-anonymous-fn')
36
+
37
+ return requestResource.runInAsyncScope(() => {
38
+ startChannel.publish({ name, metadata, type })
39
+
40
+ // Finish the span if the call was cancelled.
41
+ call.once('cancelled', requestResource.bind(() => {
42
+ finishChannel.publish({ code: CANCELLED })
43
+ }))
44
+
45
+ if (isStream) {
46
+ wrapStream(call, requestResource, parentResource)
47
+ } else {
48
+ arguments[1] = wrapCallback(callback, requestResource, parentResource)
49
+ }
50
+
51
+ shimmer.wrap(call, 'emit', emit => requestResource.bind(emit))
52
+
53
+ return func.apply(this, arguments)
54
+ })
55
+ }
56
+ }
57
+
58
+ function wrapRegister (register) {
59
+ return function (name, handler, serialize, deserialize, type) {
60
+ if (typeof handler === 'function') {
61
+ arguments[1] = wrapHandler(handler, name)
62
+ }
63
+
64
+ return register.apply(this, arguments)
65
+ }
66
+ }
67
+
68
+ function wrapStream (call, requestResource) {
69
+ if (call.call && call.call.sendStatus) {
70
+ call.call.sendStatus = wrapSendStatus(call.call.sendStatus, requestResource)
71
+ }
72
+
73
+ shimmer.wrap(call, 'emit', emit => {
74
+ return function (eventName, ...args) {
75
+ switch (eventName) {
76
+ case 'error':
77
+ errorChannel.publish(args[0])
78
+ finishChannel.publish({ code: args[0].code })
79
+
80
+ break
81
+
82
+ // Finish the span of the response only if it was successful.
83
+ // Otherwise it'll be finished in the `error` listener.
84
+ case 'finish':
85
+ if (call.status) {
86
+ updateChannel.publish(call.status)
87
+ }
88
+
89
+ if (!call.status || call.status.code === 0) {
90
+ finishChannel.publish()
91
+ }
92
+
93
+ break
94
+ }
95
+
96
+ return emit.apply(this, arguments)
97
+ }
98
+ })
99
+ }
100
+
101
+ function wrapCallback (callback, requestResource, parentResource) {
102
+ return function (err, value, trailer, flags) {
103
+ requestResource.runInAsyncScope(() => {
104
+ if (err instanceof Error) {
105
+ errorChannel.publish(err)
106
+ finishChannel.publish(err)
107
+ } else {
108
+ finishChannel.publish({ code: OK, trailer })
109
+ }
110
+ })
111
+
112
+ if (callback) {
113
+ return parentResource.runInAsyncScope(() => {
114
+ return callback.apply(this, arguments)
115
+ })
116
+ }
117
+ }
118
+ }
119
+
120
+ function wrapSendStatus (sendStatus, requestResource) {
121
+ return function (status) {
122
+ requestResource.runInAsyncScope(() => {
123
+ updateChannel.publish(status)
124
+ })
125
+
126
+ return sendStatus.apply(this, arguments)
127
+ }
128
+ }
129
+
130
+ function isEmitter (obj) {
131
+ return typeof obj.emit === 'function' && typeof obj.once === 'function'
132
+ }
133
+
134
+ addHook({ name: 'grpc', versions: ['>=1.20.2'], file: 'src/server.js' }, server => {
135
+ shimmer.wrap(server.Server.prototype, 'register', wrapRegister)
136
+
137
+ return server
138
+ })
139
+
140
+ addHook({ name: '@grpc/grpc-js', versions: ['>=1.0.3'], file: 'build/src/server.js' }, server => {
141
+ shimmer.wrap(server.Server.prototype, 'register', wrapRegister)
142
+
143
+ return server
144
+ })
@@ -0,0 +1,4 @@
1
+ 'use strict'
2
+
3
+ require('./grpc/client')
4
+ require('./grpc/server')
@@ -0,0 +1,67 @@
1
+ 'use strict'
2
+
3
+ const shimmer = require('../../../datadog-shimmer')
4
+ const { addHook, channel, AsyncResource } = require('../helpers/instrument')
5
+
6
+ const startChannel = channel('apm:http2:client:request:start')
7
+ const finishChannel = channel('apm:http2:client:request:finish')
8
+ const errorChannel = channel('apm:http2:client:request:error')
9
+ const responseChannel = channel('apm:http2:client:response')
10
+
11
+ function createWrapEmit (requestResource, parentResource) {
12
+ return function wrapEmit (emit) {
13
+ return function (event, arg1) {
14
+ requestResource.runInAsyncScope(() => {
15
+ switch (event) {
16
+ case 'response':
17
+ responseChannel.publish(arg1)
18
+ break
19
+ case 'error':
20
+ errorChannel.publish(arg1)
21
+ case 'close': // eslint-disable-line no-fallthrough
22
+ finishChannel.publish()
23
+ break
24
+ }
25
+ })
26
+
27
+ return parentResource.runInAsyncScope(() => {
28
+ return emit.apply(this, arguments)
29
+ })
30
+ }
31
+ }
32
+ }
33
+
34
+ function createWrapRequest (authority, options) {
35
+ return function wrapRequest (request) {
36
+ return function (headers) {
37
+ const parentResource = new AsyncResource('bound-anonymous-fn')
38
+ const requestResource = new AsyncResource('bound-anonymous-fn')
39
+
40
+ return requestResource.runInAsyncScope(() => {
41
+ startChannel.publish({ headers, authority, options })
42
+
43
+ const req = request.apply(this, arguments)
44
+
45
+ shimmer.wrap(req, 'emit', createWrapEmit(requestResource, parentResource))
46
+
47
+ return req
48
+ })
49
+ }
50
+ }
51
+ }
52
+
53
+ function wrapConnect (connect) {
54
+ return function (authority, options) {
55
+ const session = connect.apply(this, arguments)
56
+
57
+ shimmer.wrap(session, 'request', createWrapRequest(authority, options))
58
+
59
+ return session
60
+ }
61
+ }
62
+
63
+ addHook({ name: 'http2' }, http2 => {
64
+ shimmer.wrap(http2, 'connect', wrapConnect)
65
+
66
+ return http2
67
+ })
@@ -0,0 +1,3 @@
1
+ 'use strict'
2
+
3
+ // Instrumentation temporarily disabled. See https://github.com/DataDog/dd-trace-js/issues/312
@@ -0,0 +1,4 @@
1
+ 'use strict'
2
+
3
+ require('./http2/client')
4
+ require('./http2/server')
@@ -4,6 +4,7 @@ const shimmer = require('../../datadog-shimmer')
4
4
  const { addHook, channel, AsyncResource } = require('./helpers/instrument')
5
5
 
6
6
  const enterChannel = channel('apm:koa:middleware:enter')
7
+ const exitChannel = channel('apm:koa:middleware:exit')
7
8
  const errorChannel = channel('apm:koa:middleware:error')
8
9
  const nextChannel = channel('apm:koa:middleware:next')
9
10
  const handleChannel = channel('apm:koa:request:handle')
@@ -95,7 +96,7 @@ function wrapMiddleware (fn, layer) {
95
96
  enterChannel.publish({ req, name, route })
96
97
 
97
98
  if (typeof next === 'function') {
98
- arguments[1] = next
99
+ arguments[1] = wrapNext(req, next)
99
100
  }
100
101
 
101
102
  try {
@@ -125,19 +126,27 @@ function wrapMiddleware (fn, layer) {
125
126
  }
126
127
 
127
128
  function fulfill (ctx, error) {
128
- if (error) {
129
- errorChannel.publish(error)
130
- }
131
-
132
129
  const req = ctx.req
133
130
  const route = ctx.routePath
134
131
 
132
+ if (error) {
133
+ errorChannel.publish({ req, error })
134
+ }
135
+
135
136
  // TODO: make sure that the parent class cannot override this in `enter`
136
137
  if (route) {
137
138
  routeChannel.publish({ req, route })
138
139
  }
139
140
 
140
- nextChannel.publish(ctx)
141
+ exitChannel.publish({ req })
142
+ }
143
+
144
+ function wrapNext (req, next) {
145
+ return function () {
146
+ nextChannel.publish({ req })
147
+
148
+ return next.apply(null, arguments)
149
+ }
141
150
  }
142
151
 
143
152
  addHook({ name: 'koa', versions: ['>=2'] }, Koa => {
@@ -0,0 +1,66 @@
1
+ 'use strict'
2
+
3
+ const shimmer = require('../../datadog-shimmer')
4
+ const { addHook, channel, AsyncResource } = require('./helpers/instrument')
5
+
6
+ const handleChannel = channel('apm:microgateway-core:request:handle')
7
+ const routeChannel = channel('apm:microgateway-core:request:route')
8
+ const errorChannel = channel('apm:microgateway-core:request:error')
9
+
10
+ const name = 'microgateway-core'
11
+ const versions = ['>=2.1']
12
+ const requestResources = new WeakMap()
13
+
14
+ function wrapConfigProxyFactory (configProxyFactory) {
15
+ return function () {
16
+ const configProxy = configProxyFactory.apply(this, arguments)
17
+
18
+ return function (req, res, next) {
19
+ const requestResource = new AsyncResource('bound-anonymous-fn')
20
+
21
+ requestResources.set(req, requestResource)
22
+
23
+ handleChannel.publish({ req, res })
24
+
25
+ return configProxy.apply(this, arguments)
26
+ }
27
+ }
28
+ }
29
+
30
+ function wrapPluginsFactory (pluginsFactory) {
31
+ return function (plugins) {
32
+ const pluginsMiddleware = pluginsFactory.apply(this, arguments)
33
+
34
+ return function pluginsMiddlewareWithTrace (req, res, next) {
35
+ arguments[2] = wrapNext(req, res, next)
36
+
37
+ return pluginsMiddleware.apply(this, arguments)
38
+ }
39
+ }
40
+ }
41
+
42
+ function wrapNext (req, res, next) {
43
+ return function nextWithTrace (err) {
44
+ const requestResource = requestResources.get(req)
45
+
46
+ requestResource.runInAsyncScope(() => {
47
+ if (err) {
48
+ errorChannel.publish(err)
49
+ }
50
+
51
+ if (res.proxy && res.proxy.base_path) {
52
+ routeChannel.publish({ req, res, route: res.proxy.base_path })
53
+ }
54
+ })
55
+
56
+ return next.apply(this, arguments)
57
+ }
58
+ }
59
+
60
+ addHook({ name, versions, file: 'lib/config-proxy-middleware.js' }, configProxyFactory => {
61
+ return shimmer.wrap(configProxyFactory, wrapConfigProxyFactory(configProxyFactory))
62
+ })
63
+
64
+ addHook({ name, versions, file: 'lib/plugins-middleware.js' }, pluginsFactory => {
65
+ return shimmer.wrap(pluginsFactory, wrapPluginsFactory(pluginsFactory))
66
+ })