dd-trace 4.4.0 → 4.6.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 (45) hide show
  1. package/LICENSE-3rdparty.csv +4 -3
  2. package/package.json +4 -4
  3. package/packages/datadog-instrumentations/src/aws-sdk.js +5 -0
  4. package/packages/datadog-instrumentations/src/cassandra-driver.js +6 -3
  5. package/packages/datadog-instrumentations/src/elasticsearch.js +39 -1
  6. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  7. package/packages/datadog-instrumentations/src/kafkajs.js +13 -4
  8. package/packages/datadog-instrumentations/src/opensearch.js +2 -1
  9. package/packages/datadog-instrumentations/src/redis.js +48 -5
  10. package/packages/datadog-plugin-aws-sdk/src/base.js +3 -3
  11. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -0
  12. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -0
  13. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -0
  14. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +1 -0
  15. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -0
  16. package/packages/datadog-plugin-cassandra-driver/src/index.js +4 -4
  17. package/packages/datadog-plugin-grpc/src/client.js +8 -2
  18. package/packages/datadog-plugin-grpc/src/server.js +2 -2
  19. package/packages/datadog-plugin-kafkajs/src/consumer.js +6 -1
  20. package/packages/datadog-plugin-kafkajs/src/producer.js +14 -2
  21. package/packages/datadog-plugin-mongodb-core/src/index.js +13 -2
  22. package/packages/datadog-plugin-openai/src/index.js +9 -2
  23. package/packages/datadog-plugin-openai/src/services.js +14 -10
  24. package/packages/datadog-plugin-oracledb/src/index.js +1 -0
  25. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +6 -5
  26. package/packages/dd-trace/src/config.js +11 -0
  27. package/packages/dd-trace/src/data_streams_context.js +15 -0
  28. package/packages/dd-trace/src/datastreams/pathway.js +58 -0
  29. package/packages/dd-trace/src/datastreams/processor.js +194 -0
  30. package/packages/dd-trace/src/datastreams/writer.js +66 -0
  31. package/packages/dd-trace/src/dogstatsd.js +14 -1
  32. package/packages/dd-trace/src/metrics.js +2 -2
  33. package/packages/dd-trace/src/plugin_manager.js +6 -1
  34. package/packages/dd-trace/src/plugins/database.js +2 -1
  35. package/packages/dd-trace/src/plugins/index.js +1 -0
  36. package/packages/dd-trace/src/plugins/outbound.js +2 -1
  37. package/packages/dd-trace/src/plugins/tracing.js +3 -0
  38. package/packages/dd-trace/src/plugins/util/git.js +37 -5
  39. package/packages/dd-trace/src/plugins/util/user-provided-git.js +36 -2
  40. package/packages/dd-trace/src/profiling/config.js +32 -5
  41. package/packages/dd-trace/src/service-naming/index.js +13 -1
  42. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +9 -0
  43. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +8 -0
  44. package/packages/dd-trace/src/telemetry/metrics.js +76 -20
  45. package/packages/dd-trace/src/tracer.js +19 -1
@@ -11,6 +11,7 @@ require,crypto-randomuuid,MIT,Copyright 2021 Node.js Foundation and contributors
11
11
  require,diagnostics_channel,MIT,Copyright 2021 Simon D.
12
12
  require,ignore,MIT,Copyright 2013 Kael Zhang and contributors
13
13
  require,import-in-the-middle,Apache license 2.0,Copyright 2021 Datadog Inc.
14
+ require,int64-buffer,MIT,Copyright 2015-2016 Yusuke Kawasaki
14
15
  require,ipaddr.js,MIT,Copyright 2011-2017 whitequark
15
16
  require,istanbul-lib-coverage,BSD-3-Clause,Copyright 2012-2015 Yahoo! Inc.
16
17
  require,koalas,MIT,Copyright 2013-2017 Brian Woodward
@@ -22,6 +23,7 @@ require,lodash.uniq,MIT,Copyright JS Foundation and other contributors
22
23
  require,lru-cache,ISC,Copyright (c) 2010-2022 Isaac Z. Schlueter and Contributors
23
24
  require,methods,MIT,Copyright 2013-2014 TJ Holowaychuk
24
25
  require,module-details-from-path,MIT,Copyright 2016 Thomas Watson Steen
26
+ require,msgpack-lite,MIT,Copyright 2015 Yusuke Kawasaki
25
27
  require,node-abort-controller,MIT,Copyright (c) 2019 Steve Faulkner
26
28
  require,opentracing,MIT,Copyright 2016 Resonance Labs Inc
27
29
  require,path-to-regexp,MIT,Copyright 2014 Blake Embrey
@@ -51,13 +53,11 @@ dev,express,MIT,Copyright 2009-2014 TJ Holowaychuk 2013-2014 Roman Shtylman 2014
51
53
  dev,get-port,MIT,Copyright Sindre Sorhus
