dd-trace 5.25.0 → 5.27.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 (74) hide show
  1. package/LICENSE-3rdparty.csv +2 -0
  2. package/index.d.ts +17 -8
  3. package/init.js +60 -47
  4. package/package.json +5 -2
  5. package/packages/datadog-core/index.js +1 -3
  6. package/packages/datadog-core/src/storage.js +21 -0
  7. package/packages/datadog-instrumentations/src/express.js +1 -1
  8. package/packages/datadog-instrumentations/src/handlebars.js +40 -0
  9. package/packages/datadog-instrumentations/src/helpers/hooks.js +5 -0
  10. package/packages/datadog-instrumentations/src/jest.js +6 -2
  11. package/packages/datadog-instrumentations/src/langchain.js +77 -0
  12. package/packages/datadog-instrumentations/src/next.js +19 -7
  13. package/packages/datadog-instrumentations/src/pug.js +23 -0
  14. package/packages/datadog-instrumentations/src/router.js +2 -3
  15. package/packages/datadog-plugin-aws-sdk/src/base.js +5 -0
  16. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +7 -6
  17. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +34 -0
  18. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +8 -8
  19. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +59 -45
  20. package/packages/datadog-plugin-cypress/src/support.js +1 -0
  21. package/packages/datadog-plugin-http/src/client.js +42 -1
  22. package/packages/datadog-plugin-http2/src/client.js +26 -1
  23. package/packages/datadog-plugin-langchain/src/handlers/chain.js +50 -0
  24. package/packages/datadog-plugin-langchain/src/handlers/default.js +53 -0
  25. package/packages/datadog-plugin-langchain/src/handlers/embedding.js +63 -0
  26. package/packages/datadog-plugin-langchain/src/handlers/language_models/chat_model.js +99 -0
  27. package/packages/datadog-plugin-langchain/src/handlers/language_models/index.js +48 -0
  28. package/packages/datadog-plugin-langchain/src/handlers/language_models/llm.js +57 -0
  29. package/packages/datadog-plugin-langchain/src/index.js +89 -0
  30. package/packages/datadog-plugin-langchain/src/tokens.js +35 -0
  31. package/packages/datadog-plugin-mocha/src/index.js +1 -1
  32. package/packages/datadog-plugin-moleculer/src/server.js +0 -1
  33. package/packages/dd-trace/src/appsec/api_security_sampler.js +50 -27
  34. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  35. package/packages/dd-trace/src/appsec/iast/analyzers/header-injection-analyzer.js +33 -16
  36. package/packages/dd-trace/src/appsec/iast/analyzers/template-injection-analyzer.js +18 -0
  37. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +3 -2
  38. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +1 -0
  39. package/packages/dd-trace/src/appsec/index.js +6 -6
  40. package/packages/dd-trace/src/appsec/recommended.json +353 -155
  41. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +1 -1
  42. package/packages/dd-trace/src/appsec/remote_config/index.js +0 -7
  43. package/packages/dd-trace/src/appsec/reporter.js +1 -0
  44. package/packages/dd-trace/src/appsec/sdk/utils.js +21 -2
  45. package/packages/dd-trace/src/config.js +21 -4
  46. package/packages/dd-trace/src/constants.js +6 -1
  47. package/packages/dd-trace/src/crashtracking/crashtracker.js +98 -0
  48. package/packages/dd-trace/src/crashtracking/index.js +15 -0
  49. package/packages/dd-trace/src/crashtracking/noop.js +8 -0
  50. package/packages/dd-trace/src/llmobs/sdk.js +1 -1
  51. package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
  52. package/packages/dd-trace/src/llmobs/writers/spans/base.js +3 -0
  53. package/packages/dd-trace/src/log/index.js +10 -13
  54. package/packages/dd-trace/src/log/log.js +52 -0
  55. package/packages/dd-trace/src/log/writer.js +50 -19
  56. package/packages/dd-trace/src/noop/span.js +1 -0
  57. package/packages/dd-trace/src/opentelemetry/span.js +15 -0
  58. package/packages/dd-trace/src/opentracing/propagation/text_map.js +35 -22
  59. package/packages/dd-trace/src/opentracing/span.js +14 -0
  60. package/packages/dd-trace/src/opentracing/span_context.js +1 -0
  61. package/packages/dd-trace/src/plugins/index.js +3 -0
  62. package/packages/dd-trace/src/plugins/tracing.js +2 -2
  63. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +121 -0
  64. package/packages/dd-trace/src/plugins/util/ip_extractor.js +0 -1
  65. package/packages/dd-trace/src/plugins/util/web.js +39 -11
  66. package/packages/dd-trace/src/profiling/exporters/agent.js +42 -5
  67. package/packages/dd-trace/src/profiling/profiler.js +5 -2
  68. package/packages/dd-trace/src/proxy.js +5 -0
  69. package/packages/dd-trace/src/telemetry/logs/index.js +16 -11
  70. package/packages/dd-trace/src/telemetry/logs/log-collector.js +3 -8
  71. package/packages/dd-trace/src/telemetry/metrics.js +6 -1
  72. package/packages/dd-trace/src/util.js +16 -1
  73. package/version.js +4 -2
  74. /package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/{code-injection-sensitive-analyzer.js → tainted-range-based-sensitive-analyzer.js} +0 -0
