newrelic 8.6.0 → 8.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,44 @@
1
+ ### v8.7.0 (2022-01-04)
2
+
3
+ * Updated `onResolved` instrumentation hook to only be called the first time we see a specific module filepath resolved.
4
+
5
+ * Removed `tracer.segment` in place of direct usage of context manager.
6
+
7
+ * Fixed an issue where multiple calls to `instrumentLoadedModule` resulted in re-instrumenting the same module.
8
+
9
+ * Fixed issue where `instrumentLoadedModule` would return `true` even if the instrumentation handler indicated it did not apply instrumentation.
10
+
11
+ * Added support metrics for tracking when instrumentation was applied per module.
12
+
13
+ * `Supportability/Features/Instrumentation/OnResolved/<module-name>`
14
+ * `Supportability/Features/Instrumentation/OnResolved/<module-name>/Version/<major version>`
15
+ * `Supportability/Features/Instrumentation/OnRequire/<module-name>`
16
+ * `Supportability/Features/Instrumentation/OnRequire/<module-name>/Version/<major version>`
17
+
18
+ * Fixed issue where expected status code ranges would not be parsed until ignored status codes were also defined.
19
+
20
+ * Added an input `changelog_file` to pass in name of changelog. This defaults to `NEWS.md` but some repos use `CHANGELOG.md`
21
+
22
+ * Abstracted `bin/prepare-release.js` to work against other repositories.
23
+
24
+ * Added reusable prepare-release workflow that can be referenced in all other newrelic Node.js repositories.
25
+
26
+ * Updated pending PRs workflow to check all repos the team owns.
27
+
28
+ * Changed the event type from `pull_request` to `pull_request_target` to allow for auto assign of PRs to the Node.js Engineering Board
29
+
30
+ * Fixed add to board workflow to properly pass repository secrets into reusable board workflow.
31
+
32
+ * Changes token used to post issues to org level project board
33
+
34
+ * Runs versioned tests for external modules against tests defined in the external repository instead of tests published in npm modules.
35
+
36
+ * Added a reusable workflow to automatically add issues to the Node.js Engineering Board when created.
37
+
38
+ * Added CI job to update system configurations with new agent version on release.
39
+
40
+ * Moved `methods.js` under bluebird versioned test folder.
41
+
1
42
  ### v8.6.0 (2021-11-17)
2
43
 
3
44
  * Added `onResolved` instrumentation hook to apply instrumentation prior to module load.
package/api.js CHANGED
@@ -14,7 +14,6 @@ const hashes = require('./lib/util/hashes')
14
14
  const properties = require('./lib/util/properties')
15
15
  const stringify = require('json-stringify-safe')
16
16
  const shimmer = require('./lib/shimmer')
17
- const shims = require('./lib/shim')
18
17
  const isValidType = require('./lib/util/attribute-types')
19
18
  const TransactionShim = require('./lib/shim/transaction-shim')
20
19
  const TransactionHandle = require('./lib/transaction/handle')
@@ -1332,34 +1331,16 @@ API.prototype.instrumentLoadedModule = function instrumentLoadedModule(moduleNam
1332
1331
  NAMES.SUPPORTABILITY.API + '/instrumentLoadedModule'
1333
1332
  )
1334
1333
  metric.incrementCallCount()
1335
- try {
1336
- const instrumentationName = shimmer.getInstrumentationNameFromModuleName(moduleName)
1337
- if (!shimmer.registeredInstrumentations[instrumentationName]) {
1338
- logger.warn("No instrumentation registered for '%s'.", instrumentationName)
1339
- return false
1340
- }
1341
-
1342
- const instrumentation = shimmer.registeredInstrumentations[instrumentationName]
1343
- if (!instrumentation.onRequire) {
1344
- logger.warn("No onRequire function registered for '%s'.", instrumentationName)
1345
- return false
1346
- }
1347
1334
 
1335
+ try {
1348
1336
  const resolvedName = require.resolve(moduleName)
1349
1337
 
1350
- const shim = shims.createShimFromType(
1351
- instrumentation.type,
1352
- this.agent,
1353
- moduleName,
1354
- resolvedName
1355
- )
1356
-
1357
- instrumentation.onRequire(shim, module, moduleName)
1358
-
1359
- return true
1338
+ return shimmer.instrumentPostLoad(this.agent, module, moduleName, resolvedName)
1360
1339
  } catch (error) {
1361
1340
  logger.error('instrumentLoadedModule encountered an error, module not instrumented: %s', error)
1362
1341
  }
1342
+
1343
+ return false
1363
1344
  }
1364
1345
 
