dd-trace 3.9.0 → 3.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -15,7 +15,7 @@ export declare interface Tracer extends opentracing.Tracer {
15
15
  * @param {SpanOptions} [options] Options for the newly created span.
16
16
  * @returns {Span} A new Span object.
17
17
  */
18
- startSpan(name: string, options?: SpanOptions): Span;
18
+ startSpan (name: string, options?: SpanOptions): Span;
19
19
 
20
20
  /**
21
21
  * Injects the given SpanContext instance for cross-process propagation
@@ -27,7 +27,7 @@ export declare interface Tracer extends opentracing.Tracer {
27
27
  * @param {string} format The format of the carrier.
28
28
  * @param {any} carrier The carrier object.
29
29
  */
30
- inject(spanContext: SpanContext | Span, format: string, carrier: any): void;
30
+ inject (spanContext: SpanContext | Span, format: string, carrier: any): void;
31
31
 
32
32
  /**
33
33
  * Returns a SpanContext instance extracted from `carrier` in the given
@@ -38,31 +38,31 @@ export declare interface Tracer extends opentracing.Tracer {
38
38
  * The extracted SpanContext, or null if no such SpanContext could
39
39
  * be found in `carrier`
40
40
  */
41
- extract(format: string, carrier: any): SpanContext | null;
41
+ extract (format: string, carrier: any): SpanContext | null;
42
42
 
43
43
  /**
44
44
  * Initializes the tracer. This should be called before importing other libraries.
45
45
  */
46
- init(options?: TracerOptions): this;
46
+ init (options?: TracerOptions): this;
47
47
 
48
48
  /**
49
49
  * Sets the URL for the trace agent. This should only be called _after_
50
50
  * init() is called, only in cases where the URL needs to be set after
51
51
  * initialization.
52
52
  */
53
- setUrl(url: string): this;
53
+ setUrl (url: string): this;
54
54
 
55
55
  /**
56
56
  * Enable and optionally configure a plugin.
57
57
  * @param plugin The name of a built-in plugin.
58
58
  * @param config Configuration options. Can also be `false` to disable the plugin.
59
59
  */
60
- use<P extends keyof Plugins>(plugin: P, config?: Plugins[P] | boolean): this;
60
+ use<P extends keyof Plugins> (plugin: P, config?: Plugins[P] | boolean): this;
61
61
 
62
62
  /**
63
63
  * Returns a reference to the current scope.
64
64
  */
65
- scope(): Scope;
65
+ scope (): Scope;
66
66
 
67
67
  /**
68
68
  * Instruments a function by automatically creating a span activated on its
@@ -81,8 +81,8 @@ export declare interface Tracer extends opentracing.Tracer {
81
81
  * If the `orphanable` option is set to false, the function will not be traced
82
82
  * unless there is already an active span or `childOf` option.
83
83
  */
84
- trace<T>(name: string, fn: (span?: Span, fn?: (error?: Error) => any) => T): T;
85
- trace<T>(name: string, options: TraceOptions & SpanOptions, fn: (span?: Span, done?: (error?: Error) => string) => T): T;
84
+ trace<T> (name: string, fn: (span?: Span, fn?: (error?: Error) => any) => T): T;
85
+ trace<T> (name: string, options: TraceOptions & SpanOptions, fn: (span?: Span, done?: (error?: Error) => string) => T): T;
86
86
 
87
87
  /**
88
88
  * Wrap a function to automatically create a span activated on its
@@ -98,23 +98,23 @@ export declare interface Tracer extends opentracing.Tracer {
98
98
  * * The function doesn't accept a callback and doesn't return a promise, in
99
99
  * which case the span will finish at the end of the function execution.
100
100
  */
101
- wrap<T = (...args: any[]) => any>(name: string, fn: T, requiresParent?: boolean): T;
102
- wrap<T = (...args: any[]) => any>(name: string, options: TraceOptions & SpanOptions, fn: T): T;
103
- wrap<T = (...args: any[]) => any>(name: string, options: (...args: any[]) => TraceOptions & SpanOptions, fn: T): T;
101
+ wrap<T = (...args: any[]) => any> (name: string, fn: T, requiresParent?: boolean): T;
102
+ wrap<T = (...args: any[]) => any> (name: string, options: TraceOptions & SpanOptions, fn: T): T;
103
+ wrap<T = (...args: any[]) => any> (name: string, options: (...args: any[]) => TraceOptions & SpanOptions, fn: T): T;
104
104
 
105
105
  /**
106
106
  * Create and return a string that can be included in the <head> of a
107
107
  * document to enable RUM tracing to include it. The resulting string
108
108
  * should not be cached.
109
109
  */
110
- getRumData(): string;
110
+ getRumData (): string;
111
111
 
112
112
  /**
113
113
  * Links an authenticated user to the current trace.
114
114
  * @param {User} user Properties of the authenticated user. Accepts custom fields.
115
115
  * @returns {Tracer} The Tracer instance for chaining.
116
116
  */
117
- setUser(user: User): Tracer;
117
+ setUser (user: User): Tracer;
118
118
  }