@@ -1,4 +1,5 @@
1
1
  Component,Origin,License,Copyright
2
+ require,@datadog/libdatadog,Apache license 2.0,Copyright 2024 Datadog Inc.
2
3
  require,@datadog/native-appsec,Apache license 2.0,Copyright 2018 Datadog Inc.
3
4
  require,@datadog/native-metrics,Apache license 2.0,Copyright 2018 Datadog Inc.
4
5
  require,@datadog/native-iast-rewriter,Apache license 2.0,Copyright 2018 Datadog Inc.
@@ -7,6 +8,7 @@ require,@datadog/pprof,Apache license 2.0,Copyright 2019 Google Inc.
7
8
  require,@datadog/sketches-js,Apache license 2.0,Copyright 2020 Datadog Inc.
8
9
  require,@opentelemetry/api,Apache license 2.0,Copyright OpenTelemetry Authors
9
10
  require,@opentelemetry/core,Apache license 2.0,Copyright OpenTelemetry Authors
11
+ require,@isaacs/ttlcache,ISC,Copyright (c) 2022-2023 - Isaac Z. Schlueter and Contributors
10
12
  require,crypto-randomuuid,MIT,Copyright 2021 Node.js Foundation and contributors
11
13
  require,dc-polyfill,MIT,Copyright 2023 Datadog Inc.
12
14
  require,ignore,MIT,Copyright 2013 Kael Zhang and contributors
package/index.d.ts CHANGED
@@ -179,6 +179,7 @@ interface Plugins {
179
179
  "kafkajs": tracer.plugins.kafkajs
180
180
  "knex": tracer.plugins.knex;
181
181
  "koa": tracer.plugins.koa;
182
+ "langchain": tracer.plugins.langchain;
182
183
  "mariadb": tracer.plugins.mariadb;
183
184
  "memcached": tracer.plugins.memcached;
184
185
  "microgateway-core": tracer.plugins.microgateway_core;
@@ -662,19 +663,13 @@ declare namespace tracer {
662
663
  mode?: 'safe' | 'extended' | 'disabled'
663
664
  },
664
665
  /**
665
- * Configuration for Api Security sampling
666
+ * Configuration for Api Security
666
667
  */
667
668
  apiSecurity?: {
668
669
  /** Whether to enable Api Security.
669
- * @default false
670
+ * @default true
670
671
  */
671
672
  enabled?: boolean,
672
-
673
- /** Controls the request sampling rate (between 0 and 1) in which Api Security is triggered.
674
- * The value will be coerced back if it's outside of the 0-1 range.
675
- * @default 0.1
676
- */
677
- requestSampling?: number
678
673
  },
679
674
  /**
680
675
  * Configuration for RASP
@@ -1043,6 +1038,14 @@ declare namespace tracer {
1043
1038
  * @default code => code < 500
1044
1039
  */
1045
1040
  validateStatus?: (code: number) => boolean;
1041
+
1042
+ /**
1043
+ * Enable injection of tracing headers into requests signed with AWS IAM headers.
1044
+ * Disable this if you get AWS signature errors (HTTP 403).
1045
+ *
1046
+ * @default false
1047
+ */
1048
+ enablePropagationWithAmazonHeaders?: boolean;
1046
1049
  }
1047
1050
 
1048
1051
  /** @hidden */
