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.
@@ -7,6 +7,12 @@ const DT_VERSION_MAJOR = 0
7
7
  const DT_VERSION_MINOR = 1
8
8
 
9
9
  module.exports = class DistributedTracePayload {
10
+ /**
11
+ * The class reponsible for producing distributed trace payloads.
12
+ * Created by calling {@link TransactionHandle#createDistributedTracePayload}.
13
+ *
14
+ * @constructor
15
+ */
10
16
  constructor(payload) {
11
17
  logger.trace('DistributedTracePayload created with %s', payload)
12
18
  this.plainTextPayload = JSON.stringify({
@@ -16,11 +22,21 @@ module.exports = class DistributedTracePayload {
16
22
  this.base64Payload = null
17
23
  }
18
24
 
25
+ /**
26
+ * @returns {String} The base64 encoded JSON representation of the
27
+ * distributed trace payload.
28
+ */
19
29
  text() {
20
30
  logger.trace('DistributedTracePayload text: %s', this.plainTextPayload)
21
31
  return this.plainTextPayload
22
32
  }
23
33
 
34
+ /**
35
+ * Construct a payload suitable for HTTP transport.
36
+ *
37
+ * @returns {String} The base64 encoded JSON representation of the
38
+ * distributed trace payload.
39
+ */
24
40
  httpSafe() {
25
41
  if (!this.base64Payload) {
26
42
  this.base64Payload = makeBuffer(this.plainTextPayload, 'utf-8').toString('base64')
@@ -33,7 +33,7 @@ module.exports = class TransactionHandle {
33
33
  *
34
34
  * Proxy method for Transaction#createDistrubtedTracePayload.
35
35
  *
36
- * @returns {DistributedTracePayload} The created payload.
36
+ * @returns {DistributedTracePayload} The created payload object.
37
37
  *
38
38
  */
39
39
  createDistributedTracePayload() {
@@ -122,6 +122,7 @@ function Transaction(agent) {
122
122
  this.traceId = null
123
123
  this.parentSpanId = null
124
124
  this.isDistributedTrace = null
125
+ this.acceptedDistributedTrace = null
125
126
 
126
127
  // Lazy evaluate the priority and sampling in case we end up accepting a payload.
127
128
  this.priority = null
@@ -192,6 +193,10 @@ Transaction.prototype.end = function end(done) {
192
193
  transaction.record()
193
194
  }
194
195
 
196
+ // This is function currently must be called after all recorders have been fired due
197
+ // to some of the recorders (namely the db recorders) add parameters to the segments.
198
+ transaction.trace.generateSpanEvents()
199
+
195
200
  transaction.agent.emit('transactionFinished', transaction)
196
201
  if (typeof done === 'function') {
197
202
  done(transaction)
@@ -687,7 +692,7 @@ Transaction.prototype.getIntrinsicAttributes = function getIntrinsicAttributes()
687
692
 
688
693
  // If CAT is enabled, extract the path hashes and referring transaction info.
689
694
  if (config.cross_application_tracer.enabled) {
690
- if (config.feature_flag.distributed_tracing) {
695
+ if (config.distributed_tracing.enabled) {
691
696
  this.addDistributedTraceIntrinsics(this._intrinsicAttributes)
692
697
  } else {
693
698
  this._intrinsicAttributes.path_hash = hashes.calculatePathHash(
@@ -749,7 +754,7 @@ function acceptDistributedTracePayload(payload, transport) {
749
754
  }
750
755
 
751
756
  const config = this.agent.config
752
- const distTraceEnabled = config.feature_flag.distributed_tracing
757
+ const distTraceEnabled = config.distributed_tracing.enabled
753
758
  const catEnabled = config.cross_application_tracer.enabled
754
759
  const trustedAccount = config.trusted_account_key || config.account_id
755
760
 
@@ -765,6 +770,23 @@ function acceptDistributedTracePayload(payload, transport) {
765
770
  return
766
771
  }
767
772
 
773
+ if (!parsed.v || !parsed.d) {
774
+ if (!parsed.v) {
775
+ logger.warn(
776
+ 'Received a distributed trace payload with no version field',
777
+ this.id
778
+ )
779
+ }
780
+ if (!parsed.d) {
781
+ logger.warn(
782
+ 'Received a distributed trace payload with no data field',
783
+ this.id
784
+ )
785
+ }
786
+ this.agent.recordSupportability('DistributedTrace/AcceptPayload/ParseException')
787
+ return
788
+ }
789
+
768
790
  const majorVersion = parsed.v && typeof parsed.v[0] === 'number' && parsed.v[0]
769
791
  if (majorVersion == null) {
770
792
  logger.warn('Invalid distributed trace payload, not accepting')
@@ -792,10 +814,10 @@ function acceptDistributedTracePayload(payload, transport) {
792
814
  return
793
815
  }
794
816
 
795
- const accountId = data.tk || data.ac
796
- if (accountId !== trustedAccount) {
817
+ const trustedAccountKey = data.tk || data.ac
818
+ if (trustedAccountKey !== trustedAccount) {
797
819
  this.agent.recordSupportability(
798
- `DistributedTrace/AcceptPayload/UntrustedAccount/${accountId}`
820
+ `DistributedTrace/AcceptPayload/Ignored/UntrustedAccount`
799
821
  )
800
822
  return
801
823
  }
@@ -804,7 +826,7 @@ function acceptDistributedTracePayload(payload, transport) {
804
826
 
805
827
  this.parentType = data.ty
806
828
  this.parentApp = data.ap
807
- this.parentAcct = accountId
829
+ this.parentAcct = data.ac
808
830
  this.parentTransportType = transport
809
831
  this.parentTransportDuration = Math.max(0, (Date.now() - data.ti) / 1000)
810
832
  this.traceId = data.tr
@@ -823,6 +845,10 @@ function acceptDistributedTracePayload(payload, transport) {
823
845
  }
824
846
 
825
847
  this.isDistributedTrace = true
848
+ // Track if the distributed trace was created through accepting, since
849
+ // there is potentially no data difference between creation from
850
+ // Mobile or Browser trace payloads and creation.
851
+ this.acceptedDistributedTrace = true
826
852
 
827
853
  this.agent.recordSupportability('DistributedTrace/AcceptPayload/Success')
828
854
  }
@@ -873,7 +899,7 @@ function createDistributedTracePayload() {
873
899
  const config = this.agent.config
874
900
  const accountId = config.account_id
875
901
  const appId = config.application_id
876
- const distTraceEnabled = config.feature_flag.distributed_tracing
902
+ const distTraceEnabled = config.distributed_tracing.enabled
877
903
  const catEnabled = config.cross_application_tracer.enabled
878
904
 
879
905
  if (!accountId || !appId || !distTraceEnabled || !catEnabled) {
@@ -898,7 +924,7 @@ function createDistributedTracePayload() {
898
924
  ti: Date.now()
899
925
  }
900
926
 
901
- if (this.agent.config.span_events.enabled && currSegment) {
927
+ if (this.agent.config.span_events.enabled && this.sampled && currSegment) {
902
928
  data.id = currSegment.id
903
929
  }
904
930
 
@@ -918,7 +944,8 @@ function createDistributedTracePayload() {
918
944
  Transaction.prototype.addDistributedTraceIntrinsics = addDistributedTraceIntrinsics
919
945
  function addDistributedTraceIntrinsics(attrs) {
920
946
  this._calculatePriority()
921
- // *always* add these if DT flag is enabled
947
+
948
+ // *always* add these if DT flag is enabled.
922
949
  attrs.traceId = this.traceId || this.id
923
950
  attrs.guid = this.id
924
951
  attrs.priority = this.priority
@@ -1,5 +1,7 @@
1
1
  'use strict'
2
2
 
3
+ const byteUtils = require('../../util/byte-limit')
4
+
3
5
  module.exports = TraceAttributes
4
6
 
5
7
  /**
@@ -20,7 +22,7 @@ function TraceAttributes(opts) {
20
22
  */
21
23
  TraceAttributes.prototype.isValidLength = isValidLength
22
24
  function isValidLength(str) {
23
- return Buffer.byteLength(str, 'utf8') < 256
25
+ return byteUtils.isValidLength(str, 256)
24
26
  }
25
27
 
26
28
  /**
@@ -55,7 +57,10 @@ TraceAttributes.prototype.get = function get(dest) {
55
57
  continue
56
58
  }
57
59
 
58
- attrs[key] = typeof attr.value === 'string' ? truncate(attr.value) : attr.value
60
+ attrs[key] = typeof attr.value === 'string'
61
+ ? byteUtils.truncate(attr.value, 255)
62
+ : attr.value
63
+
59
64
  if (++attrCount >= this.limit) {
60
65
  break
61
66
  }
@@ -83,31 +88,6 @@ TraceAttributes.prototype.reset = function reset() {
83
88
  this.attributes = Object.create(null)
84
89
  }
85
90
 
86
- /**
87
- * Trims a string value to 255 bytes, if necessary.
88
- *
89
- * @private
90
- *
91
- * @param {string} val - The value to truncate to 255 bytes.
92
- *
93
- * @return {string} The truncated value.
94
- */
95
- function truncate(val) {
96
- // First truncation handles the simple case of only one-byte characters.
97
- if (!isValidLength(val)) {
98
- val = val.substring(0, 255)
99
- }
100
-
101
- // Our limitation is on byte length, and the string could contain multi-byte
102
- // characters. Doing a byte-substring could chop a character in half. We need
103
- // to pop the remaining characters off one by one until we have a good length.
104
- var l = val.length
105
- while (!isValidLength(val)) {
106
- val = val.substring(0, --l)
107
- }
108
- return val
109
- }
110
-
111
91
  /**
112
92
  * Checks incoming attribute value against valid types:
113
93
  * string, number, boolean, & null.
@@ -49,72 +49,63 @@ function Trace(transaction) {
49
49
  * segments that support recording.
50
50
  */
51
51
  Trace.prototype.end = function end() {
52
- var config = this.transaction.agent.config
53
- var segments = []
54
- var processFn
55
-
56
- if (config.span_events.enabled && config.feature_flag.distributed_tracing) {
57
- segments.push(new DTTraceNode(
58
- this.root,
59
- this.transaction.id,
60
- this.transaction.parentId || null
61
- ))
62
- processFn = makeDTNodeProcessor(this.transaction.agent.spans)
63
- } else {
64
- segments.push(this.root)
65
- processFn = processNode
66
- }
52
+ var segments = [this.root]
67
53
 
68
54
  while (segments.length) {
69
55
  var segment = segments.pop()
70
- processFn(segment, segments)
56
+ _softEndSegment(segment)
57
+ Array.prototype.push.apply(segments, segment.getChildren())
71
58
  }
72
59
  }
73
60
 
74
- function DTTraceNode(segment, parentId, grandparentId) {
75
- this.segment = segment
76
- this.parentId = parentId
77
- this.grandparentId = grandparentId
78
- }
79
-
80
- function makeDTNodeProcessor(spanAggregator) {
81
- var addMoreSpans = true
82
- return function processNodeDT(segmentInfo, segments) {
83
- var segment = segmentInfo.segment
61
+ /**
62
+ * Iterates over the trace tree and generates a span event for each segment.
63
+ */
64
+ Trace.prototype.generateSpanEvents = function generateSpanEvents() {
65
+ var config = this.transaction.agent.config
84
66
 
85
- if (segment.timer.softEnd()) {
86
- segment._updateRootTimer()
87
- // timer.softEnd() returns true if the timer was ended prematurely, so
88
- // in that case we can name the segment as truncated
89
- segment.name = NAMES.TRUNCATED.PREFIX + segment.name
67
+ if (
68
+ this.transaction.sampled &&
69
+ config.span_events.enabled &&
70
+ config.distributed_tracing.enabled
71
+ ) {
72
+ var toProcess = []
73
+ // Root segment does not become a span, so we need to process it separately.
74
+ const spanAggregator = this.transaction.agent.spans
75
+ const children = this.root.getChildren()
76
+ for (let i = 0; i < children.length; ++i) {
77
+ toProcess.push(new DTTraceNode(children[i], this.transaction.parentSpanId))
90
78
  }
91
79
 
92
- // This assumes that spans will have their transaction's priority. If insertion of
93
- // one event fails, all following will fail due to having the same priority.
94
- if (addMoreSpans) {
95
- addMoreSpans = spanAggregator.addSegment(
96
- segment,
97
- segmentInfo.parentId,
98
- segmentInfo.grandparentId
80
+ while (toProcess.length) {
81
+ var segmentInfo = toProcess.pop()
82
+ var segment = segmentInfo.segment
83
+
84
+ // Even though at some point we might want to stop adding events
85
+ // because all the priorities should be the same, we need to count
86
+ // the spans as seen.
87
+ spanAggregator.addSegment(segment, segmentInfo.parentId)
88
+
89
+ Array.prototype.push.apply(
90
+ toProcess,
91
+ segment.getChildren().map(child => new DTTraceNode(child, segment.id))
99
92
  )
100
93
  }
101
-
102
- var children = segment.getChildren()
103
- Array.prototype.push.apply(
104
- segments,
105
- children.map(child => new DTTraceNode(child, segment.id, segmentInfo.parentId))
106
- )
107
94
  }
108
95
  }
109
96
 
110
- function processNode(segment, segments) {
97
+ function DTTraceNode(segment, parentId) {
98
+ this.segment = segment
99
+ this.parentId = parentId
100
+ }
101
+
102
+ function _softEndSegment(segment) {
111
103
  if (segment.timer.softEnd()) {
112
104
  segment._updateRootTimer()
113
105
  // timer.softEnd() returns true if the timer was ended prematurely, so
114
106
  // in that case we can name the segment as truncated
115
107
  segment.name = NAMES.TRUNCATED.PREFIX + segment.name
116
108
  }
117
- Array.prototype.push.apply(segments, segment.getChildren())
118
109
  }
119
110
 
120
111
  /**
@@ -0,0 +1,40 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * Checks if a given string is within agent attribute limits.
5
+ *
6
+ * @param {string} str - Object key name or value
7
+ * @param {number} limit - String byte limit
8
+ */
9
+ function isValidLength(str, limit) {
10
+ return Buffer.byteLength(str, 'utf8') < limit
11
+ }
12
+
13
+ /**
14
+ * Trims a string value to given byte limit, if necessary.
15
+ *
16
+ * @private
17
+ *
18
+ * @param {string} val - The value to truncate to given byte limit.
19
+ * @param {number} limit - The byte limit
20
+ *
21
+ * @return {string} The truncated value.
22
+ */
23
+ function truncate(val, limit) {
24
+ // First truncation handles the simple case of only one-byte characters.
25
+ if (!isValidLength(val + 1)) {
26
+ val = val.substring(0, limit)
27
+ }
28
+
29
+ // Our limitation is on byte length, and the string could contain multi-byte
30
+ // characters. Doing a byte-substring could chop a character in half. We need
31
+ // to pop the remaining characters off one by one until we have a good length.
32
+ var l = val.length
33
+ while (!isValidLength(val, limit + 1)) {
34
+ val = val.substring(0, --l)
35
+ }
36
+ return val
37
+ }
38
+
39
+ module.exports.isValidLength = isValidLength
40
+ module.exports.truncate = truncate
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "newrelic",
3
- "version": "4.4.0",
3
+ "version": "4.7.0",
4
4
  "author": "New Relic Node.js agent team <nodejs@newrelic.com>",
5
5
  "licenses": [
6
6
  {
@@ -96,7 +96,7 @@
96
96
  ],
97
97
  "homepage": "http://github.com/newrelic/node-newrelic",
98
98
  "engines": {
99
- "node": ">=4.0.0",
99
+ "node": ">=4.0.0 <11.0.0",
100
100
  "npm": ">=2.0.0"
101
101
  },
102
102
  "directories": {
package/.npmignore DELETED
@@ -1,4 +0,0 @@
1
- test
2
- examples
3
- out
4
- Makefile