newrelic 8.17.1 → 9.0.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.js CHANGED
@@ -53,8 +53,8 @@ function initialize() {
53
53
  throw new Error(message)
54
54
  }
55
55
 
56
- // TODO: Update this check when Node v18 support is added
57
- if (psemver.satisfies('>=17.0.0')) {
56
+ // TODO: Update this check when Node v20 support is added
57
+ if (psemver.satisfies('>=19.0.0')) {
58
58
  logger.warn(
59
59
  'New Relic for Node.js %s has not been tested on Node.js %s. Please ' +
60
60
  'update the agent or downgrade your version of Node.js',
@@ -525,7 +525,7 @@ function transactionSampleData(traces, callback) {
525
525
  * @param logRecords
526
526
  * @param {Function} callback The continuation / error handler.
527
527
  */
528
- CollectorAPI.prototype.log_event_data = function errorData(logRecords, callback) {
528
+ CollectorAPI.prototype.log_event_data = function logEventData(logRecords, callback) {
529
529
  if (!callback) {
530
530
  throw new TypeError('callback is required')
531
531
  }
@@ -5,7 +5,6 @@
5
5
 
6
6
  'use strict'
7
7
 
8
- const a = require('async')
9
8
  const fetchSystemInfo = require('../system-info')
10
9
  const logger = require('../logger').child({ component: 'facts' })
11
10
  const os = require('os')
@@ -19,94 +18,95 @@ const LOG_LIBRARIES = ['winston', 'bunyan', 'pino', 'loglevel', 'npmlog', 'fancy
19
18
 
20
19
  module.exports = facts
21
20
 
22
- function facts(agent, callback) {
21
+ async function facts(agent, callback) {
23
22
  const startTime = Date.now()
24
- a.parallel(
25
- {
26
- systemInfo: a.apply(fetchSystemInfo, agent),
27
- environment: agent.environment.getJSON
28
- },
29
- function factMapCb(err, data) {
30
- logger.trace('Facts gathering finished in %dms', Date.now() - startTime)
31
23
 
32
- if (err) {
33
- logger.debug(err, 'Failed to load system facts!')
34
- }
35
- data = data || Object.create(null)
36
- const systemInfo = data.systemInfo || Object.create(null)
37
- const environment = data.environment || []
38
-
39
- countSupportabilityForLogLibraries(environment, agent)
40
-
41
- const hostname = agent.config.getHostnameSafe()
42
- const results = {
43
- utilization: {
44
- metadata_version: 5,
45
- logical_processors: systemInfo.logicalProcessors || null,
46
- total_ram_mib: systemInfo.memory || null,
47
- hostname: hostname
48
- },
49
- pid: process.pid,
50
- host: hostname,
51
- display_host: agent.config.getDisplayHost() || hostname,
52
- language: 'nodejs',
53
- app_name: agent.config.applications(),
54
- agent_version: agent.version,
55
- environment: environment,
56
- settings: agent.config.publicSettings(),
57
- high_security: agent.config.high_security,
58
- labels: parseLabels(agent.config.labels),
59
- metadata: Object.keys(process.env).reduce((obj, key) => {
60
- if (key.startsWith('NEW_RELIC_METADATA_')) {
61
- obj[key] = process.env[key]
62
- }
63
- return obj
64
- }, {})
65
- }
24
+ const systemInfoPromise = new Promise((resolve) => {
25
+ fetchSystemInfo(agent, (_, data) => {
26
+ resolve(data)
27
+ })
28
+ })
66
29
 
67
- logger.debug('New Relic metadata %o', results.metadata)
68
-
69
- /**
70
- * WARNING: This may not make sense if you are familiar with our config
71
- * and updating of config from server. But the intention here is to always
72
- * send the values from user config of harvest limits because on connect these
73
- * values get reconfigured based on harvest cycle intervals. So if you originally
74
- * had 1000 and a harvest of 5 seconds the new value of the harvest limit would be 83.
75
- * Then every subsequent connect request it would continue to decrease until it eventually hit 0
76
- * and we would never be sampling a certain piece of data.
77
- */
78
- results.event_harvest_config = {
79
- harvest_limits: {
80
- analytic_event_data: agent.config.transaction_events.max_samples_stored,
81
- custom_event_data: agent.config.custom_insights_events.max_samples_stored,
82
- error_event_data: agent.config.error_collector.max_event_samples_stored,
83
- span_event_data: agent.config.span_events.max_samples_stored,
84
- log_event_data: agent.config.application_logging.forwarding.max_samples_stored
85
- }
86
- }
30
+ const environmentPromise = agent.environment.getJSON()
31
+ let [systemInfo, environment] = await Promise.all([systemInfoPromise, environmentPromise])
32
+ logger.trace('Facts gathering finished in %dms', Date.now() - startTime)
87
33
 
88
- results.identifier = getIdentifierOverride(results.app_name)
34
+ if (environment.failed) {
35
+ logger.debug(environment.err, 'Failed to load system facts!')
36
+ }
89
37
 
90
- const ipAddresses = getAllIPAddresses()
91
- if (ipAddresses.length) {
92
- results.utilization.ip_address = ipAddresses
93
- }
38
+ systemInfo = systemInfo || Object.create(null)
39
+ environment = environment || []
94
40
 
95
- if (systemInfo.bootId) {
96
- results.utilization.boot_id = systemInfo.bootId
97
- }
41
+ countSupportabilityForLogLibraries(environment, agent)
98
42
 
99
- if (systemInfo.vendors) {
100
- results.utilization.vendors = systemInfo.vendors
43
+ const hostname = agent.config.getHostnameSafe()
44
+ const results = {
45
+ utilization: {
46
+ metadata_version: 5,
47
+ logical_processors: systemInfo.logicalProcessors || null,
48
+ total_ram_mib: systemInfo.memory || null,
49
+ hostname: hostname
50
+ },
51
+ pid: process.pid,
52
+ host: hostname,
53
+ display_host: agent.config.getDisplayHost() || hostname,
54
+ language: 'nodejs',
55
+ app_name: agent.config.applications(),
56
+ agent_version: agent.version,
57
+ environment: environment,
58
+ settings: agent.config.publicSettings(),
59
+ high_security: agent.config.high_security,
60
+ labels: parseLabels(agent.config.labels),
61
+ metadata: Object.keys(process.env).reduce((obj, key) => {
62
+ if (key.startsWith('NEW_RELIC_METADATA_')) {
63
+ obj[key] = process.env[key]
101
64
  }
65
+ return obj
66
+ }, {})
67
+ }
68
+
69
+ logger.debug('New Relic metadata %o', results.metadata)
70
+
71
+ /**
72
+ * WARNING: This may not make sense if you are familiar with our config
73
+ * and updating of config from server. But the intention here is to always
74
+ * send the values from user config of harvest limits because on connect these
75
+ * values get reconfigured based on harvest cycle intervals. So if you originally
76
+ * had 1000 and a harvest of 5 seconds the new value of the harvest limit would be 83.
77
+ * Then every subsequent connect request it would continue to decrease until it eventually hit 0
78
+ * and we would never be sampling a certain piece of data.
79
+ */
80
+ results.event_harvest_config = {
81
+ harvest_limits: {
82
+ analytic_event_data: agent.config.transaction_events.max_samples_stored,
83
+ custom_event_data: agent.config.custom_insights_events.max_samples_stored,
84
+ error_event_data: agent.config.error_collector.max_event_samples_stored,
85
+ span_event_data: agent.config.span_events.max_samples_stored,
86
+ log_event_data: agent.config.application_logging.forwarding.max_samples_stored
87
+ }
88
+ }
102
89
 
103
- if (systemInfo.config) {
104
- results.utilization.config = systemInfo.config
105
- }
90
+ results.identifier = getIdentifierOverride(results.app_name)
106
91
 
107
- return callback(results)
108
- }
109
- )
92
+ const ipAddresses = getAllIPAddresses()
93
+ if (ipAddresses.length) {
94
+ results.utilization.ip_address = ipAddresses
95
+ }
96
+
97
+ if (systemInfo.bootId) {
98
+ results.utilization.boot_id = systemInfo.bootId
99
+ }
100
+
101
+ if (systemInfo.vendors) {
102
+ results.utilization.vendors = systemInfo.vendors
103
+ }
104
+
105
+ if (systemInfo.config) {
106
+ results.utilization.config = systemInfo.config
107
+ }
108
+
109
+ callback(results)
110
110
  }
111
111
 
112
112
  function getAllIPAddresses() {
@@ -8,8 +8,6 @@
8
8
  const parse = require('url').parse
9
9
  const ProxyAgent = require('https-proxy-agent')
10
10
  const logger = require('../logger').child({ component: 'http-agent' })
11
- const certificates = require('./ssl/certificates.js')
12
-
13
11
  const https = require('https')
14
12
 
15
13
  // poor person's single-instance-objects. We
@@ -21,6 +19,8 @@ let agentProxyWithKeepAlive = null
21
19
 
22
20
  /**
23
21
  * Returns an HTTP agent with keep-alive enabled
22
+ *
23
+ * @param config
24
24
  */
25
25
  exports.keepAliveAgent = function keepAliveAgent(config) {
26
26
  config = config ? config : {}
@@ -45,6 +45,8 @@ exports.keepAliveAgent = function keepAliveAgent(config) {
45
45
  * Include keep-alive configuration, but ultimately its up
46
46
  * to the proxy server as to how its connection is made
47
47
  * with New Relic's servers.
48
+ *
49
+ * @param config
48
50
  */
49
51
  exports.proxyAgent = function proxyAgent(config) {
50
52
  if (null !== agentProxyWithKeepAlive) {
@@ -109,25 +111,6 @@ function proxyOptions(config) {
109
111
 
110
112
  if (config.certificates && config.certificates.length > 0) {
111
113
  opts.certificates = config.certificates
112
-
113
- // merge user certificates with built-in certs
114
- if (config.feature_flag.certificate_bundle) {
115
- logger.info(
116
- 'Using a proxy with a special cert. This enables our cert bundle which, combined ' +
117
- 'with some versions of node, exacerbates a leak in node core TLS.'
118
- )
119
-
120
- const certWarningMessage = [
121
- 'Deprecation Warning: The certificate bundle included by New Relic will be ',
122
- 'disabled by default and then fully removed in later major versions. We recommend ',
123
- 'testing with the certificate_bundle feature flag set to `false` to determine if ',
124
- 'you will need to modify your environment or setup your own appropriate bundle. ',
125
- 'Example configuration: feature_flag: { certificate_bundle: false }.'
126
- ].join('')
127
- logger.warnOnce('CERT_WARNING', certWarningMessage)
128
-
129
- opts.certificates = config.certificates.concat(certificates)
130
- }
131
114
  }
132
115
 
133
116
  return opts
@@ -14,7 +14,6 @@ const parse = require('./parse-response')
14
14
  const stringify = require('json-stringify-safe')
15
15
  const Sink = require('../util/stream-sink')
16
16
  const agents = require('./http-agents')
17
- const certificates = require('./ssl/certificates')
18
17
  const isValidLength = require('../util/byte-limit').isValidLength
19
18
  const { DATA_USAGE } = require('../metrics/names')
20
19
 
@@ -61,7 +60,7 @@ RemoteMethod.prototype.updateEndpoint = function updateEndpoint(endpoint) {
61
60
  RemoteMethod.prototype.serialize = function serialize(payload, callback) {
62
61
  let res
63
62
  try {
64
- res = stringify(payload)
63
+ res = stringify(payload, (key, value) => (typeof value === 'bigint' ? value.toString() : value))
65
64
  } catch (error) {
66
65
  logger.error(error, 'Unable to serialize payload for method %s.', this.name)
67
66
  return process.nextTick(function onNextTick() {
@@ -153,8 +152,8 @@ RemoteMethod.prototype._post = function _post(data, nrHeaders, callback) {
153
152
  if (options.compressed) {
154
153
  // NOTE: gzip and deflate throw immediately in Node 14+ with an invalid argument
155
154
  try {
156
- const useGzip = this._config.compressed_content_encoding === 'gzip'
157
- const compressor = useGzip ? zlib.gzip : zlib.deflate
155
+ const useDeflate = this._config.compressed_content_encoding === 'deflate'
156
+ const compressor = useDeflate ? zlib.deflate : zlib.gzip
158
157
  compressor(data, function onCompress(err, compressed) {
159
158
  if (err) {
160
159
  logger.warn(err, 'Error compressing JSON for delivery. Not sending.')
@@ -306,21 +305,6 @@ RemoteMethod.prototype._request = function _request(options) {
306
305
  } else {
307
306
  if (this._config.certificates && this._config.certificates.length > 0) {
308
307
  requestOptions.ca = this._config.certificates
309
-
310
- if (this._config.feature_flag.certificate_bundle) {
311
- logger.debug('Adding custom certificate to the cert bundle.')
312
-
313
- const certWarningMessage = [
314
- 'Deprecation Warning: The certificate bundle included by New Relic will be ',
315
- 'disabled by default and then fully removed in later major versions. We recommend ',
316
- 'testing with the certificate_bundle feature flag set to `false` to determine if ',
317
- 'you will need to modify your environment or setup your own appropriate bundle. ',
318
- 'Example configuration: feature_flag: { certificate_bundle: false }.'
319
- ].join('')
320
- logger.warnOnce('CERT_WARNING', certWarningMessage)
321
-
322
- requestOptions.ca = this._config.certificates.concat(certificates)
323
- }
324
308
  }
325
309
  request = https.request(requestOptions)
326
310
  }
@@ -139,6 +139,16 @@ exports.config = () => ({
139
139
  */
140
140
  allow_all_headers: false,
141
141
 
142
+ /**
143
+ * If the data compression threshold is reached in the payload, the
144
+ * agent compresses data, using gzip compression by default. The
145
+ * config option `compressed_content_encoding` can be set to 'deflate'
146
+ * to use deflate compression.
147
+ *
148
+ * @env NEW_RELIC_COMPRESSED_CONTENT_ENCODING
149
+ */
150
+ compressed_content_encoding: 'gzip',
151
+
142
152
  /**
143
153
  * Attributes are key-value pairs containing information that determines
144
154
  * the properties of an event or transaction.
package/lib/config/env.js CHANGED
@@ -22,6 +22,7 @@ const ENV_MAPPING = {
22
22
  proxy_pass: 'NEW_RELIC_PROXY_PASS',
23
23
  agent_enabled: 'NEW_RELIC_ENABLED',
24
24
  allow_all_headers: 'NEW_RELIC_ALLOW_ALL_HEADERS',
25
+ compressed_content_encoding: 'NEW_RELIC_COMPRESSED_CONTENT_ENCODING',
25
26
  attributes: {
26
27
  enabled: 'NEW_RELIC_ATTRIBUTES_ENABLED',
27
28
  exclude: 'NEW_RELIC_ATTRIBUTES_EXCLUDE',
@@ -154,7 +154,6 @@ function Config(config) {
154
154
  this.browser_monitoring.loader_version = ''
155
155
 
156
156
  // Settings to play nice with DLPs (see NODE-1044).
157
- this.compressed_content_encoding = 'deflate' // Deflate or gzip
158
157
  this.simple_compression = false // Disables subcomponent compression
159
158
  this.put_for_data_send = false // Changes http verb for harvest
160
159
 
@@ -858,13 +857,6 @@ Config.prototype.logUnknown = function logUnknown(json, key) {
858
857
  logger.debug('New Relic sent unknown configuration parameter %s with value %s.', key, value)
859
858
  }
860
859
 
861
- /**
862
- * Return the availability of async_hook for use by the agent.
863
- */
864
- Config.prototype.checkAsyncHookStatus = function checkAsyncHookStatus() {
865
- return this.feature_flag.await_support
866
- }
867
-
868
860
  /**
869
861
  * Gets the user set host display name. If not provided, it returns the default value.
870
862
  *
@@ -5,7 +5,6 @@
5
5
 
6
6
  'use strict'
7
7
 
8
- const a = require('async')
9
8
  const logger = require('../logger').child({ component: 'query_tracer' })
10
9
  const Aggregator = require('../aggregators/base-aggregator')
11
10
  const SlowQuery = require('./slow-query')
@@ -132,8 +131,26 @@ class QueryTraceAggregator extends Aggregator {
132
131
  this.samples = new Map()
133
132
  }
134
133
 
135
- prepareJSON(done) {
136
- a.map(this.samples.values(), (sample, cb) => sample.prepareJSON(cb), done)
134
+ async prepareJSON(done) {
135
+ const samplePromises = []
136
+ for (const sample of this.samples.values()) {
137
+ const samplePromise = new Promise((resolve, reject) => {
138
+ sample.prepareJSON((err, data) => {
139
+ if (err) {
140
+ reject(err)
141
+ }
142
+ resolve(data)
143
+ })
144
+ })
145
+ samplePromises.push(samplePromise)
146
+ }
147
+
148
+ try {
149
+ const data = await Promise.all(samplePromises)
150
+ done(null, data)
151
+ } catch (err) {
152
+ done(err)
153
+ }
137
154
  }
138
155
 
139
156
  prepareJSONSync() {