@@ -1590,6 +1593,12 @@ declare namespace tracer {
1590
1593
  */
1591
1594
  interface kafkajs extends Instrumentation {}
1592
1595
 
1596
+ /**
1597
+ * This plugin automatically instruments the
1598
+ * [langchain](https://js.langchain.com/) module
1599
+ */
1600
+ interface langchain extends Instrumentation {}
1601
+
1593
1602
  /**
1594
1603
  * This plugin automatically instruments the
1595
1604
  * [ldapjs](https://github.com/ldapjs/node-ldapjs/) module.
package/init.js CHANGED
@@ -1,58 +1,71 @@
1
1
  'use strict'
2
2
 
3
- const path = require('path')
4
- const Module = require('module')
5
- const semver = require('semver')
6
- const log = require('./packages/dd-trace/src/log')
7
- const { isTrue } = require('./packages/dd-trace/src/util')
8
- const telemetry = require('./packages/dd-trace/src/telemetry/init-telemetry')
3
+ /* eslint-disable no-var */
9
4
 
10
- let initBailout = false
11
- let clobberBailout = false
12
- const forced = isTrue(process.env.DD_INJECT_FORCE)
5
+ var NODE_MAJOR = require('./version').NODE_MAJOR
13
6
 
14
- if (process.env.DD_INJECTION_ENABLED) {
15
- // If we're running via single-step install, and we're not in the app's
16
- // node_modules, then we should not initialize the tracer. This prevents
17
- // single-step-installed tracer from clobbering the manually-installed tracer.
18
- let resolvedInApp
19
- const entrypoint = process.argv[1]
20
- try {
21
- resolvedInApp = Module.createRequire(entrypoint).resolve('dd-trace')
22
- } catch (e) {
23
- // Ignore. If we can't resolve the module, we assume it's not in the app.
24
- }
25
- if (resolvedInApp) {
26
- const ourselves = path.join(__dirname, 'index.js')
27
- if (ourselves !== resolvedInApp) {
28
- clobberBailout = true
7
+ // We use several things that are not supported by older versions of Node:
8
+ // - AsyncLocalStorage
9
+ // - The `semver` module
10
+ // - dc-polyfill
11
+ // - Mocha (for testing)
12
+ // and probably others.
13
+ // TODO: Remove all these dependencies so that we can report telemetry.
14
+ if (NODE_MAJOR >= 12) {
15
+ var path = require('path')
16
+ var Module = require('module')
17
+ var semver = require('semver')
18
+ var log = require('./packages/dd-trace/src/log')
19
+ var isTrue = require('./packages/dd-trace/src/util').isTrue
20
+ var telemetry = require('./packages/dd-trace/src/telemetry/init-telemetry')
21
+
22
+ var initBailout = false
23
+ var clobberBailout = false
24
+ var forced = isTrue(process.env.DD_INJECT_FORCE)
25
+
26
+ if (process.env.DD_INJECTION_ENABLED) {
27
+ // If we're running via single-step install, and we're not in the app's
28
+ // node_modules, then we should not initialize the tracer. This prevents
29
+ // single-step-installed tracer from clobbering the manually-installed tracer.
30
+ var resolvedInApp
31
+ var entrypoint = process.argv[1]
32
+ try {
33
+ resolvedInApp = Module.createRequire(entrypoint).resolve('dd-trace')
34
+ } catch (e) {
35
+ // Ignore. If we can't resolve the module, we assume it's not in the app.
36
+ }
37
+ if (resolvedInApp) {
38
+ var ourselves = path.join(__dirname, 'index.js')
39
+ if (ourselves !== resolvedInApp) {
40
+ clobberBailout = true
41
+ }
29
42
  }
30
- }
31
43
 
32
- // If we're running via single-step install, and the runtime doesn't match
33
- // the engines field in package.json, then we should not initialize the tracer.
34
- if (!clobberBailout) {
35
- const { engines } = require('./package.json')
36
- const version = process.versions.node
37
- if (!semver.satisfies(version, engines.node)) {
38
- initBailout = true
39
- telemetry([
40
- { name: 'abort', tags: ['reason:incompatible_runtime'] },
41
- { name: 'abort.runtime', tags: [] }
42
- ])
43
- log.info('Aborting application instrumentation due to incompatible_runtime.')
44
- log.info(`Found incompatible runtime nodejs ${version}, Supported runtimes: nodejs ${engines.node}.`)
45
- if (forced) {
46
- log.info('DD_INJECT_FORCE enabled, allowing unsupported runtimes and continuing.')
44
+ // If we're running via single-step install, and the runtime doesn't match
45
+ // the engines field in package.json, then we should not initialize the tracer.
46
+ if (!clobberBailout) {
47
+ var engines = require('./package.json').engines
48
+ var version = process.versions.node
49
+ if (!semver.satisfies(version, engines.node)) {
50
+ initBailout = true
51
+ telemetry([
52
+ { name: 'abort', tags: ['reason:incompatible_runtime'] },
53
+ { name: 'abort.runtime', tags: [] }
54
+ ])
55
+ log.info('Aborting application instrumentation due to incompatible_runtime.')
56
+ log.info('Found incompatible runtime nodejs ' + version + ', Supported runtimes: nodejs ' + engines.node + '.')
57
+ if (forced) {
58
+ log.info('DD_INJECT_FORCE enabled, allowing unsupported runtimes and continuing.')
59
+ }
47
60
  }
48
61
  }
49
62
  }
50
- }
51
63
 
52
- if (!clobberBailout && (!initBailout || forced)) {
53
- const tracer = require('.')
54
- tracer.init()
55
- module.exports = tracer
56
- telemetry('complete', [`injection_forced:${forced && initBailout ? 'true' : 'false'}`])
57
- log.info('Application instrumentation bootstrapping complete')
64
+ if (!clobberBailout && (!initBailout || forced)) {
65
+ var tracer = require('.')
66
+ tracer.init()
67
+ module.exports = tracer
68
+ telemetry('complete', ['injection_forced:' + (forced && initBailout ? 'true' : 'false')])
69
+ log.info('Application instrumentation bootstrapping complete')
70
+ }
58
71
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "5.25.0",
3
+ "version": "5.27.0",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -15,6 +15,7 @@
15
15
  "type:test": "cd docs && yarn && yarn test",
16
16
  "lint": "node scripts/check_licenses.js && eslint . && yarn audit",
17
17
  "lint-fix": "node scripts/check_licenses.js && eslint . --fix && yarn audit",
18
+ "release:proposal": "node scripts/release/proposal",
18
19
  "services": "node ./scripts/install_plugin_modules && node packages/dd-trace/test/setup/services",
19
20
  "test": "SERVICES=* yarn services && mocha --expose-gc 'packages/dd-trace/test/setup/node.js' 'packages/*/test/**/*.spec.js'",
20
21
  "test:appsec": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" --exclude \"packages/dd-trace/test/appsec/**/*.plugin.spec.js\" \"packages/dd-trace/test/appsec/**/*.spec.js\"",
@@ -81,12 +82,14 @@
81
82
  "node": ">=18"
82
83
  },
83
84
  "dependencies": {
84
- "@datadog/native-appsec": "8.2.1",
85
+ "@datadog/libdatadog": "^0.2.2",
86
+ "@datadog/native-appsec": "8.3.0",
85
87
  "@datadog/native-iast-rewriter": "2.5.0",
86
88
  "@datadog/native-iast-taint-tracking": "3.2.0",
87
89
  "@datadog/native-metrics": "^3.0.1",
88
90
  "@datadog/pprof": "5.4.1",
89
91
  "@datadog/sketches-js": "^2.1.0",
92
+ "@isaacs/ttlcache": "^1.4.1",
90
93
  "@opentelemetry/api": ">=1.0.0 <1.9.0",
91
94
  "@opentelemetry/core": "^1.14.0",
92
95
  "crypto-randomuuid": "^1.0.0",
@@ -1,7 +1,5 @@
1
1
  'use strict'
2
2
 
3
- const { AsyncLocalStorage } = require('async_hooks')
4
-
5
- const storage = new AsyncLocalStorage()
3
+ const storage = require('./src/storage')
6
4
 
7
5
  module.exports = { storage }
@@ -0,0 +1,21 @@
1
+ 'use strict'
2
+
3
+ const { AsyncLocalStorage } = require('async_hooks')
4
+
5
+ const storages = Object.create(null)
6
+ const legacyStorage = new AsyncLocalStorage()
7
+
8
+ const storage = function (namespace) {
9
+ if (!storages[namespace]) {
10
+ storages[namespace] = new AsyncLocalStorage()
11
+ }
12
+ return storages[namespace]
13
+ }
14
+
15
+ storage.disable = legacyStorage.disable.bind(legacyStorage)
16
+ storage.enterWith = legacyStorage.enterWith.bind(legacyStorage)
17
+ storage.exit = legacyStorage.exit.bind(legacyStorage)
18
+ storage.getStore = legacyStorage.getStore.bind(legacyStorage)
19
+ storage.run = legacyStorage.run.bind(legacyStorage)
20
+
21
+ module.exports = storage
@@ -29,7 +29,7 @@ function wrapResponseJson (json) {
29
29
  obj = arguments[1]
30
30
  }
31
31
 
32
- responseJsonChannel.publish({ req: this.req, body: obj })
32
+ responseJsonChannel.publish({ req: this.req, res: this, body: obj })
33
33
  }
34
34
 
35
35
  return json.apply(this, arguments)
@@ -0,0 +1,40 @@
1
+ 'use strict'
2
+
3
+ const shimmer = require('../../datadog-shimmer')
4
+ const { channel, addHook } = require('./helpers/instrument')
5
+
6
+ const handlebarsCompileCh = channel('datadog:handlebars:compile:start')
7
+ const handlebarsRegisterPartialCh = channel('datadog:handlebars:register-partial:start')
8
+
9
+ function wrapCompile (compile) {
10
+ return function wrappedCompile (source) {
11
+ if (handlebarsCompileCh.hasSubscribers) {
12
+ handlebarsCompileCh.publish({ source })
13
+ }
14
+
15
+ return compile.apply(this, arguments)
16
+ }
17
+ }
18
+
19
+ function wrapRegisterPartial (registerPartial) {
20
+ return function wrappedRegisterPartial (name, partial) {
21
+ if (handlebarsRegisterPartialCh.hasSubscribers) {
22
+ handlebarsRegisterPartialCh.publish({ partial })
23
+ }
24
+
25
+ return registerPartial.apply(this, arguments)
26
+ }
27
+ }
28
+
29
+ addHook({ name: 'handlebars', file: 'dist/cjs/handlebars/compiler/compiler.js', versions: ['>=4.0.0'] }, compiler => {
30
+ shimmer.wrap(compiler, 'compile', wrapCompile)
31
+ shimmer.wrap(compiler, 'precompile', wrapCompile)
32
+
33
+ return compiler
34
+ })
35
+
36
+ addHook({ name: 'handlebars', file: 'dist/cjs/handlebars/base.js', versions: ['>=4.0.0'] }, base => {
37
+ shimmer.wrap(base.HandlebarsEnvironment.prototype, 'registerPartial', wrapRegisterPartial)
38
+
39
+ return base
40
+ })
@@ -19,6 +19,8 @@ module.exports = {
19
19
  '@jest/test-sequencer': () => require('../jest'),
20
20
  '@jest/transform': () => require('../jest'),
21
21
  '@koa/router': () => require('../koa'),
22
+ '@langchain/core': () => require('../langchain'),
23
+ '@langchain/openai': () => require('../langchain'),
22
24
  '@node-redis/client': () => require('../redis'),
23
25
  '@opensearch-project/opensearch': () => require('../opensearch'),
24
26
  '@opentelemetry/sdk-trace-node': () => require('../otel-sdk-trace'),
@@ -51,6 +53,7 @@ module.exports = {
51
53
  'generic-pool': () => require('../generic-pool'),
52
54
  graphql: () => require('../graphql'),
53
55
  grpc: () => require('../grpc'),
56
+ handlebars: () => require('../handlebars'),
54
57
  hapi: () => require('../hapi'),
55
58
  http: () => require('../http'),
56
59
  http2: () => require('../http2'),
@@ -66,6 +69,7 @@ module.exports = {
66
69
  koa: () => require('../koa'),
67
70
  'koa-router': () => require('../koa'),
68
71
  kafkajs: () => require('../kafkajs'),
72
+ langchain: () => require('../langchain'),
69
73
  ldapjs: () => require('../ldapjs'),
70
74
  'limitd-client': () => require('../limitd-client'),
71
75
  lodash: () => require('../lodash'),
@@ -105,6 +109,7 @@ module.exports = {
105
109
  'promise-js': () => require('../promise-js'),
106
110
  promise: () => require('../promise'),
107
111
  protobufjs: () => require('../protobufjs'),
112
+ pug: () => require('../pug'),
108
113
  q: () => require('../q'),
109
114
  qs: () => require('../qs'),
110
115
  redis: () => require('../redis'),
@@ -127,6 +127,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
127
127
 
128
128
  if (repositoryRoot) {
129
129
  this.testSourceFile = getTestSuitePath(context.testPath, repositoryRoot)
130
+ this.repositoryRoot = repositoryRoot
130
131
  }
131
132
 
132
133
  this.isEarlyFlakeDetectionEnabled = this.testEnvironmentOptions._ddIsEarlyFlakeDetectionEnabled
@@ -667,10 +668,13 @@ function jestAdapterWrapper (jestAdapter, jestVersion) {
667
668
  * controls whether coverage is reported.
668
669
  */
669
670
  if (environment.testEnvironmentOptions?._ddTestCodeCoverageEnabled) {
671
+ const root = environment.repositoryRoot || environment.rootDir
672
+
670
673
  const coverageFiles = getCoveredFilenamesFromCoverage(environment.global.__coverage__)
671
- .map(filename => getTestSuitePath(filename, environment.rootDir))
674
+ .map(filename => getTestSuitePath(filename, root))
675
+
672
676
  asyncResource.runInAsyncScope(() => {
673
- testSuiteCodeCoverageCh.publish({ coverageFiles, testSuite: environment.testSuite })
677
+ testSuiteCodeCoverageCh.publish({ coverageFiles, testSuite: environment.testSourceFile })
674
678
  })
675
679
  }
676
680
  testSuiteFinishCh.publish({ status, errorMessage })
@@ -0,0 +1,77 @@
1
+ 'use strict'
2
+
3
+ const { addHook } = require('./helpers/instrument')
4
+ const shimmer = require('../../datadog-shimmer')
5
+
6
+ const tracingChannel = require('dc-polyfill').tracingChannel
7
+
8
+ const invokeTracingChannel = tracingChannel('apm:langchain:invoke')
9
+
10
+ function wrapLangChainPromise (fn, type, namespace = []) {
11
+ return function () {
12
+ if (!invokeTracingChannel.start.hasSubscribers) {
13
+ return fn.apply(this, arguments)
14
+ }
15
+
16
+ // Runnable interfaces have an `lc_namespace` property
17
+ const ns = this.lc_namespace || namespace
18
+ const resource = [...ns, this.constructor.name].join('.')
19
+
20
+ const ctx = {
21
+ args: arguments,
22
+ instance: this,
23
+ type,
24
+ resource
25
+ }
26
+
27
+ return invokeTracingChannel.tracePromise(fn, ctx, this, ...arguments)
28
+ }
29
+ }
30
+
31
+ // langchain compiles into ESM and CommonJS, with ESM being the default and landing in the `.js` files
32
+ // however, CommonJS ends up in `cjs` files, and are required under the hood with `.cjs` files
33
+ // we patch each separately and explicitly to match against exports only once, and not rely on file regex matching
34
+ const extensions = ['js', 'cjs']
35
+
36
+ for (const extension of extensions) {
37
+ addHook({ name: '@langchain/core', file: `dist/runnables/base.${extension}`, versions: ['>=0.1'] }, exports => {
38
+ const RunnableSequence = exports.RunnableSequence
39
+ shimmer.wrap(RunnableSequence.prototype, 'invoke', invoke => wrapLangChainPromise(invoke, 'chain'))
40
+ shimmer.wrap(RunnableSequence.prototype, 'batch', batch => wrapLangChainPromise(batch, 'chain'))
41
+ return exports
42
+ })
43
+
44
+ addHook({
45
+ name: '@langchain/core',
46
+ file: `dist/language_models/chat_models.${extension}`,
47
+ versions: ['>=0.1']
48
+ }, exports => {
49
+ const BaseChatModel = exports.BaseChatModel
50
+ shimmer.wrap(
51
+ BaseChatModel.prototype,
52
+ 'generate',
53
+ generate => wrapLangChainPromise(generate, 'chat_model')
54
+ )
55
+ return exports
56
+ })
57
+
58
+ addHook({ name: '@langchain/core', file: `dist/language_models/llms.${extension}`, versions: ['>=0.1'] }, exports => {
59
+ const BaseLLM = exports.BaseLLM
60
+ shimmer.wrap(BaseLLM.prototype, 'generate', generate => wrapLangChainPromise(generate, 'llm'))
61
+ return exports
62
+ })
63
+
64
+ addHook({ name: '@langchain/openai', file: `dist/embeddings.${extension}`, versions: ['>=0.1'] }, exports => {
65
+ const OpenAIEmbeddings = exports.OpenAIEmbeddings
66
+
67
+ // OpenAI (and Embeddings in general) do not define an lc_namespace
68
+ const namespace = ['langchain', 'embeddings', 'openai']
69
+ shimmer.wrap(OpenAIEmbeddings.prototype, 'embedDocuments', embedDocuments =>
70
+ wrapLangChainPromise(embedDocuments, 'embedding', namespace)
71
+ )
72
+ shimmer.wrap(OpenAIEmbeddings.prototype, 'embedQuery', embedQuery =>
73
+ wrapLangChainPromise(embedQuery, 'embedding', namespace)
74
+ )
75
+ return exports
76
+ })
77
+ }
@@ -14,8 +14,14 @@ const queryParsedChannel = channel('apm:next:query-parsed')
14
14
  const requests = new WeakSet()
15
15
  const nodeNextRequestsToNextRequests = new WeakMap()
16
16
 
17
+ // Next.js <= 14.2.6
17
18
  const MIDDLEWARE_HEADER = 'x-middleware-invoke'
18
19
 
20
+ // Next.js >= 14.2.7
21
+ const NEXT_REQUEST_META = Symbol.for('NextInternalRequestMeta')
22
+ const META_IS_MIDDLEWARE = 'middlewareInvoke'
23
+ const encounteredMiddleware = new WeakSet()
24
+
19
25
  function wrapHandleRequest (handleRequest) {
20
26
  return function (req, res, pathname, query) {
21
27
  return instrument(req, res, () => handleRequest.apply(this, arguments))
@@ -111,6 +117,11 @@ function getPageFromPath (page, dynamicRoutes = []) {
111
117
  return getPagePath(page)
112
118
  }
113
119
 
120
+ function getRequestMeta (req, key) {
121
+ const meta = req[NEXT_REQUEST_META] || {}
122
+ return typeof key === 'string' ? meta[key] : meta
123
+ }
124
+
114
125
  function instrument (req, res, error, handler) {
115
126
  if (typeof error === 'function') {
116
127
  handler = error
@@ -121,8 +132,9 @@ function instrument (req, res, error, handler) {
121
132
  res = res.originalResponse || res
122
133
 
123
134
  // TODO support middleware properly in the future?
124
- const isMiddleware = req.headers[MIDDLEWARE_HEADER]
125
- if (isMiddleware || requests.has(req)) {
135
+ const isMiddleware = req.headers[MIDDLEWARE_HEADER] || getRequestMeta(req, META_IS_MIDDLEWARE)
136
+ if ((isMiddleware && !encounteredMiddleware.has(req)) || requests.has(req)) {
137
+ encounteredMiddleware.add(req)
126
138
  if (error) {
127
139
  errorChannel.publish({ error })
128
140
  }
@@ -188,7 +200,7 @@ function finish (ctx, result, err) {
188
200
  // however, it is not provided as a class function or exported property
189
201
  addHook({
190
202
  name: 'next',
191
- versions: ['>=13.3.0 <14.2.7'],
203
+ versions: ['>=13.3.0 <15'],
192
204
  file: 'dist/server/web/spec-extension/adapters/next-request.js'
193
205
  }, NextRequestAdapter => {
194
206
  shimmer.wrap(NextRequestAdapter.NextRequestAdapter, 'fromNodeNextRequest', fromNodeNextRequest => {
@@ -203,7 +215,7 @@ addHook({
203
215
 
204
216
  addHook({
205
217
  name: 'next',
206
- versions: ['>=11.1 <14.2.7'],
218
+ versions: ['>=11.1 <15'],
207
219
  file: 'dist/server/serve-static.js'
208
220
  }, serveStatic => shimmer.wrap(serveStatic, 'serveStatic', wrapServeStatic))
209
221
 
@@ -213,7 +225,7 @@ addHook({
213
225
  file: 'dist/next-server/server/serve-static.js'
214
226
  }, serveStatic => shimmer.wrap(serveStatic, 'serveStatic', wrapServeStatic))
215
227
 
216
- addHook({ name: 'next', versions: ['>=11.1 <14.2.7'], file: 'dist/server/next-server.js' }, nextServer => {
228
+ addHook({ name: 'next', versions: ['>=11.1 <15'], file: 'dist/server/next-server.js' }, nextServer => {
217
229
  const Server = nextServer.default
218
230
 
219
231
  shimmer.wrap(Server.prototype, 'handleRequest', wrapHandleRequest)
@@ -230,7 +242,7 @@ addHook({ name: 'next', versions: ['>=11.1 <14.2.7'], file: 'dist/server/next-se
230
242
  })
231
243
 
232
244
  // `handleApiRequest` changes parameters/implementation at 13.2.0
233
- addHook({ name: 'next', versions: ['>=13.2 <14.2.7'], file: 'dist/server/next-server.js' }, nextServer => {
245
+ addHook({ name: 'next', versions: ['>=13.2 <15'], file: 'dist/server/next-server.js' }, nextServer => {
234
246
  const Server = nextServer.default
235
247
  shimmer.wrap(Server.prototype, 'handleApiRequest', wrapHandleApiRequestWithMatch)
236
248
  return nextServer
@@ -264,7 +276,7 @@ addHook({
264
276
 
265
277
  addHook({
266
278
  name: 'next',
267
- versions: ['>=13 <14.2.7'],
279
+ versions: ['>=13 <15'],
268
280
  file: 'dist/server/web/spec-extension/request.js'
269
281
  }, request => {
270
282
  const nextUrlDescriptor = Object.getOwnPropertyDescriptor(request.NextRequest.prototype, 'nextUrl')
@@ -0,0 +1,23 @@
1
+ 'use strict'
2
+
3
+ const shimmer = require('../../datadog-shimmer')
4
+ const { channel, addHook } = require('./helpers/instrument')
5
+
6
+ const pugCompileCh = channel('datadog:pug:compile:start')
7
+
8
+ function wrapCompile (compile) {
9
+ return function wrappedCompile (source) {
10
+ if (pugCompileCh.hasSubscribers) {
11
+ pugCompileCh.publish({ source })
12
+ }
13
+
14
+ return compile.apply(this, arguments)
15
+ }
16
+ }
17
+
18
+ addHook({ name: 'pug', versions: ['>=2.0.4'] }, compiler => {
19
+ shimmer.wrap(compiler, 'compile', wrapCompile)
20
+ shimmer.wrap(compiler, 'compileClientWithDependenciesTracked', wrapCompile)
21
+
22
+ return compiler
23
+ })
@@ -112,7 +112,6 @@ function createWrapRouterMethod (name) {
112
112
  path: pattern instanceof RegExp ? `(${pattern})` : pattern,
113
113
  test: layer => {
114
114
  const matchers = layerMatchers.get(layer)
115
-
116
115
  return !isFastStar(layer, matchers) &&
117
116
  !isFastSlash(layer, matchers) &&
118
117
  cachedPathToRegExp(pattern).test(layer.path)
@@ -121,7 +120,7 @@ function createWrapRouterMethod (name) {
121
120
  }
122
121
 
123
122
  function isFastStar (layer, matchers) {
124
- if (layer.regexp.fast_star !== undefined) {
123
+ if (layer.regexp?.fast_star !== undefined) {
125
124
  return layer.regexp.fast_star
126
125
  }
127
126
 
@@ -129,7 +128,7 @@ function createWrapRouterMethod (name) {
129
128
  }
130
129
 
131
130
  function isFastSlash (layer, matchers) {
132
- if (layer.regexp.fast_slash !== undefined) {
131
+ if (layer.regexp?.fast_slash !== undefined) {
133
132
  return layer.regexp.fast_slash
134
133
  }
135
134
 
@@ -93,6 +93,7 @@ class BaseAwsSdkPlugin extends ClientPlugin {
93
93
  this.responseExtractDSMContext(operation, params, response.data ?? response, span)
94
94
  }
95
95
  this.addResponseTags(span, response)
96
+ this.addSpanPointers(span, response)
96
97
  this.finish(span, response, response.error)
97
98
  })
98
99
  }
@@ -101,6 +102,10 @@ class BaseAwsSdkPlugin extends ClientPlugin {
101
102
  // implemented by subclasses, or not
102
103
  }
103
104
 
105
+ addSpanPointers (span, response) {
106
+ // Optionally implemented by subclasses, for services where we're unable to inject trace context
107
+ }
108
+
104
109
  operationFromRequest (request) {
105
110
  // can be overriden by subclasses
106
111
  return this.operationName({
@@ -113,14 +113,15 @@ class Kinesis extends BaseAwsSdkPlugin {
113
113
  response.Records.forEach(record => {
114
114
  const parsedAttributes = JSON.parse(Buffer.from(record.Data).toString())
115
115
 
116
- if (
117
- parsedAttributes?._datadog && streamName
118
- ) {
119
- const payloadSize = getSizeOrZero(record.Data)
116
+ const payloadSize = getSizeOrZero(record.Data)
117
+ if (parsedAttributes?._datadog) {
120
118
  this.tracer.decodeDataStreamsContext(parsedAttributes._datadog)
121
- this.tracer
122
- .setCheckpoint(['direction:in', `topic:${streamName}`, 'type:kinesis'], span, payloadSize)
123
119
  }
120
+ const tags = streamName
121
+ ? ['direction:in', `topic:${streamName}`, 'type:kinesis']
122
+ : ['direction:in', 'type:kinesis']
123
+ this.tracer
124
+ .setCheckpoint(tags, span, payloadSize)
124
125
  })
125
126
  }
126
127