newrelic 4.5.0 → 4.8.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/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
@@ -76,7 +76,9 @@ class Harvest {
76
76
  if (this._events) {
77
77
  for (let i = 0; i < this._events.length; ++i) {
78
78
  if (this._events[i]) {
79
- this.agent.events.merge(this._events[i][2])
79
+ this.agent.events.merge(
80
+ this._events[i].toMerge
81
+ )
80
82
  }
81
83
  }
82
84
  }
@@ -359,7 +361,8 @@ class Harvest {
359
361
 
360
362
  const self = this
361
363
  const agent = this.agent
362
- a.eachOfSeries(this._events, function sendEachPayload(payload, i, cb) {
364
+ a.eachOfSeries(this._events, function sendEachPayload(obj, i, cb) {
365
+ const payload = obj.payload
363
366
  agent.collector.analyticsEvents(payload, function onAnalyticsEvents(err) {
364
367
  if (self._shouldClearData(err, 'events')) {
365
368
  self._events[i] = null
@@ -469,7 +472,7 @@ class Harvest {
469
472
 
470
473
  _sendSpanEvents(callback) {
471
474
  const config = this.agent.config
472
- if (!config.span_events.enabled || !config.feature_flag.distributed_tracing) {
475
+ if (!config.span_events.enabled || !config.distributed_tracing.enabled) {
473
476
  logger.debug('Span events collection disabled.')
474
477
  this._spanEventsQueues = null
475
478
  return setImmediate(callback)
@@ -519,28 +522,43 @@ class Harvest {
519
522
  return []
520
523
  }
521
524
  if (queue.length < queue.limit / 3) {
522
- return [[
523
- this.agent.config.run_id,
524
- {reservoir_size: queue.limit, events_seen: queue.seen},
525
- queue.toArray()
526
- ]]
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
+ }]
527
533
  }
528
534
 
529
535
  // Our payload is large, so split it in half.
530
- const events = queue.toArray()
536
+ // TODO: update this to pull the priority off the event when DT is released
537
+ const events = queue.getRawEvents()
531
538
  const size = Math.floor(queue.length / 2)
532
539
  const limit = Math.floor(queue.limit / 2)
533
540
  const seen = Math.floor(queue.seen / 2)
541
+ const firstHalf = events.splice(0, size)
534
542
 
535
- return [[
536
- this.agent.config.run_id,
537
- {reservoir_size: limit, events_seen: seen},
538
- events.splice(0, size)
539
- ], [
540
- this.agent.config.run_id,
541
- {reservoir_size: queue.limit - limit, events_seen: queue.seen - seen},
542
- events
543
- ]]
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
+ }
544
562
  }
545
563
  }
546
564
 
@@ -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)
@@ -6,11 +6,19 @@ var urltils = require('../../util/urltils')
6
6
  var hashes = require('../../util/hashes')
7
7
  var logger = require('../../logger').child({component: 'outbound'})
8
8
  var shimmer = require('../../shimmer')
9
+ var util = require('util')
10
+ var url = require('url')
11
+ var copy = require('../../util/copy')
9
12
 
10
13
  const DEFAULT_HOST = 'localhost'
11
14
  const DEFAULT_PORT = 80
12
15
  const DEFAULT_SSL_PORT = 443
13
16
 
17
+ const NEWRELIC_ID_HEADER = 'x-newrelic-id'
18
+ const NEWRELIC_TRACE_HEADER = 'newrelic'
19
+ const NEWRELIC_TRANSACTION_HEADER = 'x-newrelic-transaction'
20
+ const NEWRELIC_SYNTHETICS_HEADER = 'x-newrelic-synthetics'
21
+
14
22
  /**
15
23
  * Instruments an outbound HTTP request.
16
24
  *
@@ -21,6 +29,12 @@ const DEFAULT_SSL_PORT = 443
21
29
  * @return {http.ClientRequest} The instrumented outbound request.
22
30
  */
23
31
  module.exports = function instrumentOutbound(agent, opts, makeRequest) {
32
+ if (typeof opts === 'string') {
33
+ opts = url.parse(opts)
34
+ } else {
35
+ opts = copy.shallow(opts)
36
+ }
37
+
24
38
  let hostname = opts.hostname || opts.host || DEFAULT_HOST
25
39
  let port = opts.port || opts.defaultPort
26
40
  if (!port) {
@@ -32,7 +46,7 @@ module.exports = function instrumentOutbound(agent, opts, makeRequest) {
32
46
  'Invalid host name (%s) or port (%s) for outbound request.',
33
47
  hostname, port
34
48
  )
35
- return makeRequest()
49
+ return makeRequest(opts)
36
50
  }
37
51
 
38
52
  // Technically we shouldn't append the port if this is an https request on 443
@@ -54,8 +68,48 @@ module.exports = function instrumentOutbound(agent, opts, makeRequest) {
54
68
  )
55
69
 
56
70
  function instrumentRequest(segment) {
71
+ const transaction = segment.transaction
72
+ const outboundHeaders = Object.create(null)
73
+
74
+ // FLAG: synthetics
75
+ if (
76
+ agent.config.feature_flag.synthetics &&
77
+ agent.config.encoding_key &&
78
+ transaction.syntheticsHeader
79
+ ) {
80
+ outboundHeaders[NEWRELIC_SYNTHETICS_HEADER] = transaction.syntheticsHeader
81
+ }
82
+
83
+ // If CAT is enabled, inject the transaction header.
84
+ // TODO: abstract header logic shared with TransactionShim#insertCATRequestHeaders
85
+ if (agent.config.cross_application_tracer.enabled) {
86
+ if (agent.config.distributed_tracing.enabled) {
87
+ _addDistributedHeaders(transaction, outboundHeaders)
88
+ } else if (agent.config.encoding_key) {
89
+ _addCATHeaders(agent, transaction, outboundHeaders)
90
+ } else {
91
+ logger.trace('No encoding key found, not adding CAT headers')
92
+ }
93
+ }
94
+
95
+ if (util.isArray(opts.headers)) {
96
+ opts.headers = opts.headers.slice()
97
+ Array.prototype.push.apply(
98
+ opts.headers,
99
+ Object.keys(outboundHeaders).map(function getHeaderTuples(key) {
100
+ return [key, outboundHeaders[key]]
101
+ })
102
+ )
103
+ } else {
104
+ opts.headers = Object.assign(
105
+ Object.create(null),
106
+ opts.headers,
107
+ outboundHeaders
108
+ )
109
+ }
110
+
57
111
  segment.start()
58
- const request = makeRequest()
112
+ const request = makeRequest(opts)
59
113
  const parsed = urltils.scrubAndParseParameters(request.path)
60
114
  const proto = parsed.protocol || opts.protocol || 'http:'
61
115
  segment.name += parsed.path
@@ -129,7 +183,7 @@ function handleResponse(segment, hostname, req, res) {
129
183
  const agent = segment.transaction.agent
130
184
  if (
131
185
  agent.config.cross_application_tracer.enabled &&
132
- !agent.config.feature_flag.distributed_tracing
186
+ !agent.config.distributed_tracing.enabled
133
187
  ) {
134
188
  pullCatHeaders(agent.config, segment, hostname, res.headers['x-newrelic-app-data'])
135
189
  }
@@ -202,3 +256,48 @@ function _makeNonEnumerable(obj, prop) {
202
256
  logger.debug(e, 'Failed to make %s non enumerable.', prop)
203
257
  }
204
258
  }
259
+
260
+ function _addDistributedHeaders(tx, outboundHeaders) {
261
+ try {
262
+ const txData = tx.createDistributedTracePayload().httpSafe()
263
+ outboundHeaders[NEWRELIC_TRACE_HEADER] = txData
264
+
265
+ logger.trace(
266
+ 'Added outbound request distributed tracing headers in transaction %s',
267
+ tx.id
268
+ )
269
+ } catch (err) {
270
+ logger.trace(err, 'Failed to create distributed trace payload')
271
+ }
272
+ }
273
+
274
+ function _addCATHeaders(agent, tx, outboundHeaders) {
275
+ if (agent.config.obfuscatedId) {
276
+ outboundHeaders[NEWRELIC_ID_HEADER] = agent.config.obfuscatedId
277
+ }
278
+
279
+ var pathHash = hashes.calculatePathHash(
280
+ agent.config.applications()[0],
281
+ tx.getFullName() || '',
282
+ tx.referringPathHash
283
+ )
284
+ tx.pushPathHash(pathHash)
285
+
286
+ try {
287
+ let txData = JSON.stringify([
288
+ tx.id,
289
+ false,
290
+ tx.tripId || tx.id,
291
+ pathHash
292
+ ])
293
+ txData = hashes.obfuscateNameUsingKey(txData, agent.config.encoding_key)
294
+ outboundHeaders[NEWRELIC_TRANSACTION_HEADER] = txData
295
+
296
+ logger.trace(
297
+ 'Added outbound request CAT headers in transaction %s',
298
+ tx.id
299
+ )
300
+ } catch (err) {
301
+ logger.trace(err, 'Failed to create CAT payload')
302
+ }
303
+ }
@@ -11,7 +11,6 @@ var url = require('url')
11
11
  var urltils = require('../../util/urltils')
12
12
  var properties = require('../../util/properties')
13
13
  var psemver = require('../../util/process-version')
14
- var copy = require('../../util/copy')
15
14
 
16
15
  const NAMES = require('../../metrics/names')
17
16
  const DESTS = require('../../config/attribute-filter').DESTINATIONS
@@ -206,7 +205,7 @@ function wrapEmitWithTransaction(agent, emit, isHTTPS) {
206
205
  }
207
206
  }
208
207
  if (agent.config.cross_application_tracer.enabled) {
209
- if (agent.config.feature_flag.distributed_tracing) {
208
+ if (agent.config.distributed_tracing.enabled) {
210
209
  const payload = request.headers[NEWRELIC_TRACE_HEADER]
211
210
  if (payload) {
212
211
  logger.trace(
@@ -499,54 +498,10 @@ function wrapRequest(agent, request) {
499
498
 
500
499
  const args = agent.tracer.slice(arguments)
501
500
  const context = this
502
- const outboundHeaders = Object.create(null)
503
-
504
- // FLAG: synthetics
505
- if (
506
- agent.config.feature_flag.synthetics &&
507
- agent.config.encoding_key &&
508
- transaction.syntheticsHeader
509
- ) {
510
- outboundHeaders[NEWRELIC_SYNTHETICS_HEADER] = transaction.syntheticsHeader
511
- }
512
-
513
- // If CAT is enabled, inject the transaction header.
514
- // TODO: abstract header logic shared with TransactionShim#insertCATRequestHeaders
515
- if (agent.config.cross_application_tracer.enabled) {
516
- if (agent.config.feature_flag.distributed_tracing) {
517
- _addDistributedHeaders(transaction, outboundHeaders)
518
- } else if (agent.config.encoding_key) {
519
- _addCATHeaders(agent, transaction, outboundHeaders)
520
- } else {
521
- logger.trace('No encoding key found, not adding CAT headers')
522
- }
523
- }
524
-
525
- if (typeof options === 'string') {
526
- options = url.parse(options)
527
- } else {
528
- options = copy.shallow(options)
529
- }
530
-
531
- if (util.isArray(options.headers)) {
532
- options.headers = options.headers.slice()
533
- Array.prototype.push.apply(
534
- options.headers,
535
- Object.keys(outboundHeaders).map(function getHeaderTuples(key) {
536
- return [key, outboundHeaders[key]]
537
- })
538
- )
539
- } else {
540
- options.headers = Object.assign(
541
- Object.create(null),
542
- options.headers,
543
- outboundHeaders
544
- )
545
- }
546
- args[0] = options
547
501
 
548
502
  // hostname & port logic pulled directly from node's 0.10 lib/http.js
549
- return instrumentOutbound(agent, options, function makeRequest() {
503
+ return instrumentOutbound(agent, options, function makeRequest(opts) {
504
+ args[0] = opts
550
505
  return request.apply(context, args)
551
506
  })
552
507
  }
@@ -738,7 +693,7 @@ module.exports = function initialize(agent, http, moduleName) {
738
693
  *
739
694
  * @param {string} header - The raw X-NewRelic-Synthetics header
740
695
  * @param {string} encKey - Encoding key handed down from the server
741
- * @param {̄Array} trustedIds - Array of accounts to trust the header from.
696
+ * @param {Array.<number>} trustedIds - Array of accounts to trust the header from.
742
697
  * @param {Transaction} transaction - Where the synthetics data is attached to.
743
698
  */
744
699
  function handleSyntheticsHeader(header, encKey, trustedIds, transaction) {
@@ -756,8 +711,8 @@ function handleSyntheticsHeader(header, encKey, trustedIds, transaction) {
756
711
  *
757
712
  * @param {string} header - The raw X-NewRelic-Synthetics header
758
713
  * @param {string} encKey - Encoding key handed down from the server
759
- * @param {̄Array} trustedIds - Array of accounts to trust the header from.
760
- * @return {Object or null} - On successful parse and verification an object of
714
+ * @param {Array.<number>} trustedIds - Array of accounts to trust the header from.
715
+ * @return {Object|null} - On successful parse and verification an object of
761
716
  * synthetics data is returned, otherwise null is
762
717
  * returned.
763
718
  */
@@ -877,48 +832,3 @@ function _collectHeaders(headers, nameMap, prefix, tx) {
877
832
  }
878
833
  }
879
834
  }
880
-
881
- function _addDistributedHeaders(tx, outboundHeaders) {
882
- try {
883
- const txData = tx.createDistributedTracePayload().httpSafe()
884
- outboundHeaders[NEWRELIC_TRACE_HEADER] = txData
885
-
886
- logger.trace(
887
- 'Added outbound request distributed tracing headers in transaction %s',
888
- tx.id
889
- )
890
- } catch (err) {
891
- logger.trace(err, 'Failed to create distributed trace payload')
892
- }
893
- }
894
-
895
- function _addCATHeaders(agent, tx, outboundHeaders) {
896
- if (agent.config.obfuscatedId) {
897
- outboundHeaders[NEWRELIC_ID_HEADER] = agent.config.obfuscatedId
898
- }
899
-
900
- var pathHash = hashes.calculatePathHash(
901
- agent.config.applications()[0],
902
- tx.getFullName() || '',
903
- tx.referringPathHash
904
- )
905
- tx.pushPathHash(pathHash)
906
-
907
- try {
908
- let txData = JSON.stringify([
909
- tx.id,
910
- false,
911
- tx.tripId || tx.id,
912
- pathHash
913
- ])
914
- txData = hashes.obfuscateNameUsingKey(txData, agent.config.encoding_key)
915
- outboundHeaders[NEWRELIC_TRANSACTION_HEADER] = txData
916
-
917
- logger.trace(
918
- 'Added outbound request CAT headers in transaction %s',
919
- tx.id
920
- )
921
- } catch (err) {
922
- logger.trace(err, 'Failed to create CAT payload')
923
- }
924
- }