52
54
  dev,glob,ISC,Copyright Isaac Z. Schlueter and Contributors
53
55
  dev,graphql,MIT,Copyright 2015 Facebook Inc.
54
- dev,int64-buffer,MIT,Copyright 2015-2016 Yusuke Kawasaki
55
56
  dev,jszip,MIT,Copyright 2015-2016 Stuart Knightley and contributors
56
57
  dev,knex,MIT,Copyright (c) 2013-present Tim Griesser
57
58
  dev,mkdirp,MIT,Copyright 2010 James Halliday
58
59
  dev,mocha,MIT,Copyright 2011-2018 JS Foundation and contributors https://js.foundation
59
60
  dev,multer,MIT,Copyright 2014 Hage Yaapa
60
- dev,msgpack-lite,MIT,Copyright 2015 Yusuke Kawasaki
61
61
  dev,nock,MIT,Copyright 2017 Pedro Teixeira and other contributors
62
62
  dev,nyc,ISC,Copyright 2015 Contributors
63
63
  dev,pprof-format,MIT,Copyright 2022 Stephen Belanger
@@ -67,5 +67,6 @@ dev,sinon,BSD-3-Clause,Copyright 2010-2017 Christian Johansen
67
67
  dev,sinon-chai,WTFPL and BSD-2-Clause,Copyright 2004 Sam Hocevar 2012–2017 Domenic Denicola
68
68
  dev,tap,ISC,Copyright 2011-2022 Isaac Z. Schlueter and Contributors
69
69
  dev,tape,MIT,Copyright James Halliday
70
- file,aws-lambda-nodejs-runtime-interface-client,Apache 2.0,Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
70
+ file,aws-lambda-nodejs-runtime-interface-client,Apache 2.0,Copyright 2019 Amazon.com Inc. or its affiliates. All Rights Reserved.
71
71
  file,profile.proto,Apache license 2.0,Copyright 2016 Google Inc.
72
+ file,is-git-url,MIT,Copyright (c) 2017 Jon Schlinkert.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "4.4.0",
3
+ "version": "4.6.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -19,7 +19,7 @@
19
19
  "test:appsec:ci": "nyc --no-clean --include \"packages/dd-trace/src/appsec/**/*.js\" --exclude \"packages/dd-trace/test/appsec/**/*.plugin.spec.js\" -- npm run test:appsec",
20
20
  "test:appsec:plugins": "mocha --colors --exit -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/dd-trace/test/appsec/**/*.@($(echo $PLUGINS)).plugin.spec.js\"",
21
21
  "test:appsec:plugins:ci": "yarn services && nyc --no-clean --include \"packages/dd-trace/test/appsec/**/*.@($(echo $PLUGINS)).plugin.spec.js\" -- npm run test:appsec:plugins",
22
- "test:trace:core": "tap packages/dd-trace/test/*.spec.js \"packages/dd-trace/test/{ci-visibility,config,encode,exporters,opentelemetry,opentracing,plugins,telemetry}/**/*.spec.js\"",
22
+ "test:trace:core": "tap packages/dd-trace/test/*.spec.js \"packages/dd-trace/test/{ci-visibility,encode,exporters,opentelemetry,opentracing,plugins,service-naming,telemetry}/**/*.spec.js\"",
23
23
  "test:trace:core:ci": "npm run test:trace:core -- --coverage --nyc-arg=--include=\"packages/dd-trace/src/**/*.js\"",
24
24
  "test:instrumentations": "mocha --colors -r 'packages/dd-trace/test/setup/mocha.js' 'packages/datadog-instrumentations/test/**/*.spec.js'",
25
25
  "test:instrumentations:ci": "nyc --no-clean --include 'packages/datadog-instrumentations/src/**/*.js' -- npm run test:instrumentations",
@@ -78,6 +78,7 @@
78
78
  "diagnostics_channel": "^1.1.0",
79
79
  "ignore": "^5.2.0",
80
80
  "import-in-the-middle": "^1.3.5",
81
+ "int64-buffer": "^0.1.9",
81
82
  "ipaddr.js": "^2.0.1",
82
83
  "istanbul-lib-coverage": "3.2.0",
83
84
  "koalas": "^1.0.2",
@@ -89,6 +90,7 @@
89
90
  "lru-cache": "^7.14.0",
90
91
  "methods": "^1.1.2",
91
92
  "module-details-from-path": "^1.0.3",
93
+ "msgpack-lite": "^0.1.26",
92
94
  "node-abort-controller": "^3.0.1",
93
95
  "opentracing": ">=0.12.1",
94
96
  "path-to-regexp": "^0.1.2",
@@ -120,12 +122,10 @@
120
122
  "get-port": "^3.2.0",