1365
1346
  /**
@@ -229,6 +229,7 @@ Config.prototype.mergeServerConfig = mergeServerConfig
229
229
  * recognized, unsupported, and unknown parameters.
230
230
  *
231
231
  * @param {object} json The config blob sent by New Relic.
232
+ * @param recursion
232
233
  */
233
234
  Config.prototype.onConnect = function onConnect(json, recursion) {
234
235
  json = json || Object.create(null)
@@ -865,7 +866,7 @@ Config.prototype.checkAsyncHookStatus = function checkAsyncHookStatus() {
865
866
  * I wanted to cache the DisplayHost, but if I attached the variable to the config object,
866
867
  * it sends the extra variable to New Relic, which is not desired.
867
868
  *
868
- * @return {string} display host name
869
+ * @returns {string} display host name
869
870
  */
870
871
  Config.prototype.getDisplayHost = getDisplayHost
871
872
 
@@ -903,7 +904,7 @@ function getDisplayHost() {
903
904
  * I wanted to cache the Hostname, but if I attached the variable to the config object,
904
905
  * it sends the extra variable to New Relic, which is not desired.
905
906
  *
906
- * @return {string} host name
907
+ * @returns {string} host name
907
908
  */
908
909
  Config.prototype.getHostnameSafe = getHostnameSafe
909
910
 
@@ -980,6 +981,7 @@ Config.prototype.applications = function applications() {
980
981
  *
981
982
  * @param {object} external The configuration being loaded.
982
983
  * @param {object} internal Whichever chunk of the config being overridden.
984
+ * @param arbitrary
983
985
  */
984
986
  Config.prototype._fromPassed = function _fromPassed(external, internal, arbitrary) {
985
987
  if (!external) {
@@ -1065,6 +1067,8 @@ Config.prototype._featureFlagsFromEnv = function _featureFlagsFromEnv() {
1065
1067
  * need to set this yourself.
1066
1068
  * @param object data The current level of the configuration object. Should
1067
1069
  * never need to set this yourself.
1070
+ * @param metadata
1071
+ * @param data
1068
1072
  */
1069
1073
  Config.prototype._fromEnvironment = function _fromEnvironment(metadata, data) {
1070
1074
  if (!metadata) {
@@ -1122,7 +1126,6 @@ Config.prototype._fromEnvironment = function _fromEnvironment(metadata, data) {
1122
1126
  * environment variable.
1123
1127
  *
1124
1128
  * @param {*} inputConfig configuration passed to the Config constructor
1125
- *
1126
1129
  * @returns {boolean}
1127
1130
  */
1128
1131
  Config.prototype._loggingManuallySet = function _loggingManuallySet(inputConfig) {
@@ -1137,7 +1140,6 @@ Config.prototype._loggingManuallySet = function _loggingManuallySet(inputConfig)
1137
1140
  * file or enveironment variable
1138
1141
  *
1139
1142
  * @param {*} inputConfig configuration pass to the Config constructor
1140
- *
1141
1143
  * @returns {boolean}
1142
1144
  */
1143
1145
  Config.prototype._nativeMetricsManuallySet = function _nativeMetricsManuallySet(inputConfig) {
@@ -1156,7 +1158,6 @@ Config.prototype._nativeMetricsManuallySet = function _nativeMetricsManuallySet(
1156
1158
  * environment variable.
1157
1159
  *
1158
1160
  * @param {*} inputConfig configuration passed to the Config constructor
1159
- *
1160
1161
  * @returns {boolean}
1161
1162
  */
1162
1163
  Config.prototype._DTManuallySet = function _DTManuallySet(inputConfig) {
@@ -1173,6 +1174,7 @@ Config.prototype._DTManuallySet = function _DTManuallySet(inputConfig) {
1173
1174
  * - disables cross_application_tracer.enabled if set
1174
1175
  * - defaults logging to disabled
1175
1176
  * - verifies data specific to running DT is defined either in config file of env vars
1177
+ *
1176
1178
  * @param {*} inputConfig configuration passed to the Config constructor
1177
1179
  */
1178
1180
  Config.prototype._enforceServerless = function _enforceServerless(inputConfig) {
@@ -1282,7 +1284,7 @@ Config.prototype._canonicalize = function _canonicalize() {
1282
1284
  }
1283
1285
 
1284
1286
  const expectedCodes = this.error_collector && this.error_collector.expected_status_codes
1285
- if (statusCodes) {
1287
+ if (expectedCodes) {
1286
1288
  this.error_collector.expected_status_codes = _parseCodes(expectedCodes)
1287
1289
  }
1288
1290
 
@@ -1410,7 +1412,6 @@ Config.prototype._applyHighSecurity = function _applyHighSecurity() {
1410
1412
  *
1411
1413
  * @param {Agent} agent
1412
1414
  * @param {object} policies
1413
- *
1414
1415
  * @returns {CollectorResponse} The result of the processing, with the known
1415
1416
  * policies as the response payload.
1416
1417
  */
@@ -1540,7 +1541,7 @@ function redactValue(value) {
1540
1541
  * Get a JSONifiable object containing all settings we want to report to the
1541
1542
  * collector and store in the environment_values table.
1542
1543
  *
1543
- * @return Object containing simple key-value pairs of settings
1544
+ * @returns Object containing simple key-value pairs of settings
1544
1545
  */
1545
1546
  Config.prototype.publicSettings = function publicSettings() {
1546
1547
  let settings = Object.create(null)
@@ -1683,6 +1684,8 @@ function initialize(config) {
1683
1684
 
1684
1685
  /**
1685
1686
  * This helper function creates an empty configuration object
1687
+ *
1688
+ * @param config
1686
1689
  */
1687
1690
  function createNewConfigObject(config) {
1688
1691
  config = new Config(Object.create(null))
@@ -252,7 +252,11 @@ const INFINITE_TRACING = {
252
252
  }
253
253
 
254
254
  const FEATURES = {
255
- CERTIFICATES: SUPPORTABILITY.FEATURES + '/Certificates'
255
+ CERTIFICATES: SUPPORTABILITY.FEATURES + '/Certificates',
256
+ INSTRUMENTATION: {
257
+ ON_RESOLVED: SUPPORTABILITY.FEATURES + '/Instrumentation/OnResolved',
258
+ ON_REQUIRE: SUPPORTABILITY.FEATURES + '/Instrumentation/OnRequire'
259
+ }
256
260
  }
257
261
 
258
262
  module.exports = {
package/lib/shimmer.js CHANGED
@@ -6,12 +6,15 @@
6
6
  'use strict'
7
7
 
8
8
  const path = require('path')
9
+ const semver = require('semver')
9
10
  const fs = require('./util/unwrapped-core').fs
10
11
  const logger = require('./logger').child({ component: 'shimmer' })
11
12
  const INSTRUMENTATIONS = require('./instrumentations')()
12
13
  const properties = require('./util/properties')
13
14
  const shims = require('./shim')
14
15
 
16
+ const NAMES = require('./metrics/names')
17
+
15
18
  const MODULE_TYPE = shims.constants.MODULE_TYPE
16
19
 
17
20
  const CORE_INSTRUMENTATION = {
@@ -286,9 +289,13 @@ const shimmer = (module.exports = {
286
289
  // This is triggered by the load call, so record the path that has been seen so
287
290
  // we can examine it after the load call has returned.
288
291
  const resolvedFilepath = resolve.apply(this, arguments)
289
- filepathMap[file] = resolvedFilepath
290
292
 
291
- _onResolveFileName(agent, file, resolvedFilepath)
293
+ // Only fire the first time we see a specific module resolved
294
+ if (filepathMap[file] !== resolvedFilepath) {
295
+ filepathMap[file] = resolvedFilepath
296
+
297
+ _onResolveFileName(agent, file, resolvedFilepath)
298
+ }
292
299
 
293
300
  return resolvedFilepath
294
301
  }
@@ -458,6 +465,14 @@ const shimmer = (module.exports = {
458
465
  instrumentation = moduleName
459
466
  }
460
467
  return instrumentation
468
+ },
469
+
470
+ instrumentPostLoad(agent, module, moduleName, resolvedName) {
471
+ const result = _postLoad(agent, module, moduleName, resolvedName)
472
+ // This is to not break the public API
473
+ // previously it would just call instrumentation
474
+ // and not check the result
475
+ return !!result.__NR_instrumented
461
476
  }
462
477
  })
463
478
 
@@ -502,6 +517,7 @@ function instrumentPostLoad(agent, nodule, moduleName, resolvedName) {
502
517
  const shim = shims.createShimFromType(instrumentation.type, agent, moduleName, resolvedName)
503
518
 
504
519
  applyDebugState(shim, nodule)
520
+ trackInstrumentationUsage(agent, shim, moduleName, NAMES.FEATURES.INSTRUMENTATION.ON_REQUIRE)
505
521
 
506
522
  try {
507
523
  if (instrumentation.onRequire(shim, nodule, moduleName) !== false) {
@@ -597,6 +613,8 @@ function _instrumentOnResolved(agent, moduleName, resolvedFilepath) {
597
613
 
598
614
  const shim = shims.createShimFromType(instrumentation.type, agent, moduleName, resolvedFilepath)
599
615
 
616
+ trackInstrumentationUsage(agent, shim, moduleName, NAMES.FEATURES.INSTRUMENTATION.ON_RESOLVED)
617
+
600
618
  try {
601
619
  instrumentation.onResolved(shim, moduleName, resolvedFilepath)
602
620
  } catch (instrumentationError) {
@@ -644,3 +662,45 @@ function hasValidRegisterOptions(opts) {
644
662
 
645
663
  return true
646
664
  }
665
+
666
+ /**
667
+ * Adds metrics to indicate instrumentation was used for a particular module and
668
+ * what major version the module was at, if possible.
669
+ *
670
+ * @param {*} agent The agent instance.
671
+ * @param {*} shim The instance of the shim used to instrument the module.
672
+ * @param {string} moduleName The name of the required module.
673
+ * @param {string} metricPrefix Support metric prefix to prepend to the metrics. Will indicate onRequire or onResolved
674
+ * from NAMES.FEATURES.INSTRUMENTATION.
675
+ */
676
+ function trackInstrumentationUsage(agent, shim, moduleName, metricPrefix) {
677
+ try {
678
+ const version = tryGetVersion(shim)
679
+ const instrumentationMetricName = `${metricPrefix}/${moduleName}`
680
+
681
+ agent.metrics.getOrCreateMetric(instrumentationMetricName).incrementCallCount()
682
+
683
+ if (version) {
684
+ const majorVersion = semver.major(version)
685
+ const versionMetricName = `${instrumentationMetricName}/Version/${majorVersion}`
686
+
687
+ agent.metrics.getOrCreateMetric(versionMetricName).incrementCallCount()
688
+ }
689
+ } catch (error) {
690
+ logger.debug('Unable to track instrumentation usage for: ', moduleName, error)
691
+ }
692
+ }
693
+
694
+ function tryGetVersion(shim) {
695
+ // Global module (i.e. domain) or finding root failed
696
+ if (shim._moduleRoot === '.') {
697
+ return
698
+ }
699
+
700
+ const packageInfo = shim.require('./package.json')
701
+ if (!packageInfo) {
702
+ return
703
+ }
704
+
705
+ return packageInfo.version
706
+ }
@@ -45,16 +45,6 @@ Tracer.prototype.wrapFunctionFirst = wrapFunctionFirst
45
45
  Tracer.prototype.wrapSyncFunction = wrapSyncFunction
46
46
  Tracer.prototype.wrapCallback = wrapCallback
47
47
 
48
- Object.defineProperty(Tracer.prototype, 'segment', {
49
- get: function segmentGetter() {
50
- return this._contextManager.getContext()
51
- },
52
- set: function segmentSetter(segment) {
53
- this._contextManager.setContext(segment)
54
- return segment
55
- }
56
- })
57
-
58
48
  function getTransaction() {
59
49
  const currentSegment = this._contextManager.getContext()
60
50
  if (currentSegment && currentSegment.transaction && currentSegment.transaction.isActive()) {
@@ -69,6 +59,7 @@ function getSegment() {
69
59
  return this._contextManager.getContext()
70
60
  }
71
61
 
62
+ // TODO: Remove/replace external uses to tracer.getSpanContext()
72
63
  function getSpanContext() {
73
64
  const currentSegment = this.getSegment()
74
65
  return currentSegment && currentSegment.getSpanContext()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "newrelic",
3
- "version": "8.6.0",
3
+ "version": "8.7.0",
4
4
  "author": "New Relic Node.js agent team <nodejs@newrelic.com>",
5
5
  "license": "Apache-2.0",
6
6
  "contributors": [
@@ -148,10 +148,11 @@
148
148
  "update-cross-agent-tests": "./bin/update-cats.sh",
149
149
  "versioned-tests": "./bin/run-versioned-tests.sh",
150
150
  "update-changelog-version": "node ./bin/update-changelog-version",
151
+ "checkout-external-versioned": "node ./test/versioned-external/checkout-external-tests.js",
151
152
  "versioned": "npm run versioned:npm7",
152
- "versioned:major": "npm run prepare-test && VERSIONED_MODE=--major NPM7=1 time ./bin/run-versioned-tests.sh",
153
- "versioned:npm6": "npm run prepare-test && time ./bin/run-versioned-tests.sh",
154
- "versioned:npm7": "npm run prepare-test && NPM7=1 time ./bin/run-versioned-tests.sh",
153
+ "versioned:major": "npm run checkout-external-versioned && npm run prepare-test && VERSIONED_MODE=--major NPM7=1 time ./bin/run-versioned-tests.sh",
154
+ "versioned:npm6": "npm run checkout-external-versioned && npm run prepare-test && time ./bin/run-versioned-tests.sh",
155
+ "versioned:npm7": "npm run checkout-external-versioned && npm run prepare-test && NPM7=1 time ./bin/run-versioned-tests.sh",
155
156
  "prepare": "husky install"
156
157
  },
157
158
  "bin": {