newrelic 12.3.1 → 12.5.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,24 +1,67 @@
1
- ### v12.3.1 (2024-09-04)
2
-
3
- #### Bug fixes
4
-
5
- * Fixed detection of REST API type payloads in AWS Lambda ([#2543](https://github.com/newrelic/node-newrelic/pull/2543)) ([adfeebc](https://github.com/newrelic/node-newrelic/commit/adfeebc043161e0e0c35de2cf93989dbde9cb8fa))
6
-
7
- #### Documentation
8
-
9
- * Cleaned up formatting of api.js to properly inject example snippets when rendering on API docs site ([#2524](https://github.com/newrelic/node-newrelic/pull/2524)) ([4b34f3d](https://github.com/newrelic/node-newrelic/commit/4b34f3dbab45a55ec447b6e21b69c7621b41e539))
10
- * Updated compatibility report ([#2523](https://github.com/newrelic/node-newrelic/pull/2523)) ([29784ea](https://github.com/newrelic/node-newrelic/commit/29784ea766b2a9388c050f271ab7190895bc22ed))
11
- * Updated Next.js Otel cloud provider FAQ ([#2537](https://github.com/newrelic/node-newrelic/pull/2537)) ([6553807](https://github.com/newrelic/node-newrelic/commit/655380760a89193c5b6cd47d3955d1244cd79e7b))
12
-
13
- #### Tests
14
-
15
- * Converted db unit tests to node:test ([#2514](https://github.com/newrelic/node-newrelic/pull/2514)) ([bea4548](https://github.com/newrelic/node-newrelic/commit/bea45481a8a04099096929b36532203fbb8b6921))
16
- * Converted grpc, lib, and utilization tests to `node:test` ([#2532](https://github.com/newrelic/node-newrelic/pull/2532)) ([c207e1e](https://github.com/newrelic/node-newrelic/commit/c207e1e3de75a9c3a2c4a05fa1bc318d3e455ef9))
17
- * Replaced distributed tracing tests with `node:test` ([#2527](https://github.com/newrelic/node-newrelic/pull/2527)) ([8184c56](https://github.com/newrelic/node-newrelic/commit/8184c5676155b9028c84adc0da3902803ee9d107))
18
- * Added a match function for tests ([#2541](https://github.com/newrelic/node-newrelic/pull/2541)) ([51e7f34](https://github.com/newrelic/node-newrelic/commit/51e7f34e733202a9c2c024d9d9a7f3c207dfc4b0))
19
- * Converted `config` to `node:test` ([#2517](https://github.com/newrelic/node-newrelic/pull/2517)) ([1534a73](https://github.com/newrelic/node-newrelic/commit/1534a734995b6800c4cab3b6712f1b6b1329ed5e))
20
-
21
-
1
+ ### v12.5.0 (2024-09-12)
2
+
3
+ #### Features
4
+
5
+ * Added utilization info for ECS ([#2565](https://github.com/newrelic/node-newrelic/pull/2565)) ([6f92073](https://github.com/newrelic/node-newrelic/commit/6f92073a6c01124d8ab1b54d06c176a36fbc3441))
6
+
7
+ #### Bug fixes
8
+
9
+ * Ensured README displays for Azure site extension ([#2564](https://github.com/newrelic/node-newrelic/pull/2564)) ([a30aed5](https://github.com/newrelic/node-newrelic/commit/a30aed5cf31c0c89678618e51215063562331848))
10
+
11
+ #### Documentation
12
+
13
+ * Updated compatibility report ([#2562](https://github.com/newrelic/node-newrelic/pull/2562)) ([8f7aebe](https://github.com/newrelic/node-newrelic/commit/8f7aebe7e4274ce45cfe961537a09b34077b3aa0))
14
+
15
+ #### Tests
16
+
17
+ * Convert `metric` and `metrics-recorder` tests to `node:test` ([#2552](https://github.com/newrelic/node-newrelic/pull/2552)) ([7ae4af4](https://github.com/newrelic/node-newrelic/commit/7ae4af4c8adfabadd3c865bd2fdd0e8ba5317eef))
18
+ * Updated `serverless` unit tests to `node:test` ([#2549](https://github.com/newrelic/node-newrelic/pull/2549)) ([619f23c](https://github.com/newrelic/node-newrelic/commit/619f23c938bf39c360a6da9a307c178986c70902))
19
+
20
+ ### v12.4.0 (2024-09-11)
21
+
22
+ #### Features
23
+
24
+ * Added support for `express@5` ([#2555](https://github.com/newrelic/node-newrelic/pull/2555)) ([252f3b2](https://github.com/newrelic/node-newrelic/commit/252f3b2bc1206dad52d914b98a2352da317da2d5))
25
+ * Provided ability to disable instrumentation for a 3rd party package ([#2551](https://github.com/newrelic/node-newrelic/pull/2551)) ([abfb9f0](https://github.com/newrelic/node-newrelic/commit/abfb9f029a4f6c25966c35d3284ddae0d46dfecb))
26
+ * To disable instrumentation set `config.instrumentation.<library>.enabled` to false. The values of `<library>` are the keys listed [here](https://github.com/newrelic/node-newrelic/blob/main/lib/instrumentations.js)
27
+ * This feature is use at your own risk. Disabling instrumentation for a library could affect instrumentation of other libraries executed afterwards.
28
+
29
+
30
+ #### Miscellaneous chores
31
+
32
+ * Added CI for publishing agent as Azure site extension ([#2488](https://github.com/newrelic/node-newrelic/pull/2488)) ([468943a](https://github.com/newrelic/node-newrelic/commit/468943a1ed3864dafb93a2f96561d1a778d03a5f))
33
+ * Added Azure site extension installation scripts ([#2448](https://github.com/newrelic/node-newrelic/pull/2448)) ([a56c4e1](https://github.com/newrelic/node-newrelic/commit/a56c4e146ead7d3205fead1f17afad0ea7a77e59))
34
+
35
+ #### Tests
36
+
37
+ * Converted `llm-events` tests to use `node:test` ([#2535](https://github.com/newrelic/node-newrelic/pull/2535)) ([ebfa2e9](https://github.com/newrelic/node-newrelic/commit/ebfa2e9ab8ecbe4bc9adaddd3e4a60e3ba84d0d9))
38
+ * Migrated `test/unit/spans` to use `node:test` ([#2556](https://github.com/newrelic/node-newrelic/pull/2556)) ([9319071](https://github.com/newrelic/node-newrelic/commit/931907182b0168990a04bb92c2f28310450f8ba0))
39
+ * Migrated `test/unit/util` to use `node:test` ([#2546](https://github.com/newrelic/node-newrelic/pull/2546)) ([0b07be8](https://github.com/newrelic/node-newrelic/commit/0b07be8f7f29e67630326c73b96faa5e20527a0b))
40
+ * Migrated tests in `test/unit/instrumentation` to use `node:test` ([#2531](https://github.com/newrelic/node-newrelic/pull/2531)) ([47b8398](https://github.com/newrelic/node-newrelic/commit/47b8398820d665a85a96ae84e30eaaf20564dcf8))
41
+ * Converted `collector` unit tests to `node:test` ([#2510](https://github.com/newrelic/node-newrelic/pull/2510)) ([762511b](https://github.com/newrelic/node-newrelic/commit/762511be524f971a609ff45c111c2d1a89ec1c46))
42
+ * Converted `errors` unit tests to `node:test` ([#2540](https://github.com/newrelic/node-newrelic/pull/2540)) ([ae82760](https://github.com/newrelic/node-newrelic/commit/ae82760f7001f6bcdd6a9fe0ec1e96dc60db99e5))
43
+
44
+ ### v12.3.1 (2024-09-04)
45
+
46
+ #### Bug fixes
47
+
48
+ * Fixed detection of REST API type payloads in AWS Lambda ([#2543](https://github.com/newrelic/node-newrelic/pull/2543)) ([adfeebc](https://github.com/newrelic/node-newrelic/commit/adfeebc043161e0e0c35de2cf93989dbde9cb8fa))
49
+
50
+ #### Documentation
51
+
52
+ * Cleaned up formatting of api.js to properly inject example snippets when rendering on API docs site ([#2524](https://github.com/newrelic/node-newrelic/pull/2524)) ([4b34f3d](https://github.com/newrelic/node-newrelic/commit/4b34f3dbab45a55ec447b6e21b69c7621b41e539))
53
+ * Updated compatibility report ([#2523](https://github.com/newrelic/node-newrelic/pull/2523)) ([29784ea](https://github.com/newrelic/node-newrelic/commit/29784ea766b2a9388c050f271ab7190895bc22ed))
54
+ * Updated Next.js Otel cloud provider FAQ ([#2537](https://github.com/newrelic/node-newrelic/pull/2537)) ([6553807](https://github.com/newrelic/node-newrelic/commit/655380760a89193c5b6cd47d3955d1244cd79e7b))
55
+
56
+ #### Tests
57
+
58
+ * Converted db unit tests to node:test ([#2514](https://github.com/newrelic/node-newrelic/pull/2514)) ([bea4548](https://github.com/newrelic/node-newrelic/commit/bea45481a8a04099096929b36532203fbb8b6921))
59
+ * Converted grpc, lib, and utilization tests to `node:test` ([#2532](https://github.com/newrelic/node-newrelic/pull/2532)) ([c207e1e](https://github.com/newrelic/node-newrelic/commit/c207e1e3de75a9c3a2c4a05fa1bc318d3e455ef9))
60
+ * Replaced distributed tracing tests with `node:test` ([#2527](https://github.com/newrelic/node-newrelic/pull/2527)) ([8184c56](https://github.com/newrelic/node-newrelic/commit/8184c5676155b9028c84adc0da3902803ee9d107))
61
+ * Added a match function for tests ([#2541](https://github.com/newrelic/node-newrelic/pull/2541)) ([51e7f34](https://github.com/newrelic/node-newrelic/commit/51e7f34e733202a9c2c024d9d9a7f3c207dfc4b0))
62
+ * Converted `config` to `node:test` ([#2517](https://github.com/newrelic/node-newrelic/pull/2517)) ([1534a73](https://github.com/newrelic/node-newrelic/commit/1534a734995b6800c4cab3b6712f1b6b1329ed5e))
63
+
64
+
22
65
  ### v12.3.0 (2024-08-27)
23
66
 
24
67
  #### Features
@@ -34,6 +34,7 @@ code, the source code can be found at [https://github.com/newrelic/node-newrelic
34
34
  * [@aws-sdk/client-s3](#aws-sdkclient-s3)
35
35
  * [@aws-sdk/s3-request-presigner](#aws-sdks3-request-presigner)
36
36
  * [@koa/router](#koarouter)
37
+ * [@matteo.collina/tspl](#matteocollinatspl)
37
38
  * [@newrelic/eslint-config](#newreliceslint-config)
38
39
  * [@newrelic/newrelic-oss-cli](#newrelicnewrelic-oss-cli)
39
40
  * [@newrelic/test-utilities](#newrelictest-utilities)
@@ -69,7 +70,6 @@ code, the source code can be found at [https://github.com/newrelic/node-newrelic
69
70
  * [nock](#nock)
70
71
  * [proxy](#proxy)
71
72
  * [proxyquire](#proxyquire)
72
- * [rfdc](#rfdc)
73
73
  * [rimraf](#rimraf)
74
74
  * [self-cert](#self-cert)
75
75
  * [should](#should)
@@ -1521,6 +1521,35 @@ THE SOFTWARE.
1521
1521
 
1522
1522
  ```
1523
1523
 
1524
+ ### @matteo.collina/tspl
1525
+
1526
+ This product includes source derived from [@matteo.collina/tspl](https://github.com/mcollina/tspl) ([v0.1.1](https://github.com/mcollina/tspl/tree/v0.1.1)), distributed under the [MIT License](https://github.com/mcollina/tspl/blob/v0.1.1/LICENSE):
1527
+
1528
+ ```
1529
+ MIT License
1530
+
1531
+ Copyright (c) 2023 Matteo Collina
1532
+
1533
+ Permission is hereby granted, free of charge, to any person obtaining a copy
1534
+ of this software and associated documentation files (the "Software"), to deal
1535
+ in the Software without restriction, including without limitation the rights
1536
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1537
+ copies of the Software, and to permit persons to whom the Software is
1538
+ furnished to do so, subject to the following conditions:
1539
+
1540
+ The above copyright notice and this permission notice shall be included in all
1541
+ copies or substantial portions of the Software.
1542
+
1543
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1544
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1545
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1546
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1547
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1548
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1549
+ SOFTWARE.
1550
+
1551
+ ```
1552
+
1524
1553
  ### @newrelic/eslint-config
1525
1554
 
1526
1555
  This product includes source derived from [@newrelic/eslint-config](https://github.com/newrelic/eslint-config-newrelic) ([v0.3.0](https://github.com/newrelic/eslint-config-newrelic/tree/v0.3.0)), distributed under the [Apache-2.0 License](https://github.com/newrelic/eslint-config-newrelic/blob/v0.3.0/LICENSE):
@@ -3978,29 +4007,6 @@ OTHER DEALINGS IN THE SOFTWARE.
3978
4007
 
3979
4008
  ```
3980
4009
 
3981
- ### rfdc
3982
-
3983
- This product includes source derived from [rfdc](https://github.com/davidmarkclements/rfdc) ([v1.4.1](https://github.com/davidmarkclements/rfdc/tree/v1.4.1)), distributed under the [MIT License](https://github.com/davidmarkclements/rfdc/blob/v1.4.1/LICENSE):
3984
-
3985
- ```
3986
- Copyright 2019 "David Mark Clements <david.mark.clements@gmail.com>"
3987
-
3988
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
3989
- documentation files (the "Software"), to deal in the Software without restriction, including without limitation
3990
- the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
3991
- to permit persons to whom the Software is furnished to do so, subject to the following conditions:
3992
-
3993
- The above copyright notice and this permission notice shall be included in all copies or substantial portions
3994
- of the Software.
3995
-
3996
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
3997
- TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
3998
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
3999
- CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
4000
- IN THE SOFTWARE.
4001
-
4002
- ```
4003
-
4004
4010
  ### rimraf
4005
4011
 
4006
4012
  This product includes source derived from [rimraf](https://github.com/isaacs/rimraf) ([v2.7.1](https://github.com/isaacs/rimraf/tree/v2.7.1)), distributed under the [ISC License](https://github.com/isaacs/rimraf/blob/v2.7.1/LICENSE):
package/lib/agent.js CHANGED
@@ -57,6 +57,94 @@ const MAX_ERROR_TRACES_DEFAULT = 20
57
57
  const INITIAL_HARVEST_DELAY_MS = 1000
58
58
  const DEFAULT_HARVEST_INTERVAL_MS = 60000
59
59
 
60
+ /**
61
+ * Indicates that the agent has finished connecting. It is preceded by
62
+ * {@link Agent#event:connecting}.
63
+ *
64
+ * @event Agent#connected
65
+ */
66
+
67
+ /**
68
+ * Indicates that the agent is in the connecting state. That is, it is
69
+ * communicating with the New Relic data collector and performing requisite
70
+ * operations before the agent is ready to collect and send data.
71
+ *
72
+ * @event Agent#connecting
73
+ */
74
+
75
+ /**
76
+ * Indicates that the agent has terminated, or lost, its connection to the
77
+ * New Relic data collector.
78
+ *
79
+ * @event Agent#disconnected
80
+ */
81
+
82
+ /**
83
+ * Indicates that the agent has encountered, or generated, some error that
84
+ * prevents it from operating correctly. The agent state will be set to
85
+ * "errored."
86
+ *
87
+ * @event Agent#errored
88
+ */
89
+
90
+ /**
91
+ * Indicates that the synchronous harvest cycle has completed. This will be
92
+ * prefaced by the {@link Agent#event:harvestStarted} event. It is only fired
93
+ * in a serverless context.
94
+ *
95
+ * @event Agent#harvestFinished
96
+ */
97
+
98
+ /**
99
+ * Indicates that a synchronous harvest cycle (collecting data from the various
100
+ * aggregators and sending said data to the New Relic data collector) has
101
+ * started. This is only fired in a serverless context.
102
+ *
103
+ * @event Agent#harvestStarted
104
+ */
105
+
106
+ /**
107
+ * Indicates that the agent state has entered the "started" state. That is,
108
+ * the agent has finished bootstrapping and is collecting and sending data.
109
+ *
110
+ * @event Agent#started
111
+ */
112
+
113
+ /**
114
+ * Indicates that the agent is starting. This is typically the first event
115
+ * emitted by the agent.
116
+ *
117
+ * @event Agent#starting
118
+ */
119
+
120
+ /**
121
+ * Indicates that the agent state has changed to the "stopped" state. That is,
122
+ * the agent is no longer collecting or sending data.
123
+ *
124
+ * @event Agent#stopped
125
+ */
126
+
127
+ /**
128
+ * Indicates that the agent is entering its shutdown process.
129
+ *
130
+ * @event Agent#stopping
131
+ */
132
+
133
+ /**
134
+ * Indicates that the transaction has begun recording data.
135
+ *
136
+ * @event Agent#transactionStarted
137
+ * @param {Transaction} currentTransaction
138
+ */
139
+
140
+ /**
141
+ * Indicates that the transaction has stopped recording data and has been
142
+ * closed.
143
+ *
144
+ * @event Agent#transactionFinished
145
+ * @param {Transaction} currentTransaction
146
+ */
147
+
60
148
  /**
61
149
  * There's a lot of stuff in this constructor, due to Agent acting as the
62
150
  * orchestrator for New Relic within instrumented applications.
@@ -101,7 +189,7 @@ function Agent(config) {
101
189
  this.harvester
102
190
  )
103
191
 
104
- this.metrics.on('starting metric_data data send.', this._beforeMetricDataSend.bind(this))
192
+ this.metrics.on('starting_data_send-metric_data', this._beforeMetricDataSend.bind(this))
105
193
 
106
194
  this.spanEventAggregator = createSpanEventAggregator(config, this)
107
195
 
@@ -226,6 +314,12 @@ util.inherits(Agent, EventEmitter)
226
314
  *
227
315
  * @param {Function} callback Continuation and error handler.
228
316
  * @returns {void}
317
+ *
318
+ * @fires Agent#errored When configuration is not sufficient for operations or
319
+ * cannot establish a connection to the data collector.
320
+ * @fires Agent#starting
321
+ * @fires Agent#stopped When configuration indicates that the agent should be
322
+ * disabled.
229
323
  */
230
324
  Agent.prototype.start = function start(callback) {
231
325
  if (!callback) {
@@ -306,6 +400,8 @@ Agent.prototype.startStreaming = function startStreaming() {
306
400
  *
307
401
  * @param {boolean} shouldImmediatelyHarvest Whether we should immediately schedule a harvest, or wait a cycle
308
402
  * @param {Function} callback callback function that executes after harvest completes (now if immediate, otherwise later)
403
+ *
404
+ * @fires Agent#started
309
405
  */
310
406
  Agent.prototype.onConnect = function onConnect(shouldImmediatelyHarvest, callback) {
311
407
  this.harvester.update(this.config)
@@ -368,6 +464,11 @@ Agent.prototype._serverlessModeStart = function _serverlessModeStart(callback) {
368
464
  * current instrumentation and patch to the module loader.
369
465
  *
370
466
  * @param {Function} callback callback function to invoke after agent stop
467
+ *
468
+ * @fires Agent#errored When disconnecting from the data collector has resulted
469
+ * in some error.
470
+ * @fires Agent#stopped
471
+ * @fires Agent#stopping
371
472
  */
372
473
  Agent.prototype.stop = function stop(callback) {
373
474
  if (!callback) {
@@ -432,9 +533,13 @@ Agent.prototype._resetCustomEvents = function resetCustomEvents() {
432
533
  }
433
534
 
434
535
  /**
435
- * This method invokes a harvest synchronously.
536
+ * This method invokes a harvest synchronously, i.e. sends all data to the
537
+ * New Relic collector in a blocking fashion.
538
+ *
539
+ * NOTE: this doesn't currently work outside serverless mode.
436
540
  *
437
- * NOTE: this doesn't currently work outside of serverless mode.
541
+ * @fires Agent#harvestStarted
542
+ * @fires Agent#harvestFinished
438
543
  */
439
544
  Agent.prototype.harvestSync = function harvestSync() {
440
545
  logger.trace('Peparing to harvest.')
@@ -518,6 +623,15 @@ Agent.prototype.reconfigure = function reconfigure(configuration) {
518
623
  * creation of Transactions.
519
624
  *
520
625
  * @param {string} newState The new state of the agent.
626
+ *
627
+ * @fires Agent#connected
628
+ * @fires Agent#connecting
629
+ * @fires Agent#disconnected
630
+ * @fires Agent#errored
631
+ * @fires Agent#started
632
+ * @fires Agent#starting
633
+ * @fires Agent#stopped
634
+ * @fires Agent#stopping
521
635
  */
522
636
  Agent.prototype.setState = function setState(newState) {
523
637
  if (!STATES.hasOwnProperty(newState)) {
@@ -8,6 +8,152 @@
8
8
  const EventEmitter = require('events').EventEmitter
9
9
  const logger = require('../logger').child({ component: 'base_aggregator' })
10
10
 
11
+ /**
12
+ * Triggered when the aggregator has finished sending data to the
13
+ * `analytic_event_data` collector endpoint.
14
+ *
15
+ * @event Aggregator#finished_data_send-analytic_event_data
16
+ */
17
+
18
+ /**
19
+ * Triggered when an aggregator is sending data to the `analytic_event_data`
20
+ * collector endpoint.
21
+ *
22
+ * @event Aggregator#starting_data_send-analytic_event_data
23
+ */
24
+
25
+ /**
26
+ * Triggered when the aggregator has finished sending data to the
27
+ * `custom_event_data` collector endpoint.
28
+ *
29
+ * @event Aggregator#finished_data_send-custom_event_data
30
+ */
31
+
32
+ /**
33
+ * Triggered when an aggregator is sending data to the `custom_event_data`
34
+ * collector endpoint.
35
+ *
36
+ * @event Aggregator#starting_data_send-custom_event_data
37
+ */
38
+
39
+ /**
40
+ * Triggered when the aggregator has finished sending data to the
41
+ * `error_data` collector endpoint.
42
+ *
43
+ * @event Aggregator#finished_data_send-error_data
44
+ */
45
+
46
+ /**
47
+ * Triggered when an aggregator is sending data to the `error_data`
48
+ * collector endpoint.
49
+ *
50
+ * @event Aggregator#starting_data_send-error_data
51
+ */
52
+
53
+ /**
54
+ * Triggered when the aggregator has finished sending data to the
55
+ * `error_event_data` collector endpoint.
56
+ *
57
+ * @event Aggregator#finished_data_send-error_event_data
58
+ */
59
+
60
+ /**
61
+ * Triggered when an aggregator is sending data to the `error_event_data`
62
+ * collector endpoint.
63
+ *
64
+ * @event Aggregator#starting_data_send-error_event_data
65
+ */
66
+
67
+ /**
68
+ * Triggered when the aggregator has finished sending data to the
69
+ * `log_event_data` collector endpoint.
70
+ *
71
+ * @event Aggregator#finished_data_send-log_event_data
72
+ */
73
+
74
+ /**
75
+ * Triggered when an aggregator is sending data to the `log_event_data`
76
+ * collector endpoint.
77
+ *
78
+ * @event Aggregator#starting_data_send-log_event_data
79
+ */
80
+
81
+ /**
82
+ * Triggered when the aggregator has finished sending data to the
83
+ * `metric_data` collector endpoint.
84
+ *
85
+ * @event Aggregator#finished_data_send-metric_data
86
+ */
87
+
88
+ /**
89
+ * Triggered when an aggregator is sending data to the `metric_data`
90
+ * collector endpoint.
91
+ *
92
+ * @event Aggregator#starting_data_send-metric_data
93
+ */
94
+
95
+ /**
96
+ * Triggered when the aggregator has finished sending data to the
97
+ * `span_event_data` collector endpoint.
98
+ *
99
+ * @event Aggregator#finished_data_send-span_event_data
100
+ */
101
+
102
+ /**
103
+ * Triggered when an aggregator is sending data to the `span_event_data`
104
+ * collector endpoint.
105
+ *
106
+ * @event Aggregator#starting_data_send-span_event_data
107
+ */
108
+
109
+ /**
110
+ * Triggered when the aggregator has finished sending data to the
111
+ * `sql_trace_data` collector endpoint.
112
+ *
113
+ * @event Aggregator#finished_data_send-sql_trace_data
114
+ */
115
+
116
+ /**
117
+ * Triggered when an aggregator is sending data to the `sql_trace_data`
118
+ * collector endpoint.
119
+ *
120
+ * @event Aggregator#starting_data_send-sql_trace_data
121
+ */
122
+
123
+ /**
124
+ * Triggered when the aggregator has finished sending data to the
125
+ * `transaction_sample_data` collector endpoint.
126
+ *
127
+ * @event Aggregator#finished_data_send-transaction_sample_data
128
+ */
129
+
130
+ /**
131
+ * Triggered when an aggregator is sending data to the `transaction_sample_data`
132
+ * collector endpoint.
133
+ *
134
+ * @event Aggregator#starting_data_send-transaction_sample_data
135
+ */
136
+
137
+ /**
138
+ * Baseline data aggregator that is used to ship data to the New Relic
139
+ * data collector. Specific data aggregators, e.g. an errors aggregator,
140
+ * extend this base object.
141
+ *
142
+ * Aggregators fire of several events. The events are named according to the
143
+ * pattern `<finished|starting>_data_send-<endpoint_name>`. As an example,
144
+ * if the aggregator is collecting data to send to the `error_event_data`
145
+ * endpoint, there will be two events:
146
+ *
147
+ * + `starting_data_send-error_event_data`
148
+ * + `finished_data_send-error_event_data`
149
+ *
150
+ * For a list of possible endpoints, see
151
+ * {@link https://source.datanerd.us/agents/agent-specs/tree/main/endpoints/protocol-version-17}.
152
+ *
153
+ * Note: effort has been made to document the events for every endpoint this
154
+ * agent interacts with, but due to the dynamic nature of the event names we
155
+ * may have missed some.
156
+ */
11
157
  class Aggregator extends EventEmitter {
12
158
  constructor(opts, collector, harvester) {
13
159
  super()
@@ -23,6 +169,16 @@ class Aggregator extends EventEmitter {
23
169
  return true
24
170
  }
25
171
  this.enabled = this.isEnabled(opts.config)
172
+
173
+ /**
174
+ * The name of the collector endpoint that the
175
+ * aggregator will communicate with.
176
+ *
177
+ * @see https://source.datanerd.us/agents/agent-specs/tree/main/endpoints/protocol-version-17
178
+ *
179
+ * @type {string}
180
+ * @memberof Aggregator
181
+ */
26
182
  this.method = opts.method
27
183
  this.collector = collector
28
184
  this.sendTimer = null
@@ -90,7 +246,7 @@ class Aggregator extends EventEmitter {
90
246
  _runSend(data, payload) {
91
247
  if (!payload) {
92
248
  this._afterSend(false)
93
- this.emit(`finished ${this.method} data send.`)
249
+ this.emit(`finished_data_send-${this.method}`)
94
250
  return
95
251
  }
96
252
 
@@ -102,13 +258,36 @@ class Aggregator extends EventEmitter {
102
258
 
103
259
  // TODO: Log?
104
260
  this._afterSend(true)
105
- this.emit(`finished ${this.method} data send.`)
261
+ this.emit(`finished_data_send-${this.method}`)
106
262
  })
107
263
  }
108
264
 
265
+ /**
266
+ * Serialize all collected data and ship it off to the New Relic data
267
+ * collector. The target endpoint is defined by {@link Aggregator#method}.
268
+ *
269
+ * @fires Aggregator#finished_data_send-analytic_event_data
270
+ * @fires Aggregator#starting_data_send-analytic_event_data
271
+ * @fires Aggregator#finished_data_send-custom_event_data
272
+ * @fires Aggregator#starting_data_send-custom_event_data
273
+ * @fires Aggregator#finished_data_send-error_data
274
+ * @fires Aggregator#starting_data_send-error_data
275
+ * @fires Aggregator#finished_data_send-error_event_data
276
+ * @fires Aggregator#starting_data_send-error_event_data
277
+ * @fires Aggregator#finished_data_send-log_event_data
278
+ * @fires Aggregator#starting_data_send-log_event_data
279
+ * @fires Aggregator#finished_data_send-metric_data
280
+ * @fires Aggregator#starting_data_send-metric_data
281
+ * @fires Aggregator#finished_data_send-span_event_data
282
+ * @fires Aggregator#starting_data_send-span_event_data
283
+ * @fires Aggregator#finished_data_send-sql_trace_data
284
+ * @fires Aggregator#starting_data_send-sql_trace_data
285
+ * @fires Aggregator#finished_data_send-transaction_sample_data
286
+ * @fires Aggregator#starting_data_send-transaction_sample_data
287
+ */
109
288
  send() {
110
289
  logger.debug(`${this.method} Aggregator data send.`)
111
- this.emit(`starting ${this.method} data send.`)
290
+ this.emit(`starting_data_send-${this.method}`)
112
291
 
113
292
  const data = this._getMergeData()
114
293
  if (this.isAsync) {
@@ -30,9 +30,29 @@ const BACKOFFS = [
30
30
 
31
31
  // Expected collector response codes
32
32
  const SUCCESS = new Set([200, 202])
33
- const RESTART = new Set([401, 409])
34
- const FAILURE_SAVE_DATA = new Set([408, 429, 500, 503])
35
- const FAILURE_DISCARD_DATA = new Set([400, 403, 404, 405, 407, 411, 413, 414, 415, 417, 431])
33
+ const RESTART = new Set([
34
+ 401, // Authentication failed.
35
+ 409 // NR says to reconnect for some reason.
36
+ ])
37
+ const FAILURE_SAVE_DATA = new Set([
38
+ 408, // Data took too long to reach NR.
39
+ 429, // Too many requests being received by NR, rate limited.
40
+ 500, // NR server went boom.
41
+ 503 // NR server is not available.
42
+ ])
43
+ const FAILURE_DISCARD_DATA = new Set([
44
+ 400, // Format of the request is incorrect.
45
+ 403, // Not entitled to perform the action.
46
+ 404, // Sending to wrong destination.
47
+ 405, // Using the wrong HTTP method (e.g. PUT instead of POST).
48
+ 407, // Proxy authentication misconfigured.
49
+ 411, // No Content-Length header provided, or value is incorrect.
50
+ 413, // Payload is too large.
51
+ 414, // URI exceeds allowed length.
52
+ 415, // Content-type or Content-encoding values are incorrect.
53
+ 417, // NR cannot meet the expectation of the request.
54
+ 431 // Request headers exceed size limit.
55
+ ])
36
56
 
37
57
  const AGENT_RUN_BEHAVIOR = CollectorResponse.AGENT_RUN_BEHAVIOR
38
58
 
@@ -130,6 +150,17 @@ CollectorAPI.prototype._updateEndpoints = function _updateEndpoints(endpoint) {
130
150
  }
131
151
  }
132
152
 
153
+ /**
154
+ * Connect to the data collector.
155
+ *
156
+ * @param {function} callback A typical error first callback to be invoked
157
+ * upon successful or unsuccessful connection. The second parameter will be
158
+ * an instance of {@link CollectorResponse}.
159
+ *
160
+ * @fires Agent#connected By way of the full connection process. This event
161
+ * is not fired directly in this method.
162
+ * @fires Agent#connecting
163
+ */
133
164
  CollectorAPI.prototype.connect = function connect(callback) {
134
165
  if (!callback) {
135
166
  this._throwCallbackError()
@@ -347,6 +378,8 @@ CollectorAPI.prototype._connect = function _connect(env, callback) {
347
378
  * @param {Function} callback function to run after processing response
348
379
  * @param {Error} error collector response error
349
380
  * @param {http.ServerOptions} res collector response
381
+ *
382
+ * @fires Agent#connected
350
383
  */
351
384
  CollectorAPI.prototype._onConnect = function _onConnect(callback, error, res) {
352
385
  const agent = this._agent
@@ -431,6 +464,8 @@ CollectorAPI.prototype.shutdown = function shutdown(callback) {
431
464
  /**
432
465
  * @param {Error} error response error
433
466
  * @param {http.ServerResponse} response response from collector
467
+ *
468
+ * @fires Agent#disconnected
434
469
  */
435
470
  function onShutdown(error, response) {
436
471
  if (error) {
@@ -0,0 +1,19 @@
1
+ /*
2
+ * Copyright 2024 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ 'use strict'
7
+ const { boolean } = require('./formatters')
8
+ const instrumentedLibraries = require('../instrumentations')()
9
+ const pkgNames = Object.keys(instrumentedLibraries)
10
+
11
+ /**
12
+ * Builds the stanza for config.instrumentation.*
13
+ * It defaults every library to true and assigns a boolean
14
+ * formatter for the environment variable conversion of the values
15
+ */
16
+ module.exports = pkgNames.reduce((config, pkg) => {
17
+ config[pkg] = { enabled: { formatter: boolean, default: true } }
18
+ return config
19
+ }, {})
@@ -7,6 +7,7 @@
7
7
 
8
8
  const defaultConfig = module.exports
9
9
  const { array, int, float, boolean, object, objectList, allowList, regex } = require('./formatters')
10
+ const pkgInstrumentation = require('./build-instrumentation-config')
10
11
 
11
12
  /**
12
13
  * A function that returns the definition of the agent configuration
@@ -1382,7 +1383,13 @@ defaultConfig.definition = () => ({
1382
1383
  default: true
1383
1384
  }
1384
1385
  }
1385
- }
1386
+ },
1387
+ /**
1388
+ * Stanza that contains all keys to disable 3rd party package instrumentation(i.e. mongodb, pg, redis, etc)
1389
+ * Note: Disabling a given 3rd party library may affect the instrumentation of 3rd party libraries used after
1390
+ * the disabled library.
1391
+ */
1392
+ instrumentation: pkgInstrumentation
1386
1393
  })
1387
1394
 
1388
1395
  /**
@@ -70,6 +70,32 @@ function _findConfigFile() {
70
70
  return configFileCandidates.find(exists)
71
71
  }
72
72
 
73
+ /**
74
+ * Indicates if the agent should be enabled or disabled based upon the
75
+ * processed configuration.
76
+ *
77
+ * @event Config#agent_enabled
78
+ * @param {boolean} indication
79
+ */
80
+
81
+ /**
82
+ * Indicates that the configuration has changed due to some external factor,
83
+ * e.g. the configuration from the New Relic server has overridden some
84
+ * local setting.
85
+ *
86
+ * @event Config#change
87
+ * @param {Config} currentConfig
88
+ */
89
+
90
+ /**
91
+ * Parses and manages the agent configuration.
92
+ *
93
+ * @param {object} config Configuration object as read from a `newrelic.js`
94
+ * file. This is used as the baseline, which is the overridden by environment
95
+ * variables and feature flags.
96
+ *
97
+ * @class
98
+ */
73
99
  function Config(config) {
74
100
  EventEmitter.call(this)
75
101
 
@@ -194,6 +220,10 @@ Config.prototype.mergeServerConfig = mergeServerConfig
194
220
  *
195
221
  * @param {object} json The config blob sent by New Relic.
196
222
  * @param {boolean} recursion flag indicating coming from server side config
223
+ *
224
+ * @fires Config#agent_enabled When there is a conflict between the local
225
+ * setting of high security mode and what the New Relic server has returned.
226
+ * @fires Config#change
197
227
  */
198
228
  Config.prototype.onConnect = function onConnect(json, recursion) {
199
229
  json = json || Object.create(null)
@@ -274,6 +304,8 @@ Config.prototype._updateHarvestConfig = function _updateHarvestConfig(serverConf
274
304
  *
275
305
  * @param {object} params A configuration dictionary.
276
306
  * @param {string} key The particular configuration parameter to set.
307
+ *
308
+ * @fires Config#change
277
309
  */
278
310
  Config.prototype._fromServer = function _fromServer(params, key) {
279
311
  /* eslint-disable-next-line sonarjs/max-switch-cases */
@@ -839,7 +871,7 @@ Config.prototype.logUnknown = function logUnknown(json, key) {
839
871
  /**
840
872
  * Gets the user set host display name. If not provided, it returns the default value.
841
873
  *
842
- * This function is written is this strange way because of the use of caching variables.
874
+ * This function is written in this strange way because of the use of caching variables.
843
875
  * I wanted to cache the DisplayHost, but if I attached the variable to the config object,
844
876
  * it sends the extra variable to New Relic, which is not desired.
845
877
  *
@@ -35,7 +35,7 @@ class ErrorCollector {
35
35
  this.seenObjectsByTransaction = Object.create(null)
36
36
  this.seenStringsByTransaction = Object.create(null)
37
37
 
38
- this.traceAggregator.on('starting error_data data send.', this._onSendErrorTrace.bind(this))
38
+ this.traceAggregator.on('starting_data_send-error_data', this._onSendErrorTrace.bind(this))
39
39
 
40
40
  this.errorGroupCallback = null
41
41
  }
package/lib/harvester.js CHANGED
@@ -37,7 +37,7 @@ module.exports = class Harvester {
37
37
  this.aggregators.map((aggregator) => {
38
38
  return new Promise((resolve) => {
39
39
  if (aggregator.enabled) {
40
- aggregator.once(`finished ${aggregator.method} data send.`, function finish() {
40
+ aggregator.once(`finished_data_send-${aggregator.method}`, function finish() {
41
41
  resolve()
42
42
  })
43
43
 
@@ -204,11 +204,12 @@ const HAPI = {
204
204
 
205
205
  const UTILIZATION = {
206
206
  AWS_ERROR: SUPPORTABILITY.UTILIZATION + '/aws/error',
207
- PCF_ERROR: SUPPORTABILITY.UTILIZATION + '/pcf/error',
208
207
  AZURE_ERROR: SUPPORTABILITY.UTILIZATION + '/azure/error',
209
- GCP_ERROR: SUPPORTABILITY.UTILIZATION + '/gcp/error',
208
+ BOOT_ID_ERROR: SUPPORTABILITY.UTILIZATION + '/boot_id/error',
210
209
  DOCKER_ERROR: SUPPORTABILITY.UTILIZATION + '/docker/error',
211
- BOOT_ID_ERROR: SUPPORTABILITY.UTILIZATION + '/boot_id/error'
210
+ ECS_CONTAINER_ERROR: SUPPORTABILITY.UTILIZATION + '/ecs/container_id/error',
211
+ GCP_ERROR: SUPPORTABILITY.UTILIZATION + '/gcp/error',
212
+ PCF_ERROR: SUPPORTABILITY.UTILIZATION + '/pcf/error'
212
213
  }
213
214
 
214
215
  const CUSTOM_EVENTS = {
@@ -32,6 +32,13 @@ function plain(normalized, path) {
32
32
  return path
33
33
  }
34
34
 
35
+ /**
36
+ * @event MetricNormalizer#appliedRule
37
+ * @param {object} rule The rule that matched and was applied.
38
+ * @param {string} normalized The newly updated metric name.
39
+ * @param {string} last The metric name that matched the rule.
40
+ */
41
+
35
42
  /**
36
43
  * The collector keeps track of rules that should be applied to metric names,
37
44
  * and sends these rules to the agent at connection time. These rules can
@@ -206,6 +213,8 @@ MetricNormalizer.prototype.addSimple = function addSimple(pattern, name) {
206
213
  *
207
214
  * @param {string} path - The URL path to turn into a name.
208
215
  * @returns {NormalizationResults} - The results of normalization.
216
+ *
217
+ * @fires MetricNormalizer#appliedRule
209
218
  */
210
219
  MetricNormalizer.prototype.normalize = function normalize(path) {
211
220
  let last = path
package/lib/shim/shim.js CHANGED
@@ -1300,7 +1300,7 @@ function getName(obj) {
1300
1300
  * @returns {boolean} True if the object is an Object, else false.
1301
1301
  */
1302
1302
  function isObject(obj) {
1303
- return obj instanceof Object
1303
+ return obj != null && (obj instanceof Object || (!obj.constructor && typeof obj === 'object'))
1304
1304
  }
1305
1305
 
1306
1306
  /**
package/lib/shimmer.js CHANGED
@@ -286,7 +286,11 @@ const shimmer = (module.exports = {
286
286
  */
287
287
  registerThirdPartyInstrumentation(agent) {
288
288
  for (const [moduleName, instrInfo] of Object.entries(INSTRUMENTATIONS)) {
289
- if (instrInfo.module) {
289
+ if (agent.config.instrumentation?.[moduleName]?.enabled === false) {
290
+ logger.warn(
291
+ `Instrumentation for ${moduleName} has been disabled via 'config.instrumentation.${moduleName}.enabled. Not instrumenting package`
292
+ )
293
+ } else if (instrInfo.module) {
290
294
  // Because external instrumentations can change independent of
291
295
  // the agent core, we don't want breakages in them to entirely
292
296
  // disable the agent.
@@ -13,8 +13,23 @@ const SEND_WARNING =
13
13
  'send() is not currently supported on streaming span event aggregator. ' +
14
14
  'This warning will not appear again this agent run.'
15
15
 
16
+ /**
17
+ * Indicates that span streaming has begun.
18
+ *
19
+ * @event StreamingSpanEventAggregator#started
20
+ */
21
+
22
+ /**
23
+ * Indicates that span streaming has finished.
24
+ *
25
+ * @event StreamingSpanEventAggregator#stopped
26
+ */
27
+
16
28
  // TODO: this doesn't "aggregate". Perhaps we need a different terminology
17
29
  // for the base-class and then this implementation can avoid the misleading language.
30
+ /**
31
+ * Handles streaming of spans to the New Relic data collector.
32
+ */
18
33
  class StreamingSpanEventAggregator extends Aggregator {
19
34
  constructor(opts, agent) {
20
35
  const { metrics, collector, harvester } = agent
@@ -31,6 +46,11 @@ class StreamingSpanEventAggregator extends Aggregator {
31
46
  this.isStream = true
32
47
  }
33
48
 
49
+ /**
50
+ * Start streaming spans to the collector.
51
+ *
52
+ * @fires StreamingSpanEventAggregator#started
53
+ */
34
54
  start() {
35
55
  if (this.started) {
36
56
  return
@@ -43,6 +63,11 @@ class StreamingSpanEventAggregator extends Aggregator {
43
63
  this.emit('started')
44
64
  }
45
65
 
66
+ /**
67
+ * Cease streaming of spans to the collector.
68
+ *
69
+ * @fires StreamingSpanEventAggregator#stopped
70
+ */
46
71
  stop() {
47
72
  if (!this.started) {
48
73
  return
@@ -60,7 +85,7 @@ class StreamingSpanEventAggregator extends Aggregator {
60
85
  logger.warnOnce('SEND_WARNING', SEND_WARNING)
61
86
  }
62
87
 
63
- this.emit(`finished ${this.method} data send.`)
88
+ this.emit(`finished_data_send-${this.method}`)
64
89
  }
65
90
 
66
91
  /**
@@ -76,7 +101,7 @@ class StreamingSpanEventAggregator extends Aggregator {
76
101
  * Attempts to add the given segment to the collection.
77
102
  *
78
103
  * @param {TraceSegment} segment - The segment to add.
79
- * @param {string} [parentId=null] - The GUID of the parent span.
104
+ * @param {string} [parentId] - The GUID of the parent span.
80
105
  * @param isRoot
81
106
  * @returns {boolean} True if the segment was added, or false if it was discarded.
82
107
  */
@@ -70,6 +70,8 @@ const MULTIPLE_INSERT_MESSAGE =
70
70
  * transaction.
71
71
  *
72
72
  * @param {object} agent The agent.
73
+ *
74
+ * @fires Agent#transactionStarted
73
75
  */
74
76
  function Transaction(agent) {
75
77
  if (!agent) {
@@ -200,6 +202,8 @@ Transaction.prototype.isActive = function isActive() {
200
202
  * instances of this transaction annotated onto the call stack.
201
203
  *
202
204
  * @returns {(Transaction|undefined)} this transaction, or undefined
205
+ *
206
+ * @fires Agent#transactionFinished
203
207
  */
204
208
  Transaction.prototype.end = function end() {
205
209
  if (!this.timer.isActive()) {
@@ -48,7 +48,7 @@ class TransactionEventAggregator extends EventAggregator {
48
48
  }
49
49
 
50
50
  // TODO: log?
51
- this.emit(`starting ${this.method} data send.`)
51
+ this.emit(`starting_data_send-${this.method}`)
52
52
 
53
53
  logger.debug('Splitting transaction events into multiple payloads')
54
54
 
@@ -60,7 +60,7 @@ class TransactionEventAggregator extends EventAggregator {
60
60
 
61
61
  this._sendMultiple(eventPayloadPairs, () => {
62
62
  // TODO: Log?
63
- this.emit(`finished ${this.method} data send.`)
63
+ this.emit(`finished_data_send-${this.method}`)
64
64
  })
65
65
  }
66
66
 
@@ -8,6 +8,19 @@
8
8
  const EventEmitter = require('events').EventEmitter
9
9
  const util = require('util')
10
10
 
11
+ /**
12
+ * Triggered when the stream has been terminated.
13
+ *
14
+ * @event StreamSink#close
15
+ */
16
+
17
+ /**
18
+ * Triggered when the stream cannot be written to.
19
+ *
20
+ * @event StreamSink#error
21
+ * @param {Error} error The error that has triggered the event.
22
+ */
23
+
11
24
  /**
12
25
  * Pipe a readable stream into this sink that fulfills the Writable Stream
13
26
  * contract and the callback will be fired when the stream has been completely
@@ -30,6 +43,14 @@ function StreamSink(callback) {
30
43
  }
31
44
  util.inherits(StreamSink, EventEmitter)
32
45
 
46
+ /**
47
+ * Write data to the stream.
48
+ *
49
+ * @param {string} string The data to write.
50
+ * @returns {boolean}
51
+ *
52
+ * @fires StreamSink#error
53
+ */
33
54
  StreamSink.prototype.write = function write(string) {
34
55
  if (!this.writable) {
35
56
  this.emit('error', new Error('Sink no longer writable!'))
@@ -49,6 +70,11 @@ StreamSink.prototype.end = function end() {
49
70
  this.callback(null, this.sink)
50
71
  }
51
72
 
73
+ /**
74
+ * Stop the stream and mark it unusable.
75
+ *
76
+ * @fires StreamSink#close
77
+ */
52
78
  StreamSink.prototype.destroy = function destroy() {
53
79
  this.emit('close')
54
80
  this.writable = false
@@ -6,7 +6,6 @@
6
6
  'use strict'
7
7
 
8
8
  const fs = require('node:fs')
9
- const http = require('node:http')
10
9
  const log = require('../logger').child({ component: 'docker-info' })
11
10
  const common = require('./common')
12
11
  const NAMES = require('../metrics/names')
@@ -17,12 +16,17 @@ const CGROUPS_V1_PATH = '/proc/self/cgroup'
17
16
  const CGROUPS_V2_PATH = '/proc/self/mountinfo'
18
17
  const BOOT_ID_PROC_FILE = '/proc/sys/kernel/random/boot_id'
19
18
 
20
- module.exports.getVendorInfo = fetchDockerVendorInfo
21
- module.exports.clearVendorCache = function clearDockerVendorCache() {
19
+ module.exports = {
20
+ clearVendorCache: clearDockerVendorCache,
21
+ getBootId,
22
+ getVendorInfo: fetchDockerVendorInfo
23
+ }
24
+
25
+ function clearDockerVendorCache() {
22
26
  vendorInfo = null
23
27
  }
24
28
 
25
- module.exports.getBootId = function getBootId(agent, callback, logger = log) {
29
+ function getBootId(agent, callback, logger = log) {
26
30
  if (!/linux/i.test(os.platform())) {
27
31
  logger.debug('Platform is not a flavor of linux, omitting boot info')
28
32
  return setImmediate(callback, null, null)
@@ -37,76 +41,8 @@ module.exports.getBootId = function getBootId(agent, callback, logger = log) {
37
41
  }
38
42
 
39
43
  logger.debug('Container boot id is not available in cgroups info')
40
-
41
- if (hasAwsContainerApi() === false) {
42
- // We don't seem to have a recognized location for getting the container
43
- // identifier.
44
- logger.debug('Container is not in a recognized ECS container, omitting boot info')
45
- recordBootIdError(agent)
46
- return callback(null, null)
47
- }
48
-
49
- getEcsContainerId({ agent, callback, logger })
50
- })
51
- }
52
-
53
- /**
54
- * Queries the AWS ECS metadata API to get the boot id.
55
- *
56
- * @param {object} params Function parameters.
57
- * @param {object} params.agent Newrelic agent instance.
58
- * @param {Function} params.callback Typical error first callback. The second
59
- * parameter is the boot id as a string.
60
- * @param {object} [params.logger] Internal logger instance.
61
- */
62
- function getEcsContainerId({ agent, callback, logger }) {
63
- const ecsApiUrl =
64
- process.env.ECS_CONTAINER_METADATA_URI_V4 || process.env.ECS_CONTAINER_METADATA_URI
65
- const req = http.request(ecsApiUrl, (res) => {
66
- let body = Buffer.alloc(0)
67
- res.on('data', (chunk) => {
68
- body = Buffer.concat([body, chunk])
69
- })
70
- res.on('end', () => {
71
- try {
72
- const json = body.toString('utf8')
73
- const data = JSON.parse(json)
74
- if (data.DockerId == null) {
75
- logger.debug('Failed to find DockerId in response, omitting boot info')
76
- recordBootIdError(agent)
77
- return callback(null, null)
78
- }
79
- callback(null, data.DockerId)
80
- } catch (error) {
81
- logger.debug('Failed to process ECS API response, omitting boot info: ' + error.message)
82
- recordBootIdError(agent)
83
- callback(null, null)
84
- }
85
- })
86
- })
87
-
88
- req.on('error', () => {
89
- logger.debug('Failed to query ECS endpoint, omitting boot info')
90
- recordBootIdError(agent)
91
44
  callback(null, null)
92
45
  })
93
-
94
- req.end()
95
- }
96
-
97
- /**
98
- * Inspects the running environment to determine if the AWS ECS metadata API
99
- * is available.
100
- *
101
- * @see https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ec2-metadata.html
102
- *
103
- * @returns {boolean}
104
- */
105
- function hasAwsContainerApi() {
106
- if (process.env.ECS_CONTAINER_METADATA_URI_V4 != null) {
107
- return true
108
- }
109
- return process.env.ECS_CONTAINER_METADATA_URI != null
110
46
  }
111
47
 
112
48
  /**
@@ -0,0 +1,112 @@
1
+ /*
2
+ * Copyright 2024 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ 'use strict'
7
+
8
+ const http = require('node:http')
9
+ const NAMES = require('../metrics/names')
10
+
11
+ module.exports = function fetchEcsInfo(
12
+ agent,
13
+ callback,
14
+ {
15
+ logger = require('../logger').child({ component: 'ecs-info' }),
16
+ getEcsContainerId = _getEcsContainerId,
17
+ hasAwsContainerApi = _hasAwsContainerApi,
18
+ recordIdError = _recordIdError
19
+ } = {}
20
+ ) {
21
+ // Per spec, we do not have a `detect_ecs` key. Since ECS is a service of AWS,
22
+ // we rely on the `detect_aws` setting.
23
+ if (!agent.config.utilization || !agent.config.utilization.detect_aws) {
24
+ return setImmediate(callback, null)
25
+ }
26
+
27
+ if (hasAwsContainerApi() === false) {
28
+ logger.debug('ECS API not available, omitting ECS container id info')
29
+ recordIdError(agent)
30
+ return callback(null, null)
31
+ }
32
+
33
+ getEcsContainerId({
34
+ agent,
35
+ logger,
36
+ recordIdError,
37
+ callback: (error, dockerId) => {
38
+ if (error) {
39
+ return callback(error, null)
40
+ }
41
+ if (dockerId === null) {
42
+ // Some error happened where we could not find the id. Skipping.
43
+ return callback(null, null)
44
+ }
45
+ return callback(null, { ecsDockerId: dockerId })
46
+ }
47
+ })
48
+ }
49
+
50
+ /**
51
+ * Queries the AWS ECS metadata API to get the boot id.
52
+ *
53
+ * @param {object} params Function parameters.
54
+ * @param {object} params.agent Newrelic agent instance.
55
+ * @param {Function} params.callback Typical error first callback. The second
56
+ * parameter is the boot id as a string.
57
+ * @param {object} params.logger Internal logger instance.
58
+ * @param {function} params.recordIdError Function to record error metric.
59
+ */
60
+ function _getEcsContainerId({ agent, callback, logger, recordIdError }) {
61
+ const ecsApiUrl =
62
+ process.env.ECS_CONTAINER_METADATA_URI_V4 || process.env.ECS_CONTAINER_METADATA_URI
63
+ const req = http.request(ecsApiUrl, (res) => {
64
+ let body = Buffer.alloc(0)
65
+ res.on('data', (chunk) => {
66
+ body = Buffer.concat([body, chunk])
67
+ })
68
+ res.on('end', () => {
69
+ try {
70
+ const json = body.toString('utf8')
71
+ const data = JSON.parse(json)
72
+ if (data.DockerId == null) {
73
+ logger.debug('Failed to find DockerId in response, omitting boot info')
74
+ recordIdError(agent)
75
+ return callback(null, null)
76
+ }
77
+ callback(null, data.DockerId)
78
+ } catch (error) {
79
+ logger.debug('Failed to process ECS API response, omitting boot info: ' + error.message)
80
+ recordIdError(agent)
81
+ callback(null, null)
82
+ }
83
+ })
84
+ })
85
+
86
+ req.on('error', () => {
87
+ logger.debug('Failed to query ECS endpoint, omitting boot info')
88
+ recordIdError(agent)
89
+ callback(null, null)
90
+ })
91
+
92
+ req.end()
93
+ }
94
+
95
+ /**
96
+ * Inspects the running environment to determine if the AWS ECS metadata API
97
+ * is available.
98
+ *
99
+ * @see https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ec2-metadata.html
100
+ *
101
+ * @returns {boolean}
102
+ */
103
+ function _hasAwsContainerApi() {
104
+ if (process.env.ECS_CONTAINER_METADATA_URI_V4 != null) {
105
+ return true
106
+ }
107
+ return process.env.ECS_CONTAINER_METADATA_URI != null
108
+ }
109
+
110
+ function _recordIdError(agent) {
111
+ agent.metrics.getOrCreateMetric(NAMES.UTILIZATION.ECS_CONTAINER_ERROR).incrementCallCount()
112
+ }
@@ -9,11 +9,12 @@ const logger = require('../logger').child({ component: 'utilization' })
9
9
 
10
10
  const VENDOR_METHODS = {
11
11
  aws: require('./aws-info'),
12
- pcf: require('./pcf-info'),
13
12
  azure: require('./azure-info'),
14
- gcp: require('./gcp-info'),
15
13
  docker: require('./docker-info').getVendorInfo,
16
- kubernetes: require('./kubernetes-info')
14
+ ecs: require('./ecs-info'),
15
+ gcp: require('./gcp-info'),
16
+ kubernetes: require('./kubernetes-info'),
17
+ pcf: require('./pcf-info')
17
18
  }
18
19
  const VENDOR_NAMES = Object.keys(VENDOR_METHODS)
19
20
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "newrelic",
3
- "version": "12.3.1",
3
+ "version": "12.5.0",
4
4
  "author": "New Relic Node.js agent team <nodejs@newrelic.com>",
5
5
  "license": "Apache-2.0",
6
6
  "contributors": [
@@ -219,6 +219,7 @@
219
219
  "@aws-sdk/client-s3": "^3.556.0",
220
220
  "@aws-sdk/s3-request-presigner": "^3.556.0",
221
221
  "@koa/router": "^12.0.1",
222
+ "@matteo.collina/tspl": "^0.1.1",
222
223
  "@newrelic/eslint-config": "^0.3.0",
223
224
  "@newrelic/newrelic-oss-cli": "^0.1.2",
224
225
  "@newrelic/test-utilities": "^9.1.0",
@@ -254,7 +255,6 @@
254
255
  "nock": "11.8.0",
255
256
  "proxy": "^2.1.1",
256
257
  "proxyquire": "^1.8.0",
257
- "rfdc": "^1.3.1",
258
258
  "rimraf": "^2.6.3",
259
259
  "self-cert": "^2.0.0",
260
260
  "should": "*",