newrelic 8.8.0 → 8.9.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,43 @@
1
+ ### v8.9.0 (2022-03-15)
2
+
3
+ * Added support for `initializeUnorderedBulkOp`, and `initializeOrderedBulkOp` in mongodb v3 instrumentation.
4
+
5
+ Thanks to Denis Lantsman (@dlants) for the contribution.
6
+
7
+ * Updated logger to delay logging until configuration is parsed. The logger will now queue all log entries that occur before the agent can parse the configuration.
8
+
9
+ Thanks to Cody Landry (@codylandry) for the contribution.
10
+
11
+ * Added `NEW_RELIC_ALLOW_ALL_HEADERS` as a boolean environment variable, same behavior as existing `allow_all_headers`.
12
+
13
+ * Updated the AWS IMDBS v2 endpoint to use `latest` to align with the internal agent specification.
14
+
15
+ * Bumped `@newrelic/koa` to ^6.1.1.
16
+
17
+ * Added Next.js to External Modules list in README.
18
+
19
+ * Updated mysql and mysql2 versioned tests to run against their own databases on the MySQL instance.
20
+
21
+ * Removed upper-bound testing from restify versioned tests so future major versions will be covered.
22
+
23
+ * Removed upper-bound testing from mysql2 versioned tests to cover existing and future major versions.
24
+
25
+ Continues to skip version 1.6.2 which had a bug that broke tests which was resolved in 1.6.3.
26
+
27
+ * Updated @hapi/hapi Node 16 versioned test runs to run against @hapi/hapi >=20.1.2 so future major releases will be ran.
28
+
29
+ * Fixed sparse checkout of non-default branch for external versioned tests.
30
+
31
+ * Added external versioned tests for the Apollo Server plugin instrumentation.
32
+
33
+ * Added nock delay to test timeouts in utilization integration tests.
34
+
35
+ * Added newrelic-node-nextjs to external versioned tests to be run on every PR.
36
+
37
+ * Updated external version test running to support more test scenarios.
38
+ * Adds `test/versioned-external` to lint ignore to avoid issues for scripts in tests that auto run linting tools (next/react).
39
+ * Adds `index.js` and `nr-hooks.js` to files automatically checked-out for test runs.
40
+
1
41
  ### v8.8.0 (2022-02-23)
2
42
 
3
43
  * Updated AWS metadata capture to utilize IMDSv2.
