newrelic 4.4.0 → 4.7.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.
package/NEWS.md CHANGED
@@ -1,3 +1,83 @@
1
+ ### 4.7.0 (2018-07-31):
2
+
3
+ * Added support for distributed tracing.
4
+
5
+ Distributed tracing lets you see the path that a request takes as it travels
6
+ through your distributed system. By showing the distributed activity through a
7
+ unified view, you can troubleshoot and understand a complex system better than
8
+ ever before.
9
+
10
+ Distributed tracing is available with an APM Pro or equivalent subscription.
11
+ To see a complete distributed trace, you need to enable the feature on a set of
12
+ neighboring services. Enabling distributed tracing changes the behavior of some
13
+ New Relic features, so carefully consult the [transition guide](https://docs.newrelic.com/docs/transition-guide-distributed-tracing) before
14
+ you enable this feature.
15
+
16
+ To enable distributed tracing, set `distributed_tracing.enabled` to `true` in
17
+ your `newrelic.js` file, or set `NEW_RELIC_DISTRIBUTED_TRACING_ENABLED` in your
18
+ environment.
19
+
20
+ * Added a warning for too-new versions of Node.js during agent startup.
21
+
22
+ * Appropriately obfuscated SQL statements will now be included in all transaction
23
+ traces.
24
+
25
+ Previously, the agent would only include the SQL statements if the corresponding
26
+ query was sufficiently slow.
27
+
28
+ * Added ability to execute instrumentation functions in the context of the segment
29
+ the segment descriptor is describing.
30
+
31
+ All `record*` methods supplied by all instrumentation shim classes now allow for
32
+ a function to be executed under the context of the segment the record call will
33
+ produce. This may be done by supplying a function in the `inContext` key for the
34
+ segment descriptor passed to the record method.
35
+
36
+ * Reservoirs will now respect setting their size to 0.
37
+
38
+ ### 4.6.0 (2018-07-24):
39
+
40
+ * Added full support for Node v10.
41
+
42
+ * Added instrumentation for `crypto.scrypt`.
43
+
44
+ * Added instrumentation for `fs.realpath.native`.
45
+
46
+ * Added instrumentation for `process.setUncaughtExceptionCaptureCallback`.
47
+
48
+ * Updated tests to use `asyncResource.runInAsyncScope` instead of `emitBefore` and
49
+ `emitAfter`
50
+
51
+ * Pulled `distributed_tracing` config value from behind `feature_flag`.
52
+
53
+ ### 4.5.1 (2018-07-18):
54
+
55
+ - The agent will now properly remerge event data on collection failure.
56
+
57
+ Previously, the agent wouldn't observe the correct format for remerging, causing
58
+ undefined events to be pushed into the reservoir.
59
+
60
+ ### 4.5.0 (2018-07-16):
61
+
62
+ * Feature flags may now be set from environment variables.
63
+
64
+ Using the naming convention `NEW_RELIC_FEATURE_FLAG_<feature flag name in upper
65
+ case>`.
66
+
67
+ * Transaction events may be harvested in two payloads now.
68
+
69
+ This change reduces the occurrence of harvests being rejected due to large
70
+ payloads. Payloads will only be split when they are large (greater than 1/3 the
71
+ maximum).
72
+
73
+ * Updated Hapi v17 instrumentation to wrap `server` export, in addition to `Server`.
74
+
75
+ * `ROOT` segment no longer turns into a span event.
76
+
77
+ * Fixed span collection when transactions are `sampled=false`.
78
+
79
+ * Removed `grandparentId` from spans.
80
+
1
81
  ### 4.4.0 (2018-07-12):
2
82
 
3
83
  * Added config `utilization` env vars to the `BOOLEAN_VARS` set.
@@ -9,6 +89,29 @@
9
89
 
10
90
  * Added node v10 to the test matrix.
11
91
 
92
+ * Converted distributed trace `x-newrelic-trace` header name to `newrelic`.
93
+
94
+ * Added support for different transport types in distributed tracing.
95
+
96
+ * Added more tests around priority/sampled attributes on traces and events.
97
+
98
+ * Lazily calculate transaction priority only when needed.
99
+
100
+ * Transaction priority is now truncated to 6 decimal places on generation.
101
+
102
+ * Adaptive sampling now uses the `sampling_target` and
103
+ `sampling_target_period_in_seconds` configuration values.
104
+
105
+ With these configurations, the adaptive sampling window is separated from the
106
+ harvest window.
107
+
108
+ * Removed `nr.tripId` attribute from distributed trace intrinsics.
109
+
110
+ * Default span events to enabled.
111
+
112
+ These are still protected behind `feature_flag.distributed_tracing` which defaults
113
+ to `false`.
114
+
12
115
  ### 4.3.0 (2018-07-09):
13
116
 
14
117
  * Added `nonce` option for `newrelic.getBrowserTimingHeader()`
@@ -30,7 +133,7 @@
30
133
 
31
134
  * Fixed issue with tracking external requests to default ports.
32
135
 
33
- Special thanks to Ryan King for pinpointing the cause of this issue.
136
+ Special thanks to Ryan King for pinpointing the cause of this issue.
34
137
 
35
138
  * Added extra check for handling arrays of functions when wrapping middleware
36
139
  mounters.
@@ -68,6 +171,25 @@
68
171
 
69
172
  * No longer download gcc on test suites that do not require it.
70
173
 
174
+ * Added `url` parameter to `http` external segments.
175
+
176
+ * Renamed request parameters on external segments.
177
+
178
+ Previously these were named just the parameter name (e.g. `/foo?bar=baz` would
179
+ become the parameter `"bar": "baz"`). Now they are prefixed with
180
+ `request.parameter`. (e.g. `"request.parameter.bar": "baz"`).
181
+
182
+ * Added `EventAggregator` base class.
183
+
184
+ The `ErrorAggregator` class was refactored and most generic event aggregation
185
+ logic was moved to the new `EventAggregator` class.
186
+
187
+ * Added `SpanEvent` and `SpanAggregator` classes.
188
+
189
+ * Added Span event generation to the trace `end` method.
190
+
191
+ * Added Span events to harvest cycle steps.
192
+
71
193
  ### 4.1.5 (2018-06-11):
72
194
 
73
195
  * Make `require()` statements explicitly reference `package.json` as a `.json` file.
@@ -110,6 +232,11 @@
110
232
  result in a false `uninstrumented` status, because the agent would interpret
111
233
  `redis.js` as the module itself.
112
234
 
235
+ * Moved `computeSampled` call to `Transaction` constructor.
236
+
237
+ Previously it was only called in `createDistributedTracePayload`, but this
238
+ gives all transactions a `sampled` value, and potentially a boosted priority.
239
+
113
240
  ### 4.1.4 (2018-06-04):
114
241
 
115
242
  * Transaction stubs are now created properly in `api#getTransaction`
@@ -133,6 +260,25 @@
133
260
 
134
261
  * Modularlized configuration constants to improve readability.
135
262
 
263
+ * Added `distributed_tracing` feature flag.
264
+
265
+ * Added `acceptDistributedTracePayload` method to `Transaction`.
266
+
267
+ * Added `createDistributedTracePayload` method to `Transaction`.
268
+
269
+ * Updated `Agent#recordSupportability` to not include `Nodejs/` in the default metric name.
270
+
271
+ * Added distributed tracing methods to `TransactionHandle`.
272
+
273
+ * Added distributed tracing cases for `http` and `other` metric recorders.
274
+
275
+ * Implemented `_addDistributedTraceInstrinsics` on `Transaction`.
276
+
277
+ If the `distributed_tracing` feature flag is enabled, the agent will ignore old
278
+ CAT attributes in favor of distributed trace–related ones.
279
+
280
+ * Added integration tests around better CAT functionality.
281
+
136
282
  ### 4.1.2 (2018-05-22):
137
283
 
138
284
  * Fixed access to properties on promisified methods.
package/api.js CHANGED
@@ -702,6 +702,13 @@ function createTracer(name, callback) {
702
702
  return arity.fixArity(callback, tracer.bindFunction(callback, segment, true))
703
703
  }
704
704
 
705
+ /**
706
+ * @callback startSegmentCallback
707
+ * @param {function} cb
708
+ * The function to time with the created segment.
709
+ * @return {Promise=} Returns a promise if cb returns a promise.
710
+ */
711
+
705
712
  /**
706
713
  * Wraps the given handler in a segment which may optionally be turned into a
707
714
  * metric.
@@ -720,7 +727,7 @@ function createTracer(name, callback) {
720
727
  * up on the transaction breakdown table and server breakdown graph. Segments
721
728
  * just show up in transaction traces.
722
729
  *
723
- * @param {function(cb) -> ?Promise} handler
730
+ * @param {startSegmentCallback} handler
724
731
  * The function to track as a segment.
725
732
  *
726
733
  * @param {function} [callback]
package/index.js CHANGED
@@ -1,21 +1,22 @@
1
1
  'use strict'
2
2
 
3
3
  // Record opening times before loading any other files.
4
- var preAgentTime = process.uptime()
5
- var agentStart = Date.now()
4
+ const preAgentTime = process.uptime()
5
+ const agentStart = Date.now()
6
6
 
7
7
  // Load unwrapped core now to ensure it gets the freshest properties.
8
8
  require('./lib/util/unwrapped-core')
9
9
 
10
- var featureFlags = require('./lib/feature_flags').prerelease
11
- var logger = require('./lib/logger')
12
- var psemver = require('./lib/util/process-version')
10
+ const featureFlags = require('./lib/feature_flags').prerelease
11
+ const psemver = require('./lib/util/process-version')
12
+ let logger = require('./lib/logger') // Gets re-loaded after initialization.
13
13
 
14
14
 
15
- var agentVersion = require('./package.json').version
15
+ const pkgJSON = require('./package.json')
16
16
  logger.info(
17
- "Using New Relic for Node.js. Agent version: %s; Node version: %s.",
18
- agentVersion, process.version
17
+ 'Using New Relic for Node.js. Agent version: %s; Node version: %s.',
18
+ pkgJSON.version,
19
+ process.version
19
20
  )
20
21
 
21
22
  if (require.cache.__NR_cache) {
@@ -49,6 +50,13 @@ function initialize() {
49
50
 
50
51
  logger.error(message)
51
52
  throw new Error(message)
53
+ } else if (!psemver.satisfies(pkgJSON.engines.node)) {
54
+ logger.warn(
55
+ 'New Relic for Node.js %s has not been tested on Node.js %s. Please ' +
56
+ 'update the agent or downgrade your version of Node.js',
57
+ pkgJSON.version,
58
+ process.version
59
+ )
52
60
  }
53
61
 
54
62
  logger.debug("Current working directory at module load is %s.", process.cwd())
package/lib/agent.js CHANGED
@@ -516,7 +516,7 @@ function _addIntrinsicAttrsFromTransaction(transaction) {
516
516
 
517
517
  // If CAT is enabled, extract the data gleened from the CAT headers.
518
518
  if (this.config.cross_application_tracer.enabled) {
519
- if (this.config.feature_flag.distributed_tracing) {
519
+ if (this.config.distributed_tracing.enabled) {
520
520
  transaction.addDistributedTraceIntrinsics(intrinsicAttributes)
521
521
  if (transaction.parentSpanId) {
522
522
  intrinsicAttributes.parentSpanId = transaction.parentSpanId
@@ -677,6 +677,12 @@ Agent.prototype._listenForConfigChanges = function _listenForConfigChanges() {
677
677
  self.transactionSampler.samplingPeriod = period * 1000
678
678
  }
679
679
  )
680
+ this.config.on(
681
+ 'transaction_events.max_samples_per_minute',
682
+ function updateEventSampleLimit(maxSamples) {
683
+ self.events.setLimit(maxSamples)
684
+ }
685
+ )
680
686
  }
681
687
 
682
688
  module.exports = Agent
@@ -791,6 +791,23 @@ exports.config = {
791
791
  enabled: true
792
792
  },
793
793
 
794
+ /**
795
+ * Controls the method of cross agent tracing in the agent.
796
+ * Distributed tracing lets you see the path that a request takes through your
797
+ * distributed system. Enabling distributed tracing changes the behavior of some
798
+ * New Relic features, so carefully consult the transition guide before you enable
799
+ * this feature: https://docs.newrelic.com/docs/transition-guide-distributed-tracing
800
+ * Default is false.
801
+ */
802
+ distributed_tracing: {
803
+ /**
804
+ * Enables/disables distributed tracing.
805
+ *
806
+ * @env NEW_RELIC_DISTRIBUTED_TRACING_ENABLED
807
+ */
808
+ enabled: false
809
+ },
810
+
794
811
  /**
795
812
  * Controls the use of cross-application tracing.
796
813
  *
package/lib/config/env.js CHANGED
@@ -114,6 +114,9 @@ const ENV_MAPPING = {
114
114
  span_events: {
115
115
  enabled: 'NEW_RELIC_SPAN_EVENTS_ENABLED'
116
116
  },
117
+ distributed_tracing: {
118
+ enabled: 'NEW_RELIC_DISTRIBUTED_TRACING_ENABLED'
119
+ },
117
120
  message_tracer: {
118
121
  segment_parameters: {
119
122
  enabled: 'NEW_RELIC_MESSAGE_TRACER_SEGMENT_PARAMETERS_ENABLED'
@@ -167,6 +170,7 @@ const BOOLEAN_VARS = new Set([
167
170
  'NEW_RELIC_HIGH_SECURITY',
168
171
  'NEW_RELIC_SLOW_SQL_ENABLED',
169
172
  'NEW_RELIC_SPAN_EVENTS_ENABLED',
173
+ 'NEW_RELIC_DISTRIBUTED_TRACING_ENABLED',
170
174
  'NEW_RELIC_LOG_ENABLED',
171
175
  'NEW_RELIC_AUDIT_LOG_ENABLED',
172
176
  'NEW_RELIC_DATASTORE_DATABASE_NAME_REPORTING_ENABLED',
@@ -21,11 +21,11 @@ var logger = null // Lazy-loaded in `initialize`.
21
21
  /**
22
22
  * CONSTANTS -- we gotta lotta 'em
23
23
  */
24
- var DEFAULT_CONFIG_PATH = require.resolve('./default')
25
- var DEFAULT_CONFIG = require('./default').config
26
- var DEFAULT_FILENAME = 'newrelic.js'
27
- var AZURE_APP_NAME = 'APP_POOL_ID'
28
- var CONFIG_FILE_LOCATIONS = [
24
+ const DEFAULT_CONFIG_PATH = require.resolve('./default')
25
+ const DEFAULT_CONFIG = require('./default').config
26
+ const DEFAULT_FILENAME = 'newrelic.js'
27
+ const AZURE_APP_NAME = 'APP_POOL_ID'
28
+ const CONFIG_FILE_LOCATIONS = [
29
29
  process.env.NEW_RELIC_HOME,
30
30
  process.cwd(),
31
31
  process.env.HOME,
@@ -159,6 +159,7 @@ function Config(config) {
159
159
  this._fromSpecial()
160
160
 
161
161
  // 4. override config with environment variables
162
+ this._featureFlagsFromEnv()
162
163
  this._fromEnvironment()
163
164
 
164
165
  // 5. clean up anything that requires postprocessing
@@ -847,6 +848,21 @@ Config.prototype._fromSpecial = function _fromSpecial() {
847
848
  }
848
849
  }
849
850
 
851
+ /**
852
+ * Iterate over all feature flags and check for the corresponding environment variable
853
+ * (of the form NEW_RELIC_FEATURE_FLAG_<feature flag name in upper case>).
854
+ */
855
+ Config.prototype._featureFlagsFromEnv = function _featureFlagsFromEnv() {
856
+ const flags = Object.keys(feature_flag.prerelease).concat(feature_flag.released)
857
+ const config = this
858
+ flags.forEach(function checkFlag(flag) {
859
+ const envVal = process.env['NEW_RELIC_FEATURE_FLAG_' + flag.toUpperCase()]
860
+ if (envVal) {
861
+ config.feature_flag[flag] = isTruthular(envVal)
862
+ }
863
+ })
864
+ }
865
+
850
866
  /**
851
867
  * Recursively visit the nodes of the constant containing the mapping between
852
868
  * environment variable names, overriding any configuration values that are
package/lib/db/tracer.js CHANGED
@@ -46,27 +46,36 @@ QueryTracer.prototype.merge = function merge(tracer) {
46
46
 
47
47
  QueryTracer.prototype.addQuery = function addQuery(segment, type, query, trace) {
48
48
  const ttConfig = this.config.transaction_tracer
49
- if (segment.getDurationInMillis() < ttConfig.explain_threshold) {
50
- return
51
- }
52
- const slowQuery = new SlowQuery(segment, type, query, trace)
53
49
 
50
+ // If DT is enabled and the segment is part of a sampled transaction
51
+ // (i.e. we are creating a span event for this segment), then we need
52
+ // to collect the sql trace.
53
+ var slowQuery
54
54
  switch (ttConfig.record_sql) {
55
55
  case 'raw':
56
+ slowQuery = new SlowQuery(segment, type, query, trace)
56
57
  logger.trace('recording raw sql')
57
58
  segment.parameters.sql = slowQuery.query
58
59
  break
59
60
  case 'obfuscated':
61
+ slowQuery = new SlowQuery(segment, type, query, trace)
60
62
  logger.trace('recording obfuscated sql')
61
63
  segment.parameters.sql_obfuscated = slowQuery.obfuscated
62
64
  break
63
65
  default:
64
66
  logger.trace(
65
- 'not collecting slow-query because transaction_tracer.record_sql was set to %s',
67
+ 'not recording sql statement, transaction_tracer.record_sql was set to %s',
66
68
  ttConfig.record_sql
67
69
  )
68
70
  return
69
71
  }
72
+
73
+ if (segment.getDurationInMillis() < ttConfig.explain_threshold) {
74
+ return
75
+ }
76
+
77
+ slowQuery = slowQuery || new SlowQuery(segment, type, query, trace)
78
+
70
79
  segment.parameters.backtrace = slowQuery.trace
71
80
 
72
81
  if (!this.config.slow_sql.enabled) {
@@ -158,7 +167,7 @@ QuerySample.prototype.getParams = function getParams() {
158
167
  params.database_name = segmentParams.database_name
159
168
  }
160
169
 
161
- if (this.tracer.config.feature_flag.distributed_tracing) {
170
+ if (this.tracer.config.distributed_tracing.enabled) {
162
171
  this.trace.segment.transaction.addDistributedTraceIntrinsics(params)
163
172
  }
164
173
 
@@ -305,7 +305,7 @@ function getGlobalPackages(cb) {
305
305
  * package appears at most once, with all the versions joined into a
306
306
  * comma-delimited list.
307
307
  *
308
- * @return {Array.<String>[]} Sorted list of [name, version] pairs.
308
+ * @return {Array.<string[]>} Sorted list of [name, version] pairs.
309
309
  */
310
310
  function flattenVersions(packages) {
311
311
  var info = Object.create(null)
@@ -170,7 +170,7 @@ function _getErrorEventIntrinsicAttrs(transaction, errorClass, message, timestam
170
170
  attributes["nr.syntheticsMonitorId"] = transaction.syntheticsData.monitorId
171
171
  }
172
172
 
173
- if (transaction.agent.config.feature_flag.distributed_tracing) {
173
+ if (transaction.agent.config.distributed_tracing.enabled) {
174
174
  transaction.addDistributedTraceIntrinsics(attributes)
175
175
  } else {
176
176
  attributes['nr.referringTransactionGuid'] = transaction.referringTransactionGuid
@@ -5,7 +5,6 @@ exports.prerelease = {
5
5
  custom_instrumentation: true,
6
6
  custom_metrics: true,
7
7
  express5: false,
8
- distributed_tracing: false,
9
8
  await_support: true,
10
9
  synthetics: true,
11
10
  native_metrics: true,
package/lib/harvest.js CHANGED
@@ -17,7 +17,7 @@ class Harvest {
17
17
  this._errorEventsQueue = agent.errors.getQueue()
18
18
  this._errors = agent.errors.getErrors()
19
19
  this._traces = [].concat(agent.traces.syntheticsTraces)
20
- this._events = agent.events
20
+ this._events = this._splitPayload(agent.events)
21
21
  this._queries = agent.queries
22
22
  this._spanEventsQueue = agent.spans.getQueue()
23
23
 
@@ -74,7 +74,13 @@ class Harvest {
74
74
  }
75
75
  }
76
76
  if (this._events) {
77
- this.agent.events.merge(this._events)
77
+ for (let i = 0; i < this._events.length; ++i) {
78
+ if (this._events[i]) {
79
+ this.agent.events.merge(
80
+ this._events[i].toMerge
81
+ )
82
+ }
83
+ }
78
84
  }
79
85
  if (this._customEvents) {
80
86
  this.agent.customEvents.merge(this._customEvents)
@@ -203,22 +209,24 @@ class Harvest {
203
209
  }
204
210
 
205
211
  _prepareEvents() {
212
+ const eventAggr = this.agent.events
213
+
206
214
  // Create the metrics so they are at least set to 0
207
215
  const discarded = this.agent.metrics.getOrCreateMetric(NAMES.EVENTS.DISCARDED)
208
216
  const seen = this.agent.metrics.getOrCreateMetric(NAMES.EVENTS.SEEN)
209
217
  const sent = this.agent.metrics.getOrCreateMetric(NAMES.EVENTS.SENT)
210
218
 
211
- seen.incrementCallCount(this._events.seen)
212
- sent.incrementCallCount(this._events.sent)
219
+ seen.incrementCallCount(eventAggr.seen)
220
+ sent.incrementCallCount(eventAggr.sent)
213
221
 
214
- // if we had to limit events and sample them, emit a warning
215
- const diff = this._events.overflow()
222
+ // If we had to limit events and sample them, emit a warning
223
+ const diff = eventAggr.overflow()
216
224
  discarded.incrementCallCount(diff)
217
225
  if (diff > 0) {
218
226
  logger.warn(
219
227
  'analytics event overflow, dropped %d events; try increasing your limit above %d',
220
228
  diff,
221
- this._events.limit
229
+ eventAggr.limit
222
230
  )
223
231
  }
224
232
  }
@@ -351,24 +359,20 @@ class Harvest {
351
359
  return setImmediate(callback)
352
360
  }
353
361
 
354
- const metrics = {
355
- reservoir_size: this._events.limit,
356
- events_seen: this._events.seen
357
- }
358
- const payload = [
359
- this.agent.config.run_id,
360
- metrics,
361
- this._events.toArray()
362
- ]
363
-
364
- // Send data to collector
365
362
  const self = this
366
363
  const agent = this.agent
367
- agent.collector.analyticsEvents(payload, function onAnalyticsEvents(err) {
368
- if (self._shouldClearData(err, 'events')) {
364
+ a.eachOfSeries(this._events, function sendEachPayload(obj, i, cb) {
365
+ const payload = obj.payload
366
+ agent.collector.analyticsEvents(payload, function onAnalyticsEvents(err) {
367
+ if (self._shouldClearData(err, 'events')) {
368
+ self._events[i] = null
369
+ }
370
+ cb(err)
371
+ })
372
+ }, function afterAllSent(err) {
373
+ if (!err) {
369
374
  self._events = null
370
375
  }
371
-
372
376
  callback(err)
373
377
  })
374
378
  }
@@ -468,7 +472,7 @@ class Harvest {
468
472
 
469
473
  _sendSpanEvents(callback) {
470
474
  const config = this.agent.config
471
- if (!config.span_events.enabled || !config.feature_flag.distributed_tracing) {
475
+ if (!config.span_events.enabled || !config.distributed_tracing.enabled) {
472
476
  logger.debug('Span events collection disabled.')
473
477
  this._spanEventsQueues = null
474
478
  return setImmediate(callback)
@@ -511,6 +515,51 @@ class Harvest {
511
515
  logger.warn('Failed to send %j; re-sampling.', name)
512
516
  return false
513
517
  }
518
+
519
+ _splitPayload(queue) {
520
+ // If we're less than 1/3 full, don't bother splitting the payload.
521
+ if (queue.length === 0) {
522
+ return []
523
+ }
524
+ if (queue.length < queue.limit / 3) {
525
+ return [{
526
+ toMerge: queue,
527
+ payload: [
528
+ this.agent.config.run_id,
529
+ {reservoir_size: queue.limit, events_seen: queue.seen},
530
+ queue.toArray()
531
+ ]
532
+ }]
533
+ }
534
+
535
+ // Our payload is large, so split it in half.
536
+ // TODO: update this to pull the priority off the event when DT is released
537
+ const events = queue.getRawEvents()
538
+ const size = Math.floor(queue.length / 2)
539
+ const limit = Math.floor(queue.limit / 2)
540
+ const seen = Math.floor(queue.seen / 2)
541
+ const firstHalf = events.splice(0, size)
542
+
543
+ return [{
544
+ toMerge: firstHalf,
545
+ payload: [
546
+ this.agent.config.run_id,
547
+ {reservoir_size: limit, events_seen: seen},
548
+ firstHalf.map(rawEventsToValues)
549
+ ]
550
+ }, {
551
+ toMerge: events,
552
+ payload: [
553
+ this.agent.config.run_id,
554
+ {reservoir_size: queue.limit - limit, events_seen: queue.seen - seen},
555
+ events.map(rawEventsToValues)
556
+ ]
557
+ }]
558
+
559
+ function rawEventsToValues(ev) {
560
+ return ev.value
561
+ }
562
+ }
514
563
  }
515
564
 
516
565
  module.exports = Harvest
@@ -12,7 +12,8 @@ function initialize(agent, crypto) {
12
12
  'pbkdf2',
13
13
  'randomBytes',
14
14
  'pseudoRandomBytes',
15
- 'randomFill'
15
+ 'randomFill',
16
+ 'scrypt'
16
17
  ],
17
18
  wrapCryptoMethod
18
19
  )
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
- var record = require('../../metrics/recorders/generic.js')
4
- var NAMES = require('../../metrics/names.js')
3
+ var record = require('../../metrics/recorders/generic')
4
+ var NAMES = require('../../metrics/names')
5
5
  var wrap = require('../../shimmer').wrapMethod
6
6
 
7
7
  module.exports = initialize
@@ -46,6 +46,7 @@ function initialize(agent, fs) {
46
46
  ]
47
47
 
48
48
  wrap(fs, 'fs', methods, segment)
49
+ wrap(fs.realpath, 'fs.realpath', 'native', wrapRealPathNative)
49
50
  wrap(fs, 'fs', uninstrumented, agent.tracer.wrapFunctionNoSegment.bind(agent.tracer))
50
51
  wrap(fs, 'fs', ['watch'], wrapWatch)
51
52
  wrap(fs, 'fs', ['watchFile'], wrapWatchFile)
@@ -54,6 +55,10 @@ function initialize(agent, fs) {
54
55
  return agent.tracer.wrapFunctionLast(NAMES.FS.PREFIX + method, record, fn)
55
56
  }
56
57
 
58
+ function wrapRealPathNative(fn) {
59
+ return segment(fn, 'realpath.native')
60
+ }
61
+
57
62
  function wrapWatch(fn) {
58
63
  return function wrappedWatch() {
59
64
  var args = agent.tracer.slice(arguments)
@@ -8,6 +8,8 @@ module.exports = initialize
8
8
 
9
9
 
10
10
  function initialize(agent) {
11
+ let exceptionCallbackRegistered = false
12
+
11
13
  // `_fatalException` is an undocumented feature of domains, introduced in
12
14
  // Node.js v0.8. We use `_fatalException` because wrapping it will not
13
15
  // potentially change the behavior of the server unlike listening for
@@ -16,7 +18,7 @@ function initialize(agent) {
16
18
  return function wrappedFatalException(error) {
17
19
  // Only record the error if we are not currently within an instrumented
18
20
  // domain.
19
- if (!process.domain) {
21
+ if (!process.domain && !exceptionCallbackRegistered) {
20
22
  agent.errors.add(null, error)
21
23
  agent.tracer.segment = null
22
24
  }
@@ -45,6 +47,20 @@ function initialize(agent) {
45
47
  }
46
48
  }
47
49
 
50
+ shimmer.wrapMethod(
51
+ process,
52
+ 'process',
53
+ 'setUncaughtExceptionCaptureCallback',
54
+ wrapUncaughtExceptionCallback
55
+ )
56
+
57
+ function wrapUncaughtExceptionCallback(original) {
58
+ return function wrapped(fn) {
59
+ exceptionCallbackRegistered = fn !== null
60
+ return original.apply(this, arguments)
61
+ }
62
+ }
63
+
48
64
  // This will initialize the most optimal native-promise instrumentation that
49
65
  // we have available.
50
66
  asyncHooks(agent)