119
119
 
120
120
  export declare interface TraceOptions extends Analyzable {
@@ -144,7 +144,7 @@ export declare interface TraceOptions extends Analyzable {
144
144
  * have children.
145
145
  */
146
146
  export declare interface Span extends opentracing.Span {
147
- context(): SpanContext;
147
+ context (): SpanContext;
148
148
  }
149
149
 
150
150
  /**
@@ -161,17 +161,17 @@ export declare interface SpanContext extends opentracing.SpanContext {
161
161
  /**
162
162
  * Returns the string representation of the internal trace ID.
163
163
  */
164
- toTraceId(): string;
164
+ toTraceId (): string;
165
165
 
166
166
  /**
167
167
  * Returns the string representation of the internal span ID.
168
168
  */
169
- toSpanId(): string;
169
+ toSpanId (): string;
170
170
 
171
171
  /**
172
172
  * Returns the string representation used for DBM integration.
173
173
  */
174
- toTraceparent(): string;
174
+ toTraceparent (): string;
175
175
  }
176
176
 
177
177
  /**
@@ -329,7 +329,7 @@ export declare interface TracerOptions {
329
329
  * Number of spans before partially exporting a trace. This prevents keeping all the spans in memory for very large traces.
330
330
  * @default 1000
331
331
  */
332
- flushMinSpans?: number;
332
+ flushMinSpans?: number;
333
333
 
334
334
  /**
335
335
  * Whether to enable runtime metrics.
@@ -382,10 +382,10 @@ export declare interface TracerOptions {
382
382
  runtimeId?: boolean
383
383
 
384
384
  /**
385
- * Whether to write traces to log output, rather than send to an agent
385
+ * Whether to write traces to log output or agentless, rather than send to an agent
386
386
  * @default false
387
387
  */
388
- exporter?: 'log' | 'agent'
388
+ exporter?: 'log' | 'agent' | 'datadog'
389
389
 
390
390
  /**
391
391
  * Whether to enable the experimental `getRumData` method.
@@ -396,7 +396,7 @@ export declare interface TracerOptions {
396
396
  /**
397
397
  * Configuration of the IAST. Can be a boolean as an alias to `iast.enabled`.
398
398
  */
399
- iast?: boolean | {
399
+ iast?: boolean | {
400
400
  /**
401
401
  * Whether to enable IAST.
402
402
  * @default false
@@ -516,7 +516,7 @@ export declare interface TracerOptions {
516
516
  /**
517
517
  * User object that can be passed to `tracer.setUser()`.
518
518
  */
519
- export declare interface User {
519
+ export declare interface User {
520
520
  /**
521
521
  * Unique identifier of the user.
522
522
  * Mandatory.
@@ -579,7 +579,7 @@ export declare interface Scope {
579
579
  *
580
580
  * @returns {Span} The active span.
581
581
  */
582
- active(): Span | null;
582
+ active (): Span | null;
583
583
 
584
584
  /**
585
585
  * Activate a span in the scope of a function.
@@ -588,7 +588,7 @@ export declare interface Scope {
588
588
  * @param {Function} fn Function that will have the span activated on its scope.
589
589
  * @returns The return value of the provided function.
590
590
  */
591
- activate<T>(span: Span, fn: ((...args: any[]) => T)): T;
591
+ activate<T> (span: Span, fn: ((...args: any[]) => T)): T;
592
592
 
593
593
  /**
594
594
  * Binds a target to the provided span, or the active span if omitted.
@@ -597,9 +597,9 @@ export declare interface Scope {
597
597
  * @param {Span} [span=scope.active()] The span to activate.
598
598
  * @returns The bound target.
599
599
  */
600
- bind<T extends (...args: any[]) => void>(fn: T, span?: Span | null): T;
601
- bind<V, T extends (...args: any[]) => V>(fn: T, span?: Span | null): T;
602
- bind<T>(fn: Promise<T>, span?: Span | null): Promise<T>;
600
+ bind<T extends (...args: any[]) => void> (fn: T, span?: Span | null): T;
601
+ bind<V, T extends (...args: any[]) => V> (fn: T, span?: Span | null): T;
602
+ bind<T> (fn: Promise<T>, span?: Span | null): Promise<T>;
603
603
  }
604
604
 
605
605
  /** @hidden */
@@ -1212,7 +1212,7 @@ declare namespace plugins {
1212
1212
  * This plugin automatically instruments the
1213
1213
  * [mariadb](https://github.com/mariadb-corporation/mariadb-connector-nodejs) module.
1214
1214
  */
1215
- interface mariadb extends mysql {}
1215
+ interface mariadb extends mysql {}
1216
1216
 
1217
1217
  /**
1218
1218
  * This plugin automatically instruments the
@@ -1236,7 +1236,7 @@ declare namespace plugins {
1236
1236
  * This plugin automatically instruments the
1237
1237
  * [moleculer](https://moleculer.services/) module.
1238
1238
  */
1239
- interface moleculer extends Moleculer {
1239
+ interface moleculer extends Moleculer {
1240
1240
  /**
1241
1241
  * Configuration for Moleculer clients. Set to false to disable client
1242
1242
  * instrumentation.
@@ -1290,7 +1290,7 @@ declare namespace plugins {
1290
1290
  /**
1291
1291
  * Hooks to run before spans are finished.
1292
1292
  */
1293
- hooks?: {
1293
+ hooks?: {
1294
1294
  /**
1295
1295
  * Hook to execute just before the request span finishes.
1296
1296
  */
@@ -1319,7 +1319,7 @@ declare namespace plugins {
1319
1319
  * This plugin automatically instruments the
1320
1320
  * [paperplane](https://github.com/articulate/paperplane) module.
1321
1321
  */
1322
- interface paperplane extends HttpServer {}
1322
+ interface paperplane extends HttpServer {}
1323
1323
 
1324
1324
  /**
1325
1325
  * This plugin automatically instruments the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "3.9.0",
3
+ "version": "3.9.2",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -1,12 +1,13 @@
1
1
  'use strict'
2
2
 
3
3
  const shimmer = require('../../datadog-shimmer')
4
- const { addHook, channel, AsyncResource } = require('./helpers/instrument')
4
+ const { addHook, channel } = require('./helpers/instrument')
5
5
 
6
6
  const enterChannel = channel('apm:connect:middleware:enter')
7
7
  const exitChannel = channel('apm:connect:middleware:exit')
8
8
  const errorChannel = channel('apm:connect:middleware:error')
9
9
  const nextChannel = channel('apm:connect:middleware:next')
10
+ const finishChannel = channel('apm:connect:middleware:finish')
10
11
  const handleChannel = channel('apm:connect:request:handle')
11
12
 
12
13
  function wrapConnect (connect) {
@@ -61,7 +62,6 @@ function wrapLayerHandle (layer) {
61
62
  return shimmer.wrap(original, function () {
62
63
  if (!enterChannel.hasSubscribers) return original.apply(this, arguments)
63
64
 
64
- const middlewareResource = new AsyncResource('bound-anonymous-fn')
65
65
  const lastIndex = arguments.length - 1
66
66
  const name = original._name || original.name
67
67
  const req = arguments[arguments.length > 3 ? 1 : 0]
@@ -71,21 +71,21 @@ function wrapLayerHandle (layer) {
71
71
  arguments[lastIndex] = wrapNext(req, next)
72
72
  }
73
73
 
74
- return middlewareResource.runInAsyncScope(() => {
75
- const route = layer.route
74
+ const route = layer.route
76
75
 
77
- enterChannel.publish({ name, req, route })
76
+ enterChannel.publish({ name, req, route })
78
77
 
79
- try {
80
- return original.apply(this, arguments)
81
- } catch (error) {
82
- errorChannel.publish({ req, error })
83
- nextChannel.publish({ req })
84
- exitChannel.publish({ req })
78
+ try {
79
+ return original.apply(this, arguments)
80
+ } catch (error) {
81
+ errorChannel.publish({ req, error })
82
+ nextChannel.publish({ req })
83
+ finishChannel.publish({ req })
85
84
 
86
- throw error
87
- }
88
- })
85
+ throw error
86
+ } finally {
87
+ exitChannel.publish({ req })
88
+ }
89
89
  })
90
90
  }
91
91
 
@@ -96,7 +96,7 @@ function wrapNext (req, next) {
96
96
  }
97
97
 
98
98
  nextChannel.publish({ req })
99
- exitChannel.publish({ req })
99
+ finishChannel.publish({ req })
100
100
 
101
101
  next.apply(this, arguments)
102
102
  }
@@ -2,12 +2,12 @@
2
2
 
3
3
  const {
4
4
  channel,
5
- addHook,
6
- AsyncResource
5
+ addHook
7
6
  } = require('../helpers/instrument')
8
7
  const shimmer = require('../../../datadog-shimmer')
9
8
 
10
9
  const startServerCh = channel('apm:http:server:request:start')
10
+ const exitServerCh = channel('apm:http:server:request:exit')
11
11
  const errorServerCh = channel('apm:http:server:request:error')
12
12
  const finishServerCh = channel('apm:http:server:request:finish')
13
13
 
@@ -45,18 +45,17 @@ function wrapEmit (emit) {
45
45
  if (eventName === 'request') {
46
46
  res.req = req
47
47
 
48
- const asyncResource = new AsyncResource('bound-anonymous-fn')
49
- return asyncResource.runInAsyncScope(() => {
50
- startServerCh.publish({ req, res })
48
+ startServerCh.publish({ req, res })
51
49
 
52
- try {
53
- return emit.apply(this, arguments)
54
- } catch (err) {
55
- errorServerCh.publish(err)
50
+ try {
51
+ return emit.apply(this, arguments)
52
+ } catch (err) {
53
+ errorServerCh.publish(err)
56
54
 
57
- throw err
58
- }
59
- })
55
+ throw err
56
+ } finally {
57
+ exitServerCh.publish({ req })
58
+ }
60
59
  }
61
60
  return emit.apply(this, arguments)
62
61
  }
@@ -1,12 +1,13 @@
1
1
  'use strict'
2
2
 
3
3
  const shimmer = require('../../datadog-shimmer')
4
- const { addHook, channel, AsyncResource } = require('./helpers/instrument')
4
+ const { addHook, channel } = require('./helpers/instrument')
5
5
 
6
6
  const enterChannel = channel('apm:koa:middleware:enter')
7
7
  const exitChannel = channel('apm:koa:middleware:exit')
8
8
  const errorChannel = channel('apm:koa:middleware:error')
9
9
  const nextChannel = channel('apm:koa:middleware:next')
10
+ const finishChannel = channel('apm:koa:middleware:finish')
10
11
  const handleChannel = channel('apm:koa:request:handle')
11
12
  const routeChannel = channel('apm:koa:request:route')
12
13
 
@@ -86,42 +87,41 @@ function wrapMiddleware (fn, layer) {
86
87
  return function (ctx, next) {
87
88
  if (!ctx || !enterChannel.hasSubscribers) return fn.apply(this, arguments)
88
89
 
89
- const middlewareResource = new AsyncResource('bound-anonymous-fn')
90
90
  const req = ctx.req
91
91
 
92
- return middlewareResource.runInAsyncScope(() => {
93
- const path = layer && layer.path
94
- const route = typeof path === 'string' && !path.endsWith('(.*)') && !path.endsWith('([^/]*)') && path
92
+ const path = layer && layer.path
93
+ const route = typeof path === 'string' && !path.endsWith('(.*)') && !path.endsWith('([^/]*)') && path
95
94
 
96
- enterChannel.publish({ req, name, route })
95
+ enterChannel.publish({ req, name, route })
97
96
 
98
- if (typeof next === 'function') {
99
- arguments[1] = wrapNext(req, next)
100
- }
97
+ if (typeof next === 'function') {
98
+ arguments[1] = wrapNext(req, next)
99
+ }
101
100
 
102
- try {
103
- const result = fn.apply(this, arguments)
104
-
105
- if (result && typeof result.then === 'function') {
106
- return result.then(
107
- result => {
108
- fulfill(ctx)
109
- return result
110
- },
111
- err => {
112
- fulfill(ctx, err)
113
- throw err
114
- }
115
- )
116
- } else {
117
- fulfill(ctx)
118
- return result
119
- }
120
- } catch (e) {
121
- fulfill(ctx, e)
122
- throw e
101
+ try {
102
+ const result = fn.apply(this, arguments)
103
+
104
+ if (result && typeof result.then === 'function') {
105
+ return result.then(
106
+ result => {
107
+ fulfill(ctx)
108
+ return result
109
+ },
110
+ err => {
111
+ fulfill(ctx, err)
112
+ throw err
113
+ }
114
+ )
115
+ } else {
116
+ fulfill(ctx)
117
+ return result
123
118
  }
124
- })
119
+ } catch (e) {
120
+ fulfill(ctx, e)
121
+ throw e
122
+ } finally {
123
+ exitChannel.publish({ req })
124
+ }
125
125
  }
126
126
  }
127
127
 
@@ -138,7 +138,7 @@ function fulfill (ctx, error) {
138
138
  routeChannel.publish({ req, route })
139
139
  }
140
140
 
141
- exitChannel.publish({ req })
141
+ finishChannel.publish({ req })
142
142
  }
143
143
 
144
144
  function wrapNext (req, next) {
@@ -9,6 +9,7 @@ const handleChannel = channel('apm:restify:request:handle')
9
9
  const errorChannel = channel('apm:restify:middleware:error')
10
10
  const enterChannel = channel('apm:restify:middleware:enter')
11
11
  const exitChannel = channel('apm:restify:middleware:exit')
12
+ const finishChannel = channel('apm:restify:middleware:finish')
12
13
  const nextChannel = channel('apm:restify:middleware:next')
13
14
 
14
15
  function wrapSetupRequest (setupRequest) {
@@ -53,8 +54,10 @@ function wrapFn (fn) {
53
54
  } catch (error) {
54
55
  errorChannel.publish({ req, error })
55
56
  nextChannel.publish({ req })
56
- exitChannel.publish({ req })
57
+ finishChannel.publish({ req })
57
58
  throw error
59
+ } finally {
60
+ exitChannel.publish({ req })
58
61
  }
59
62
  }
60
63
  }
@@ -62,7 +65,7 @@ function wrapFn (fn) {
62
65
  function wrapNext (req, next) {
63
66
  return function () {
64
67
  nextChannel.publish({ req })
65
- exitChannel.publish({ req })
68
+ finishChannel.publish({ req })
66
69
 
67
70
  next.apply(this, arguments)
68
71
  }
@@ -3,11 +3,12 @@
3
3
  const METHODS = require('methods').concat('all')
4
4
  const pathToRegExp = require('path-to-regexp')
5
5
  const shimmer = require('../../datadog-shimmer')
6
- const { addHook, channel, AsyncResource } = require('./helpers/instrument')
6
+ const { addHook, channel } = require('./helpers/instrument')
7
7
 
8
8
  function createWrapRouterMethod (name) {
9
9
  const enterChannel = channel(`apm:${name}:middleware:enter`)
10
10
  const exitChannel = channel(`apm:${name}:middleware:exit`)
11
+ const finishChannel = channel(`apm:${name}:middleware:finish`)
11
12
  const errorChannel = channel(`apm:${name}:middleware:error`)
12
13
  const nextChannel = channel(`apm:${name}:middleware:next`)
13
14
 
@@ -21,7 +22,6 @@ function createWrapRouterMethod (name) {
21
22
  if (!enterChannel.hasSubscribers) return original.apply(this, arguments)
22
23
 
23
24
  const matchers = layerMatchers.get(layer)
24
- const middlewareResource = new AsyncResource('bound-anonymous-fn')
25
25
  const lastIndex = arguments.length - 1
26
26
  const name = original._name || original.name
27
27
  const req = arguments[arguments.length > 3 ? 1 : 0]
@@ -31,32 +31,32 @@ function createWrapRouterMethod (name) {
31
31
  arguments[lastIndex] = wrapNext(req, next)
32
32
  }
33
33
 
34
- return middlewareResource.runInAsyncScope(() => {
35
- let route
34
+ let route
36
35
 
37
- if (matchers) {
38
- // Try to guess which path actually matched
39
- for (let i = 0; i < matchers.length; i++) {
40
- if (matchers[i].test(layer)) {
41
- route = matchers[i].path
36
+ if (matchers) {
37
+ // Try to guess which path actually matched
38
+ for (let i = 0; i < matchers.length; i++) {
39
+ if (matchers[i].test(layer)) {
40
+ route = matchers[i].path
42
41
 
43
- break
44
- }
42
+ break
45
43
  }
46
44
  }
45
+ }
47
46
 
48
- enterChannel.publish({ name, req, route })
47
+ enterChannel.publish({ name, req, route })
49
48
 
50
- try {
51
- return original.apply(this, arguments)
52
- } catch (error) {
53
- errorChannel.publish({ req, error })
54
- nextChannel.publish({ req })
55
- exitChannel.publish({ req })
49
+ try {
50
+ return original.apply(this, arguments)
51
+ } catch (error) {
52
+ errorChannel.publish({ req, error })
53
+ nextChannel.publish({ req })
54
+ finishChannel.publish({ req })
56
55
 
57
- throw error
58
- }
59
- })
56
+ throw error
57
+ } finally {
58
+ exitChannel.publish({ req })
59
+ }
60
60
  })
61
61
 
62
62
  // This is a workaround for the `loopback` library so that it can find the correct express layer
@@ -95,7 +95,7 @@ function createWrapRouterMethod (name) {
95
95
  }
96
96
 
97
97
  nextChannel.publish({ req })
98
- exitChannel.publish({ req })
98
+ finishChannel.publish({ req })
99
99
 
100
100
  next.apply(this, arguments)
101
101
  }
@@ -14,12 +14,15 @@ class HttpServerPlugin extends Plugin {
14
14
  constructor (...args) {
15
15
  super(...args)
16
16
 
17
+ this._parentStore = undefined
18
+
17
19
  this.addSub('apm:http:server:request:start', ({ req, res }) => {
18
20
  const store = storage.getStore()
19
21
  const span = web.startSpan(this.tracer, this.config, req, res, 'web.request')
20
22
 
21
23
  span.setTag(COMPONENT, this.constructor.name)
22
24
 
25
+ this._parentStore = store
23
26
  this.enter(span, { ...store, req })
24
27
 
25
28
  const context = web.getContext(req)
@@ -38,6 +41,11 @@ class HttpServerPlugin extends Plugin {
38
41
  web.addError(error)
39
42
  })
40
43
 
44
+ this.addSub('apm:http:server:request:exit', ({ req }) => {
45
+ this.enter(this._parentStore)
46
+ this._parentStore = undefined
47
+ })
48
+
41
49
  this.addSub('apm:http:server:request:finish', ({ req }) => {
42
50
  const context = web.getContext(req)
43
51
 
@@ -14,6 +14,7 @@ class RouterPlugin extends WebPlugin {
14
14
  constructor (...args) {
15
15
  super(...args)
16
16
 
17
+ this._storeStack = []
17
18
  this._contexts = new WeakMap()
18
19
 
19
20
  this.addSub(`apm:${this.constructor.name}:middleware:enter`, ({ req, name, route }) => {
@@ -28,6 +29,7 @@ class RouterPlugin extends WebPlugin {
28
29
  context.middleware.push(span)
29
30
  }
30
31
 
32
+ this._storeStack.push(storage.getStore())
31
33
  this.enter(span)
32
34
 
33
35
  web.patch(req)
@@ -42,7 +44,7 @@ class RouterPlugin extends WebPlugin {
42
44
  context.stack.pop()
43
45
  })
44
46
 
45
- this.addSub(`apm:${this.constructor.name}:middleware:exit`, ({ req }) => {
47
+ this.addSub(`apm:${this.constructor.name}:middleware:finish`, ({ req }) => {
46
48
  const context = this._contexts.get(req)
47
49
 
48
50
  if (!context || context.middleware.length === 0) return
@@ -50,6 +52,10 @@ class RouterPlugin extends WebPlugin {
50
52
  context.middleware.pop().finish()
51
53
  })
52
54
 
55
+ this.addSub(`apm:${this.constructor.name}:middleware:exit`, ({ req }) => {
56
+ this.enter(this._storeStack.pop())
57
+ })
58
+
53
59
  this.addSub(`apm:${this.constructor.name}:middleware:error`, ({ req, error }) => {
54
60
  web.addError(req, error)
55
61
 
@@ -33,6 +33,7 @@ class Config {
33
33
  const service = options.service || DD_SERVICE || 'node'
34
34
  const host = os.hostname()
35
35
  const version = coalesce(options.version, DD_VERSION)
36
+ const functionname = process.env.AWS_LAMBDA_FUNCTION_NAME
36
37
  // Must be longer than one minute so pad with five seconds
37
38
  const flushInterval = coalesce(options.interval, 65 * 1000)
38
39
  const uploadTimeout = coalesce(options.uploadTimeout,
@@ -46,12 +47,13 @@ class Config {
46
47
  this.service = service
47
48
  this.env = env
48
49
  this.host = host
50
+ this.functionname = functionname
49
51
 
50
52
  this.version = version
51
53
  this.tags = Object.assign(
52
54
  tagger.parse(DD_TAGS),
53
55
  tagger.parse(options.tags),
54
- tagger.parse({ env, host, service, version })
56
+ tagger.parse({ env, host, service, version, functionname })
55
57
  )
56
58
  this.logger = ensureLogger(options.logger)
57
59
  this.flushInterval = flushInterval
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { Profiler } = require('./profiler')
3
+ const { Profiler, ServerlessProfiler } = require('./profiler')
4
4
  const CpuProfiler = require('./profilers/cpu')
5
5
  const WallProfiler = require('./profilers/wall')
6
6
  const SpaceProfiler = require('./profilers/space')
@@ -8,7 +8,7 @@ const { AgentExporter } = require('./exporters/agent')
8
8
  const { FileExporter } = require('./exporters/file')
9
9
  const { ConsoleLogger } = require('./loggers/console')
10
10
 
11
- const profiler = new Profiler()
11
+ const profiler = process.env.AWS_LAMBDA_FUNCTION_NAME ? new ServerlessProfiler() : new Profiler()
12
12
 
13
13
  module.exports = {
14
14
  profiler,
@@ -5,7 +5,6 @@ const { Config } = require('./config')
5
5
 
6
6
  function maybeSourceMap (sourceMap) {
7
7
  if (!sourceMap) return
8
-
9
8
  const { SourceMapper } = require('@datadog/pprof')
10
9
  return SourceMapper.create([
11
10
  process.cwd()
@@ -20,6 +19,7 @@ class Profiler extends EventEmitter {
20
19
  this._config = undefined
21
20
  this._timer = undefined
22
21
  this._lastStart = undefined
22
+ this._timeoutInterval = undefined
23
23
  }
24
24
 
25
25
  start (options) {
@@ -35,6 +35,7 @@ class Profiler extends EventEmitter {
35
35
 
36
36
  this._logger = config.logger
37
37
  this._enabled = true
38
+ this._setInterval()
38
39
 
39
40
  // Log errors if the source map finder fails, but don't prevent the rest
40
41
  // of the profiler from running without source maps.
@@ -52,13 +53,17 @@ class Profiler extends EventEmitter {
52
53
  this._logger.debug(`Started ${profiler.type} profiler`)
53
54
  }
54
55
 
55
- this._capture(config.flushInterval)
56
+ this._capture(this._timeoutInterval)
56
57
  } catch (e) {
57
58
  this._logger.error(e)
58
59
  this.stop()
59
60
  }
60
61
  }
61
62
 
63
+ _setInterval () {
64
+ this._timeoutInterval = this._config.flushInterval
65
+ }
66
+
62
67
  stop () {
63
68
  if (!this._enabled) return
64
69
 
@@ -78,8 +83,7 @@ class Profiler extends EventEmitter {
78
83
  _capture (timeout) {
79
84
  if (!this._enabled) return
80
85
  this._lastStart = new Date()
81
-
82
- if (!this._timer || timeout !== this._config.flushInterval) {
86
+ if (!this._timer || timeout !== this._timeoutInterval) {
83
87
  this._timer = setTimeout(() => this._collect(), timeout)
84
88
  this._timer.unref()
85
89
  } else {
@@ -106,7 +110,7 @@ class Profiler extends EventEmitter {
106
110
  })
107
111
  }
108
112
 
109
- this._capture(this._config.flushInterval)
113
+ this._capture(this._timeoutInterval)
110
114
  await this._submit(profiles, start, end)
111
115
  this._logger.debug('Submitted profiles')
112
116
  } catch (err) {
@@ -133,4 +137,29 @@ class Profiler extends EventEmitter {
133
137
  }
134
138
  }
135
139
 
136
- module.exports = { Profiler }
140
+ class ServerlessProfiler extends Profiler {
141
+ constructor () {
142
+ super()
143
+ this._profiledIntervals = 0
144
+ this._interval = 1
145
+ this._flushAfterIntervals = undefined
146
+ }
147
+
148
+ _setInterval () {
149
+ this._timeoutInterval = this._interval * 1000
150
+ this._flushAfterIntervals = this._config.flushInterval / 1000
151
+ }
152
+
153
+ async _collect () {
154
+ if (this._profiledIntervals >= this._flushAfterIntervals) {
155
+ this._profiledIntervals = 0
156
+ await super._collect()
157
+ } else {
158
+ this._profiledIntervals += 1
159
+ this._capture(this._timeoutInterval)
160
+ // Don't submit profile until 65 (flushAfterIntervals) intervals have elapsed
161
+ }
162
+ }
163
+ }
164
+
165
+ module.exports = { Profiler, ServerlessProfiler }