package/README.md CHANGED
@@ -70,6 +70,7 @@ There are several modules that can be installed and configured to accompany the
70
70
  * [@newrelic/winston-enricher](https://github.com/newrelic/newrelic-node-log-extensions/tree/main/packages/winston-log-enricher): Provides distributed trace and span information output as JSON-formatted log messages in Winston. This is most commonly used with the New Relic Logs product.
71
71
  * [@newrelic/pino-enricher](https://github.com/newrelic/newrelic-node-log-extensions/tree/main/packages/pino-log-enricher): Provides distributed trace and span information output as JSON-formatted log messages in Pino. This is most commonly used with the New Relic Logs product.
72
72
  * [@newrelic/mysql](https://github.com/newrelic/node-newrelic-mysql)(**Experimental**): Standalone instrumentation for [mysql2](https://github.com/sidorares/node-mysql2#readme) and [mysql2/promise](https://github.com/sidorares/node-mysql2/blob/master/documentation/Promise-Wrapper.md) as well as the same instrumentation for [mysql](https://github.com/mysqljs/mysql#readme) within the Node.js agent.
73
+ * [@newrelic/next](https://github.com/newrelic/newrelic-node-nextjs): Provides instrumentation for the [Next.js](https://github.com/vercel/next.js/) npm package.
73
74
 
74
75
  There are several modules included within the Node.js agent to add more instrumentation for 3rd party modules:
75
76
 
@@ -134,6 +134,8 @@ exports.config = () => ({
134
134
  * When true, all request headers except for those listed in attributes.exclude
135
135
  * will be captured for all traces, unless otherwise specified in a destination's
136
136
  * attributes include/exclude lists.
137
+ *
138
+ * @env NEW_RELIC_ALLOW_ALL_HEADERS
137
139
  */
138
140
  allow_all_headers: false,
139
141
 
package/lib/config/env.js CHANGED
@@ -21,6 +21,7 @@ const ENV_MAPPING = {
21
21
  proxy_user: 'NEW_RELIC_PROXY_USER',
22
22
  proxy_pass: 'NEW_RELIC_PROXY_PASS',
23
23
  agent_enabled: 'NEW_RELIC_ENABLED',
24
+ allow_all_headers: 'NEW_RELIC_ALLOW_ALL_HEADERS',
24
25
  attributes: {
25
26
  enabled: 'NEW_RELIC_ATTRIBUTES_ENABLED',
26
27
  exclude: 'NEW_RELIC_ATTRIBUTES_EXCLUDE',
@@ -206,6 +207,7 @@ const OBJECT_LIST_VARS = new Set(['NEW_RELIC_NAMING_RULES'])
206
207
  // fancy--just use 'true' and 'false', everybody.
207
208
  const BOOLEAN_VARS = new Set([
208
209
  'NEW_RELIC_ENABLED',
210
+ 'NEW_RELIC_ALLOW_ALL_HEADERS',
209
211
  'NEW_RELIC_ATTRIBUTES_ENABLED',
210
212
  'NEW_RELIC_ERROR_COLLECTOR_ENABLED',
211
213
  'NEW_RELIC_ERROR_COLLECTOR_ATTRIBUTES_ENABLED',
@@ -1623,7 +1623,8 @@ function initialize(config) {
1623
1623
  /**
1624
1624
  * When the logger is required here, it bootstraps itself and then
1625
1625
  * injects itself into this module's closure via setLogger on the
1626
- * instance of the logger it creates.
1626
+ * instance of the logger it creates. Logs are queued until config
1627
+ * has been loaded to apply logging settings to bootstrapping logs
1627
1628
  */
1628
1629
  logger = require('../logger')
1629
1630
 
@@ -1712,7 +1713,14 @@ function getOrCreateInstance() {
1712
1713
 
1713
1714
  // Config construction has potential to throw due to invalid settings.
1714
1715
  // This allows the agent to return a stub api without crashing the process.
1715
- _configInstance = Object.assign(defaultConfig(), { agent_enabled: false })
1716
+ _configInstance = Object.assign(defaultConfig(), {
1717
+ agent_enabled: false,
1718
+ logging: {
1719
+ enabled: true,
1720
+ filepath: 'stdout'
1721
+ }
1722
+ })
1723
+
1716
1724
  _configInstance.setLogger = Config.prototype.setLogger
1717
1725
  }
1718
1726
  }
@@ -43,6 +43,23 @@ common.instrumentCollection = function instrumentCollection(shim, Collection) {
43
43
  }
44
44
  }
45
45
 
46
+ /**
47
+ * Instruments the execute method on
48
+ * the BulkOperationBase class
49
+ *
50
+ * @param {Shim} shim
51
+ * @param {BulkOperationModule} bulk operation module, typically from mongodb/lib/bulk/common
52
+ * @param BulkOperationModule
53
+ */
54
+ common.instrumentBulkOperation = function instrumentBulkOperation(shim, BulkOperationModule) {
55
+ const BulkOperationBase = BulkOperationModule && BulkOperationModule.BulkOperationBase
56
+
57
+ if (BulkOperationBase && BulkOperationBase.prototype) {
58
+ const proto = BulkOperationBase.prototype
59
+ shim.recordBatchQuery(proto, 'execute', common.makeBulkDescFunc(shim, 'execute'))
60
+ }
61
+ }
62
+
46
63
  /**
47
64
  * Instruments all methods from constants.DB_OPS on
48
65
  * the Db class.
@@ -84,6 +101,25 @@ common.makeQueryDescFunc = function makeQueryDescFunc(shim, methodName) {
84
101
  }
85
102
  }
86
103
 
104
+ /**
105
+ * Sets up the desc for all instrumented bulk operations
106
+ *
107
+ * @param {Shim} shim
108
+ * @param {string} methodName
109
+ */
110
+ common.makeBulkDescFunc = function makeBulkDescFunc(shim) {
111
+ return function bulkDescFunc() {
112
+ const parameters = getInstanceAttributeParameters(shim, this)
113
+ return {
114
+ query: this.isOrdered ? 'orderedBulk' : 'unorderedBulk',
115
+ parameters,
116
+ promise: true,
117
+ callback: shim.LAST,
118
+ opaque: true
119
+ }
120
+ }
121
+ }
122
+
87
123
  /**
88
124
  * Sets up a listener for `started` on instrumenter(mongo APM). This applies to
89
125
  * mongo <4. The listener adds the following attributes to the active segment:
@@ -161,9 +197,10 @@ function setHostPort(shim, connStr, db, client) {
161
197
  * @param obj
162
198
  */
163
199
  function getInstanceAttributeParameters(shim, obj) {
164
- if (obj.s && obj.s.db && obj.s.topology) {
200
+ if (obj.s && obj.s.topology) {
165
201
  shim.logger.trace('Adding datastore instance attributes from obj.s.db + obj.s.topology')
166
- const databaseName = obj.s.db.databaseName || null
202
+ const databaseName =
203
+ (obj.s.db && obj.s.db.databaseName) || (obj.s.namespace && obj.s.namespace.db) || null
167
204
  const topology = obj.s.topology
168
205
  if (topology.s && topology.s.options) {
169
206
  return doCapture(topology.s.options, databaseName)
@@ -7,6 +7,7 @@
7
7
 
8
8
  const {
9
9
  captureAttributesOnStarted,
10
+ instrumentBulkOperation,
10
11
  instrumentCollection,
11
12
  instrumentCursor,
12
13
  instrumentDb,
@@ -27,6 +28,8 @@ function queryParser(operation) {
27
28
  collection = this.operation.target
28
29
  } else if (this.ns) {
29
30
  collection = this.ns.split(/\./)[1] || collection
31
+ } else if (this.s && this.s.collection && this.s.collection.collectionName) {
32
+ collection = this.s.collection.collectionName
30
33
  }
31
34
  return { operation, collection }
32
35
  }
@@ -68,6 +71,7 @@ module.exports = function instrument(shim, mongodb) {
68
71
  instrumentCursor(shim, mongodb.Cursor)
69
72
  instrumentCursor(shim, shim.require('./lib/aggregation_cursor'))
70
73
  instrumentCursor(shim, shim.require('./lib/command_cursor'))
74
+ instrumentBulkOperation(shim, shim.require('./lib/bulk/common'))
71
75
  instrumentCollection(shim, mongodb.Collection)
72
76
  instrumentDb(shim, mongodb.Db)
73
77
 
package/lib/logger.js CHANGED
@@ -9,12 +9,16 @@ const Logger = require('./util/logger')
9
9
  const fs = require('./util/unwrapped-core').fs
10
10
 
11
11
  // create bootstrapping logger
12
- module.exports = new Logger({
12
+ const logger = new Logger({
13
13
  name: 'newrelic_bootstrap',
14
- stream: process.stdout,
15
- level: 'info'
14
+ level: 'info',
15
+
16
+ // logger is configured below. Logs are queued until configured
17
+ configured: false
16
18
  })
17
19
 
20
+ module.exports = logger
21
+
18
22
  /**
19
23
  * Don't load config until this point, because it requires this
20
24
  * module, and if it gets loaded too early, module.exports will have no
@@ -28,8 +32,8 @@ if (config) {
28
32
  enabled: config.logging.enabled
29
33
  }
30
34
 
31
- // create the "real" logger
32
- module.exports = new Logger(options)
35
+ // configure logger
36
+ logger.configure(options)
33
37
 
34
38
  if (config.logging.enabled) {
35
39
  let stream
@@ -52,9 +56,6 @@ if (config) {
52
56
  /* eslint-enable no-console */
53
57
  })
54
58
  }
55
- module.exports.pipe(stream)
59
+ logger.pipe(stream)
56
60
  }
57
-
58
- // now tell the config module to switch to the real logger
59
- config.setLogger(module.exports)
60
61
  }
@@ -39,7 +39,8 @@ function Logger(options, extra) {
39
39
  const passedInLevel = this.coerce(options.level)
40
40
  this.options = {
41
41
  _level: passedInLevel,
42
- enabled: options.enabled === undefined ? true : options.enabled
42
+ enabled: options.enabled === undefined ? true : options.enabled,
43
+ configured: options.configured === undefined ? true : !!options.configured
43
44
  }
44
45
  this._nestedLog = false
45
46
  this.name = options.name
@@ -47,12 +48,30 @@ function Logger(options, extra) {
47
48
  this.extra = extra || Object.create(null)
48
49
  this.buffer = ''
49
50
  this.reading = false
51
+ this.logQueue = []
50
52
  if (options.stream) {
51
53
  this.pipe(options.stream)
52
54
  }
53
55
  }
54
56
  Logger.MAX_LOG_BUFFER = MAX_LOG_BUFFER
55
57
 
58
+ Logger.prototype.configure = function configure(options) {
59
+ if (options.name !== undefined) {
60
+ this.name = options.name
61
+ }
62
+
63
+ if (options.enabled !== undefined) {
64
+ this.options.enabled = options.enabled
65
+ }
66
+
67
+ if (options.level !== undefined) {
68
+ this.options._level = this.coerce(options.level)
69
+ }
70
+
71
+ this.options.configured = true
72
+ this._flushQueuedLogs()
73
+ }
74
+
56
75
  Logger.prototype.coerce = function coerce(value) {
57
76
  if (!isNaN(parseInt(value, 10)) && isFinite(value)) {
58
77
  // value is numeric
@@ -74,6 +93,11 @@ Object.keys(LEVELS).forEach(function buildLevel(_level) {
74
93
  const level = Logger.prototype.coerce(LEVELS[_level])
75
94
 
76
95
  function log(extra) {
96
+ if (!this.options.configured) {
97
+ this.logQueue.unshift({ level: _level, args: arguments })
98
+ return
99
+ }
100
+
77
101
  if (!this.options.enabled) {
78
102
  return false
79
103
  }
@@ -149,6 +173,13 @@ Object.keys(LEVELS).forEach(function buildLevel(_level) {
149
173
  }
150
174
  })
151
175
 
176
+ Logger.prototype._flushQueuedLogs = function _flushQueuedLogs() {
177
+ while (this.logQueue.length) {
178
+ const { level, args } = this.logQueue.shift()
179
+ this[level].apply(this, args)
180
+ }
181
+ }
182
+
152
183
  Object.assign(Logger.prototype, loggingFunctions)
153
184
 
154
185
  Logger.prototype.child = function child(extra) {
@@ -196,6 +227,10 @@ Logger.prototype._read = function _read() {
196
227
  * already converted the objects to strings.
197
228
  * Returns a boolean representing the status of the write
198
229
  * (success/failure)
230
+ *
231
+ * @param level
232
+ * @param args
233
+ * @param extra
199
234
  */
200
235
  Logger.prototype.write = function write(level, args, extra) {
201
236
  if (this._nestedLog) {
@@ -42,7 +42,7 @@ function fetchAWSInfo(agent, callback) {
42
42
  headers: { 'X-aws-ec2-metadata-token': authToken },
43
43
  host: INSTANCE_HOST,
44
44
  method: 'GET',
45
- path: '/2016-09-02/dynamic/instance-identity/document',
45
+ path: '/latest/dynamic/instance-identity/document',
46
46
  timeout: 500
47
47
  }
48
48
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "newrelic",
3
- "version": "8.8.0",
3
+ "version": "8.9.0",
4
4
  "author": "New Relic Node.js agent team <nodejs@newrelic.com>",
5
5
  "license": "Apache-2.0",
6
6
  "contributors": [
@@ -162,7 +162,7 @@
162
162
  "@grpc/grpc-js": "^1.5.5",
163
163
  "@grpc/proto-loader": "^0.6.9",
164
164
  "@newrelic/aws-sdk": "^4.1.1",
165
- "@newrelic/koa": "^6.1.0",
165
+ "@newrelic/koa": "^6.1.1",
166
166
  "@newrelic/superagent": "^5.1.0",
167
167
  "@tyriar/fibonacci-heap": "^2.0.7",
168
168
  "async": "^3.2.3",