121
123
  "glob": "^7.1.6",
122
124
  "graphql": "0.13.2",
123
- "int64-buffer": "^0.1.9",
124
125
  "jszip": "^3.5.0",
125
126
  "knex": "^2.4.2",
126
127
  "mkdirp": "^0.5.1",
127
128
  "mocha": "8",
128
- "msgpack-lite": "^0.1.26",
129
129
  "multer": "^1.4.5-lts.1",
130
130
  "nock": "^11.3.3",
131
131
  "nyc": "^15.1.0",
@@ -166,6 +166,11 @@ function getChannelSuffix (name) {
166
166
  ].includes(name) ? name : 'default'
167
167
  }
168
168
 
169
+ addHook({ name: '@smithy/smithy-client', versions: ['>=1.0.3'] }, smithy => {
170
+ shimmer.wrap(smithy.Client.prototype, 'send', wrapSmithySend)
171
+ return smithy
172
+ })
173
+
169
174
  addHook({ name: '@aws-sdk/smithy-client', versions: ['>=3'] }, smithy => {
170
175
  shimmer.wrap(smithy.Client.prototype, 'send', wrapSmithySend)
171
176
  return smithy
@@ -28,7 +28,8 @@ addHook({ name: 'cassandra-driver', versions: ['>=3.0.0'] }, cassandra => {
28
28
  }
29
29
 
30
30
  return asyncResource.runInAsyncScope(() => {
31
- startCh.publish({ keyspace: this.keyspace, query: queries })
31
+ const contactPoints = this.options && this.options.contactPoints
32
+ startCh.publish({ keyspace: this.keyspace, query: queries, contactPoints })
32
33
  try {
33
34
  const res = batch.apply(this, arguments)
34
35
  if (typeof res === 'function' || !res) {
@@ -56,7 +57,8 @@ addHook({ name: 'cassandra-driver', versions: ['>=4.4'] }, cassandra => {
56
57
  }
57
58
  const asyncResource = new AsyncResource('bound-anonymous-fn')
58
59
  return asyncResource.runInAsyncScope(() => {
59
- startCh.publish({ keyspace: this.keyspace, query })
60
+ const contactPoints = this.options && this.options.contactPoints
61
+ startCh.publish({ keyspace: this.keyspace, query, contactPoints })
60
62
  const promise = _execute.apply(this, arguments)
61
63
 
62
64
  const promiseAsyncResource = new AsyncResource('bound-anonymous-fn')
@@ -88,7 +90,8 @@ addHook({ name: 'cassandra-driver', versions: ['3 - 4.3'] }, cassandra => {
88
90
  }
89
91
 
90
92
  return asyncResource.runInAsyncScope(() => {
91
- startCh.publish({ keyspace: this.keyspace, query })
93
+ const contactPoints = this.options && this.options.contactPoints
94
+ startCh.publish({ keyspace: this.keyspace, query, contactPoints })
92
95
 
93
96
  const lastIndex = arguments.length - 1
94
97
  let cb = arguments[lastIndex]
@@ -9,11 +9,13 @@ const shimmer = require('../../datadog-shimmer')
9
9
 
10
10
  addHook({ name: '@elastic/transport', file: 'lib/Transport.js', versions: ['>=8'] }, (exports) => {
11
11
  shimmer.wrap(exports.default.prototype, 'request', createWrapRequest('elasticsearch'))
12
+ shimmer.wrap(exports.default.prototype, 'getConnection', createWrapGetConnection('elasticsearch'))
12
13
  return exports
13
14
  })
14
15
 
15
16
  addHook({ name: '@elastic/elasticsearch', file: 'lib/Transport.js', versions: ['>=5.6.16 <8', '>=8'] }, Transport => {
16
17
  shimmer.wrap(Transport.prototype, 'request', createWrapRequest('elasticsearch'))
18
+ shimmer.wrap(Transport.prototype, 'getConnection', createWrapGetConnection('elasticsearch'))
17
19
  return Transport
18
20
  })
19
21
 
@@ -22,6 +24,42 @@ addHook({ name: 'elasticsearch', file: 'src/lib/transport.js', versions: ['>=10'
22
24
  return Transport
23
25
  })
24
26
 
27
+ addHook({ name: 'elasticsearch', file: 'src/lib/connection_pool.js', versions: ['>=10'] }, ConnectionPool => {
28
+ shimmer.wrap(ConnectionPool.prototype, 'select', createWrapSelect('elasticsearch'))
29
+ return ConnectionPool
30
+ })
31
+
32
+ function createWrapGetConnection (name) {
33
+ const connectCh = channel(`apm:${name}:query:connect`)
34
+ return function wrapRequest (request) {
35
+ return function () {
36
+ const connection = request.apply(this, arguments)
37
+ if (connectCh.hasSubscribers && connection && connection.url) {
38
+ connectCh.publish(connection.url)
39
+ }
40
+ return connection
41
+ }
42
+ }
43
+ }
44
+
45
+ function createWrapSelect () {
46
+ const connectCh = channel('apm:elasticsearch:query:connect')
47
+ return function wrapRequest (request) {
48
+ return function () {
49
+ if (arguments.length === 1) {
50
+ const cb = arguments[0]
51
+ arguments[0] = function (err, connection) {
52
+ if (connectCh.hasSubscribers && connection && connection.host) {
53
+ connectCh.publish({ hostname: connection.host.host, port: connection.host.port })
54
+ }
55
+ cb(err, connection)
56
+ }
57
+ }
58
+ return request.apply(this, arguments)
59
+ }
60
+ }
61
+ }
62
+
25
63
  function createWrapRequest (name) {
26
64
  const startCh = channel(`apm:${name}:query:start`)
27
65
  const finishCh = channel(`apm:${name}:query:finish`)
@@ -83,4 +121,4 @@ function createWrapRequest (name) {
83
121
  }
84
122
  }
85
123
 
86
- module.exports = { createWrapRequest }
124
+ module.exports = { createWrapRequest, createWrapGetConnection }
@@ -16,6 +16,7 @@ module.exports = {
16
16
  '@opensearch-project/opensearch': () => require('../opensearch'),
17
17
  '@opentelemetry/sdk-trace-node': () => require('../otel-sdk-trace'),
18
18
  '@redis/client': () => require('../redis'),
19
+ '@smithy/smithy-client': () => require('../aws-sdk'),
19
20
  'amqp10': () => require('../amqp10'),
20
21
  'amqplib': () => require('../amqplib'),
21
22
  'aws-sdk': () => require('../aws-sdk'),
@@ -16,10 +16,19 @@ const consumerFinishCh = channel('apm:kafkajs:consume:finish')
16
16
  const consumerErrorCh = channel('apm:kafkajs:consume:error')
17
17
 
18
18
  addHook({ name: 'kafkajs', versions: ['>=1.4'] }, (obj) => {
19
- const Kafka = obj.Kafka
19
+ class Kafka extends obj.Kafka {
20
+ constructor (options) {
21
+ super(options)
22
+ this._brokers = (options.brokers && typeof options.brokers !== 'function')
23
+ ? options.brokers.join(',') : undefined
24
+ }
25
+ }
26
+ obj.Kafka = Kafka
27
+
20
28
  shimmer.wrap(Kafka.prototype, 'producer', createProducer => function () {
21
29
  const producer = createProducer.apply(this, arguments)
22
30
  const send = producer.send
31
+ const bootstrapServers = this._brokers
23
32
 
24
33
  producer.send = function () {
25
34
  const innerAsyncResource = new AsyncResource('bound-anonymous-fn')
@@ -36,7 +45,7 @@ addHook({ name: 'kafkajs', versions: ['>=1.4'] }, (obj) => {
36
45
  message.headers = message.headers || {}
37
46
  }
38
47
  }
39
- producerStartCh.publish({ topic, messages })
48
+ producerStartCh.publish({ topic, messages, bootstrapServers })
40
49
 
41
50
  const result = send.apply(this, arguments)
42
51
 
@@ -69,6 +78,7 @@ addHook({ name: 'kafkajs', versions: ['>=1.4'] }, (obj) => {
69
78
  const consumer = createConsumer.apply(this, arguments)
70
79
  const run = consumer.run
71
80
 
81
+ const groupId = arguments[0].groupId
72
82
  consumer.run = function ({ eachMessage, ...runArgs }) {
73
83
  if (typeof eachMessage !== 'function') return run({ eachMessage, ...runArgs })
74
84
 
@@ -77,10 +87,9 @@ addHook({ name: 'kafkajs', versions: ['>=1.4'] }, (obj) => {
77
87
  const innerAsyncResource = new AsyncResource('bound-anonymous-fn')
78
88
  return innerAsyncResource.runInAsyncScope(() => {
79
89
  const { topic, partition, message } = eachMessageArgs[0]
80
- consumerStartCh.publish({ topic, partition, message })
90
+ consumerStartCh.publish({ topic, partition, message, groupId })
81
91
  try {
82
92
  const result = eachMessage.apply(this, eachMessageArgs)
83
-
84
93
  if (result && typeof result.then === 'function') {
85
94
  result.then(
86
95
  innerAsyncResource.bind(() => consumerFinishCh.publish(undefined)),
@@ -2,9 +2,10 @@
2
2
 
3
3
  const { addHook } = require('./helpers/instrument')
4
4
  const shimmer = require('../../datadog-shimmer')
5
- const { createWrapRequest } = require('./elasticsearch')
5
+ const { createWrapRequest, createWrapGetConnection } = require('./elasticsearch')
6
6
 
7
7
  addHook({ name: '@opensearch-project/opensearch', file: 'lib/Transport.js', versions: ['>=1'] }, Transport => {
8
8
  shimmer.wrap(Transport.prototype, 'request', createWrapRequest('opensearch'))
9
+ shimmer.wrap(Transport.prototype, 'getConnection', createWrapGetConnection('opensearch'))
9
10
  return Transport
10
11
  })
@@ -11,6 +11,8 @@ const startCh = channel('apm:redis:command:start')
11
11
  const finishCh = channel('apm:redis:command:finish')
12
12
  const errorCh = channel('apm:redis:command:error')
13
13
 
14
+ let createClientUrl
15
+
14
16
  function wrapAddCommand (addCommand) {
15
17
  return function (command) {
16
18
  if (!startCh.hasSubscribers) {
@@ -22,7 +24,7 @@ function wrapAddCommand (addCommand) {
22
24
 
23
25
  const asyncResource = new AsyncResource('bound-anonymous-fn')
24
26
  return asyncResource.runInAsyncScope(() => {
25
- start(this, name, args)
27
+ start(this, name, args, this._url)
26
28
 
27
29
  const res = addCommand.apply(this, arguments)
28
30
  const onResolve = asyncResource.bind(() => finish(finishCh, errorCh))
@@ -35,12 +37,53 @@ function wrapAddCommand (addCommand) {
35
37
  }
36
38
  }
37
39
 
38
- addHook({ name: '@redis/client', file: 'dist/lib/client/commands-queue.js', versions: ['>=1.1'] }, redis => {
40
+ function wrapCommandQueueClass (cls) {
41
+ const ret = class RedisCommandQueue extends cls {
42
+ constructor () {
43
+ super(arguments)
44
+ if (createClientUrl) {
45
+ try {
46
+ const parsed = new URL(createClientUrl)
47
+ if (parsed) {
48
+ this._url = { host: parsed.hostname, port: +parsed.port || 6379 }
49
+ }
50
+ } catch (error) {
51
+ // ignore
52
+ }
53
+ }
54
+ this._url = this._url || { host: 'localhost', port: 6379 }
55
+ }
56
+ }
57
+ return ret
58
+ }
59
+
60
+ function wrapCreateClient (request) {
61
+ return function (opts) {
62
+ createClientUrl = opts && opts.url
63
+ const ret = request.apply(this, arguments)
64
+ createClientUrl = undefined
65
+ return ret
66
+ }
67
+ }
68
+
69
+ addHook({ name: '@node-redis/client', file: 'dist/lib/client/commands-queue.js', versions: ['>=1'] }, redis => {
70
+ redis.default = wrapCommandQueueClass(redis.default)
39
71
  shimmer.wrap(redis.default.prototype, 'addCommand', wrapAddCommand)
40
72
  return redis
41
73
  })
42
74
 
43
- addHook({ name: '@node-redis/client', file: 'dist/lib/client/commands-queue.js', versions: ['>=1'] }, redis => {
75
+ addHook({ name: '@node-redis/client', file: 'dist/lib/client/index.js', versions: ['>=1'] }, redis => {
76
+ shimmer.wrap(redis.default, 'create', wrapCreateClient)
77
+ return redis
78
+ })
79
+
80
+ addHook({ name: '@redis/client', file: 'dist/lib/client/index.js', versions: ['>=1.1'] }, redis => {
81
+ shimmer.wrap(redis.default, 'create', wrapCreateClient)
82
+ return redis
83
+ })
84
+
85
+ addHook({ name: '@redis/client', file: 'dist/lib/client/commands-queue.js', versions: ['>=1.1'] }, redis => {
86
+ redis.default = wrapCommandQueueClass(redis.default)
44
87
  shimmer.wrap(redis.default.prototype, 'addCommand', wrapAddCommand)
45
88
  return redis
46
89
  })
@@ -106,9 +149,9 @@ addHook({ name: 'redis', versions: ['>=0.12 <2.6'] }, redis => {
106
149
  return redis
107
150
  })
108
151
 
109
- function start (client, command, args) {
152
+ function start (client, command, args, url = {}) {
110
153
  const db = client.selected_db
111
- const connectionOptions = client.connection_options || client.connection_option || client.connectionOption || {}
154
+ const connectionOptions = client.connection_options || client.connection_option || client.connectionOption || url
112
155
  startCh.publish({ db, command, args, connectionOptions })
113
156
  }
114
157
 
@@ -1,11 +1,11 @@
1
1
  'use strict'
2
2
 
3
3
  const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
4
- const Plugin = require('../../dd-trace/src/plugins/plugin')
4
+ const ClientPlugin = require('../../dd-trace/src/plugins/client')
5
5
  const { storage } = require('../../datadog-core')
6
6
  const { isTrue } = require('../../dd-trace/src/util')
7
7
 
8
- class BaseAwsSdkPlugin extends Plugin {
8
+ class BaseAwsSdkPlugin extends ClientPlugin {
9
9
  static get id () { return 'aws' }
10
10
 
11
11
  get serviceIdentifier () {
@@ -116,7 +116,7 @@ class BaseAwsSdkPlugin extends Plugin {
116
116
  this.config.hooks.request(span, response)
117
117
  }
118
118
 
119
- span.finish()
119
+ super.finish()
120
120
  }
121
121
 
122
122
  configure (config) {
@@ -4,6 +4,7 @@ const BaseAwsSdkPlugin = require('../base')
4
4
 
5
5
  class DynamoDb extends BaseAwsSdkPlugin {
6
6
  static get id () { return 'dynamodb' }
7
+ static get peerServicePrecursors () { return ['tablename'] }
7
8
 
8
9
  generateTags (params, operation, response) {
9
10
  const tags = {}
@@ -3,6 +3,7 @@ const log = require('../../../dd-trace/src/log')
3
3
  const BaseAwsSdkPlugin = require('../base')
4
4
  class Kinesis extends BaseAwsSdkPlugin {
5
5
  static get id () { return 'kinesis' }
6
+ static get peerServicePrecursors () { return ['streamname'] }
6
7
 
7
8
  generateTags (params, operation, response) {
8
9
  if (!params || !params.StreamName) return {}
@@ -4,6 +4,7 @@ const BaseAwsSdkPlugin = require('../base')
4
4
 
5
5
  class S3 extends BaseAwsSdkPlugin {
6
6
  static get id () { return 's3' }
7
+ static get peerServicePrecursors () { return ['bucketname'] }
7
8
 
8
9
  generateTags (params, operation, response) {
9
10
  const tags = {}
@@ -4,6 +4,7 @@ const BaseAwsSdkPlugin = require('../base')
4
4
 
5
5
  class Sns extends BaseAwsSdkPlugin {
6
6
  static get id () { return 'sns' }
7
+ static get peerServicePrecursors () { return ['topicname'] }
7
8
 
8
9
  generateTags (params, operation, response) {
9
10
  if (!params) return {}
@@ -6,6 +6,7 @@ const { storage } = require('../../../datadog-core')
6
6
 
7
7
  class Sqs extends BaseAwsSdkPlugin {
8
8
  static get id () { return 'sqs' }
9
+ static get peerServicePrecursors () { return ['queuename'] }
9
10
 
10
11
  constructor (...args) {
11
12
  super(...args)
@@ -1,13 +1,14 @@
1
1
  'use strict'
2
2
 
3
- const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
4
3
  const DatabasePlugin = require('../../dd-trace/src/plugins/database')
4
+ const CASSANDRA_CONTACT_POINTS_KEY = 'db.cassandra.contact.points'
5
5
 
6
6
  class CassandraDriverPlugin extends DatabasePlugin {
7
7
  static get id () { return 'cassandra-driver' }
8
8
  static get system () { return 'cassandra' }
9
+ static get peerServicePrecursors () { return [CASSANDRA_CONTACT_POINTS_KEY] }
9
10
 
10
- start ({ keyspace, query, connectionOptions = {} }) {
11
+ start ({ keyspace, query, contactPoints = {} }) {
11
12
  if (Array.isArray(query)) {
12
13
  query = combine(query)
13
14
  }
@@ -21,8 +22,7 @@ class CassandraDriverPlugin extends DatabasePlugin {
21
22
  'db.type': 'cassandra',
22
23
  'cassandra.query': query,
23
24
  'cassandra.keyspace': keyspace,
24
- 'out.host': connectionOptions.host,
25
- [CLIENT_PORT_KEY]: connectionOptions.port
25
+ [CASSANDRA_CONTACT_POINTS_KEY]: contactPoints.join(',') || null
26
26
  }
27
27
  })
28
28
  }
@@ -7,12 +7,13 @@ const { addMetadataTags, getFilter, getMethodMetadata } = require('./util')
7
7
  class GrpcClientPlugin extends ClientPlugin {
8
8
  static get id () { return 'grpc' }
9
9
  static get operation () { return 'client:request' }
10
+ static get peerServicePrecursors () { return ['rpc.service'] }
10
11
 
11
12
  start ({ metadata, path, type }) {
12
13
  const metadataFilter = this.config.metadataFilter
13
14
  const method = getMethodMetadata(path, type)
14
- const span = this.startSpan('grpc.client', {
15
- service: this.config.service,
15
+ const span = this.startSpan(this.operationName(), {
16
+ service: this.config.service || this.serviceName(),
16
17
  resource: path,
17
18
  kind: 'client',
18
19
  type: 'http',
@@ -29,6 +30,11 @@ class GrpcClientPlugin extends ClientPlugin {
29
30
  }
30
31
  })
31
32
 
33
+ // needed as precursor for peer.service
34
+ if (method.service && method.package) {
35
+ span.setTag('rpc.service', method.package + '.' + method.service)
36
+ }
37
+
32
38
  if (metadata) {
33
39
  addMetadataTags(span, metadata, metadataFilter, 'request')
34
40
  inject(this.tracer, span, metadata)
@@ -24,9 +24,9 @@ class GrpcServerPlugin extends ServerPlugin {
24
24
  const metadataFilter = this.config.metadataFilter
25
25
  const childOf = extract(this.tracer, metadata)
26
26
  const method = getMethodMetadata(name, type)
27
- const span = this.startSpan('grpc.server', {
27
+ const span = this.startSpan(this.operationName(), {
28
28
  childOf,
29
- service: this.config.service,
29
+ service: this.config.service || this.serviceName(),
30
30
  resource: name,
31
31
  kind: 'server',
32
32
  type: 'web',
@@ -6,7 +6,12 @@ class KafkajsConsumerPlugin extends ConsumerPlugin {
6
6
  static get id () { return 'kafkajs' }
7
7
  static get operation () { return 'consume' }
8
8
 
9
- start ({ topic, partition, message }) {
9
+ start ({ topic, partition, message, groupId }) {
10
+ if (this.config.dsmEnabled) {
11
+ this.tracer.decodeDataStreamsContext(message.headers['dd-pathway-ctx'])
12
+ this.tracer
13
+ .setCheckpoint(['direction:in', `group:${groupId}`, `topic:${topic}`, 'type:kafka'])
14
+ }
10
15
  const childOf = extract(this.tracer, message.headers)
11
16
  this.startSpan({
12
17
  childOf,
@@ -1,12 +1,21 @@
1
1
  'use strict'
2
2
 
3
3
  const ProducerPlugin = require('../../dd-trace/src/plugins/producer')
4
+ const { encodePathwayContext } = require('../../dd-trace/src/datastreams/pathway')
5
+ const BOOTSTRAP_SERVERS_KEY = 'messaging.kafka.bootstrap.servers'
4
6
 
5
7
  class KafkajsProducerPlugin extends ProducerPlugin {
6
8
  static get id () { return 'kafkajs' }
7
9
  static get operation () { return 'produce' }
10
+ static get peerServicePrecursors () { return [BOOTSTRAP_SERVERS_KEY] }
8
11
 
9
- start ({ topic, messages }) {
12
+ start ({ topic, messages, bootstrapServers }) {
13
+ let pathwayCtx
14
+ if (this.config.dsmEnabled) {
15
+ const dataStreamsContext = this.tracer
16
+ .setCheckpoint(['direction:out', `topic:${topic}`, 'type:kafka'])
17
+ pathwayCtx = encodePathwayContext(dataStreamsContext)
18
+ }
10
19
  const span = this.startSpan({
11
20
  resource: topic,
12
21
  meta: {
@@ -17,9 +26,12 @@ class KafkajsProducerPlugin extends ProducerPlugin {
17
26
  'kafka.batch_size': messages.length
18
27
  }
19
28
  })
20
-
29
+ if (bootstrapServers) {
30
+ span.setTag(BOOTSTRAP_SERVERS_KEY, bootstrapServers)
31
+ }
21
32
  for (const message of messages) {
22
33
  if (typeof message === 'object') {
34
+ if (this.config.dsmEnabled) message.headers['dd-pathway-ctx'] = pathwayCtx
23
35
  this.tracer.inject(span, 'text_map', message.headers)
24
36
  }
25
37
  }
@@ -5,17 +5,19 @@ const DatabasePlugin = require('../../dd-trace/src/plugins/database')
5
5
  class MongodbCorePlugin extends DatabasePlugin {
6
6
  static get id () { return 'mongodb-core' }
7
7
  static get component () { return 'mongodb' }
8
-
8
+ // avoid using db.name for peer.service since it includes the collection name
9
+ // should be removed if one day this will be fixed
10
+ static get peerServicePrecursors () { return [] }
9
11
  start ({ ns, ops, options = {}, name }) {
10
12
  const query = getQuery(ops)
11
13
  const resource = truncate(getResource(this, ns, query, name))
12
-
13
14
  this.startSpan(this.operationName(), {
14
15
  service: this.serviceName(this.config),
15
16
  resource,
16
17
  type: 'mongodb',
17
18
  kind: 'client',
18
19
  meta: {
20
+ // this is not technically correct since it includes the collection but we changing will break customer stuff
19
21
  'db.name': ns,
20
22
  'mongodb.query': query,
21
23
  'out.host': options.host,
@@ -23,6 +25,15 @@ class MongodbCorePlugin extends DatabasePlugin {
23
25
  }
24
26
  })
25
27
  }
28
+
29
+ getPeerService (tags) {
30
+ const ns = tags['db.name']
31
+ if (ns && tags['peer.service'] === undefined) {
32
+ // the mongo ns is either dbName either dbName.collection. So we keep the first part
33
+ tags['peer.service'] = ns.split('.', 1)[0]
34
+ }
35
+ return super.getPeerService(tags)
36
+ }
26
37
  }
27
38
 
28
39
  function getQuery (cmd) {
@@ -100,7 +100,7 @@ class OpenApiPlugin extends TracingPlugin {
100
100
  }
101
101
 
102
102
  // createChatCompletion, createCompletion
103
- if ('logit_bias' in payload) {
103
+ if (typeof payload.logit_bias === 'object' && payload.logit_bias) {
104
104
  for (const [tokenId, bias] of Object.entries(payload.logit_bias)) {
105
105
  tags[`openai.request.logit_bias.${tokenId}`] = bias
106
106
  }
@@ -264,6 +264,8 @@ function retrieveModelRequestExtraction (tags, payload) {
264
264
  }
265
265
 
266
266
  function createChatCompletionRequestExtraction (tags, payload, store) {
267
+ if (!defensiveArrayLength(payload.messages)) return
268
+
267
269
  store.messages = payload.messages
268
270
  for (let i = 0; i < payload.messages.length; i++) {
269
271
  const message = payload.messages[i]
@@ -411,7 +413,7 @@ function createFineTuneRequestExtraction (tags, body) {
411
413
  tags['openai.request.compute_classification_metrics'] = body.compute_classification_metrics
412
414
  tags['openai.request.classification_n_classes'] = body.classification_n_classes
413
415
  tags['openai.request.classification_positive_class'] = body.classification_positive_class
414
- tags['openai.request.classification_betas_count'] = body.classification_betas.length
416
+ tags['openai.request.classification_betas_count'] = defensiveArrayLength(body.classification_betas)
415
417
  }
416
418
 
417
419
  function commonFineTuneResponseExtraction (tags, body) {
@@ -521,6 +523,7 @@ function commonCreateResponseExtraction (tags, body, store) {
521
523
 
522
524
  // createCompletion, createChatCompletion, createEdit, createEmbedding
523
525
  function usageExtraction (tags, body) {
526
+ if (typeof body.usage !== 'object' || !body.usage) return
524
527
  tags['openai.response.usage.prompt_tokens'] = body.usage.prompt_tokens
525
528
  tags['openai.response.usage.completion_tokens'] = body.usage.completion_tokens
526
529
  tags['openai.response.usage.total_tokens'] = body.usage.total_tokens
@@ -675,4 +678,8 @@ function normalizeStringOrTokenArray (input, truncate = true) {
675
678
  return truncate ? truncateText(normalized) : normalized
676
679
  }
677
680
 
681
+ function defensiveArrayLength (maybeArray) {
682
+ return Array.isArray(maybeArray) ? maybeArray.length : undefined
683
+ }
684
+
678
685
  module.exports = OpenApiPlugin
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const DogStatsDClient = require('../../dd-trace/src/dogstatsd')
3
+ const { DogStatsDClient, NoopDogStatsDClient } = require('../../dd-trace/src/dogstatsd')
4
4
  const ExternalLogger = require('../../dd-trace/src/external-logger/src')
5
5
 
6
6
  const FLUSH_INTERVAL = 10 * 1000
@@ -10,15 +10,19 @@ let logger = null
10
10
  let interval = null
11
11
 
12
12
  module.exports.init = function (tracerConfig) {
13
- metrics = new DogStatsDClient({
14
- host: tracerConfig.dogstatsd.hostname,
15
- port: tracerConfig.dogstatsd.port,
16
- tags: [
17
- `service:${tracerConfig.tags.service}`,
18
- `env:${tracerConfig.tags.env}`,
19
- `version:${tracerConfig.tags.version}`
20
- ]
21
- })
13
+ if (tracerConfig.dogstatsd) {
14
+ metrics = new DogStatsDClient({
15
+ host: tracerConfig.dogstatsd.hostname,
16
+ port: tracerConfig.dogstatsd.port,
17
+ tags: [
18
+ `service:${tracerConfig.tags.service}`,
19
+ `env:${tracerConfig.tags.env}`,
20
+ `version:${tracerConfig.tags.version}`
21
+ ]
22
+ })
23
+ } else {
24
+ metrics = new NoopDogStatsDClient()
25
+ }
22
26
 
23
27
  logger = new ExternalLogger({
24
28
  ddsource: 'openai',