newrelic 14.2.0 → 14.3.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 +85 -28
- package/lib/agent.js +8 -2
- package/lib/aggregators/profiling-aggregator.js +11 -0
- package/lib/config/index.js +43 -6
- package/lib/context-manager/async-local-context-manager.js +5 -1
- package/lib/otel/fake-span.js +119 -3
- package/lib/otel/metrics/index.js +1 -1
- package/lib/profiling/index.js +36 -2
- package/lib/profiling/profilers/base.js +10 -1
- package/lib/profiling/profilers/cpu.js +4 -3
- package/lib/profiling/profilers/heap.js +3 -3
- package/lib/subscribers/ai-monitoring/base.js +18 -3
- package/lib/subscribers/ai-monitoring/chat.js +1 -5
- package/lib/subscribers/ai-monitoring/embedding.js +1 -1
- package/lib/subscribers/anthropic-sdk/create.js +1 -1
- package/lib/subscribers/bunyan/index.js +10 -0
- package/lib/subscribers/google-adk/agent-run-async.js +1 -1
- package/lib/subscribers/google-adk/tool-run-async.js +1 -1
- package/lib/subscribers/google-genai/generate-content-stream.js +0 -5
- package/lib/subscribers/langchain/runnable-stream.js +0 -4
- package/lib/subscribers/langchain/tool.js +1 -1
- package/lib/subscribers/langchain/vectorstore.js +1 -1
- package/lib/subscribers/langgraph/graph-stream.js +1 -8
- package/lib/subscribers/openai/chat.js +1 -1
- package/lib/subscribers/winston/configure.js +13 -5
- package/lib/subscribers/winston/nr-winston-transport.js +6 -3
- package/package.json +2 -2
package/NEWS.md
CHANGED
|
@@ -1,31 +1,88 @@
|
|
|
1
|
-
### v14.
|
|
2
|
-
|
|
3
|
-
#### Features
|
|
4
|
-
|
|
5
|
-
* Added
|
|
6
|
-
* Added
|
|
7
|
-
* Added
|
|
8
|
-
* Added `
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
#### Documentation
|
|
16
|
-
|
|
17
|
-
* Updated compatibility report ([#
|
|
18
|
-
|
|
19
|
-
#### Miscellaneous chores
|
|
20
|
-
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
1
|
+
### v14.3.0 (2026-07-14)
|
|
2
|
+
|
|
3
|
+
#### Features
|
|
4
|
+
|
|
5
|
+
* Added source mapping support to heap profiler ([#4122](https://github.com/newrelic/node-newrelic/pull/4122)) ([dcee39f](https://github.com/newrelic/node-newrelic/commit/dcee39fa76a102389f9dd7c186461346ef4fa265))
|
|
6
|
+
* Added source mapping support to CPU (wall time) profiler ([#4095](https://github.com/newrelic/node-newrelic/pull/4095)) ([f6123bc](https://github.com/newrelic/node-newrelic/commit/f6123bc939afc0122443fda3eab7d9e842dcbfef))
|
|
7
|
+
* Added `otlp_resource_attributes` to all outgoing OpenTelemetry metrics calls ([#4128](https://github.com/newrelic/node-newrelic/pull/4128)) ([a0c1c5b](https://github.com/newrelic/node-newrelic/commit/a0c1c5b0f314b7ca35b84f0c6cf8229395c4aa52))
|
|
8
|
+
* Added support for `ai_monitoring.enabled`,`ai_monitoring.streaming.enabled`, and `ai_monitoring.record_content.enabled` to be assigned via server-side config ([#4123](https://github.com/newrelic/node-newrelic/pull/4123)) ([c4d8032](https://github.com/newrelic/node-newrelic/commit/c4d8032bb2fcc7e346854b6ae818bea9e17f7f9f))
|
|
9
|
+
|
|
10
|
+
#### Bug fixes
|
|
11
|
+
|
|
12
|
+
* Fixed `bunyan` log forwarding multiple duplicate lines ([#4132](https://github.com/newrelic/node-newrelic/pull/4132)) ([b0f4242](https://github.com/newrelic/node-newrelic/commit/b0f4242098ffe8a63566519707987b588b22f6aa))
|
|
13
|
+
* Fixed OTEL crash when adding exceptions to spans ([#4130](https://github.com/newrelic/node-newrelic/pull/4130)) ([508542e](https://github.com/newrelic/node-newrelic/commit/508542e7cb8f2ad11952171a4e44dc71b2d23f07))
|
|
14
|
+
|
|
15
|
+
#### Documentation
|
|
16
|
+
|
|
17
|
+
* Updated compatibility report ([#4125](https://github.com/newrelic/node-newrelic/pull/4125)) ([405b59b](https://github.com/newrelic/node-newrelic/commit/405b59b20ed811b31eadc4a85bf8ff6a64c12edd)) ([#4107](https://github.com/newrelic/node-newrelic/pull/4107)) ([267a964](https://github.com/newrelic/node-newrelic/commit/267a964cffdab4373a9292e035cc877b814f8529))
|
|
18
|
+
|
|
19
|
+
#### Miscellaneous chores
|
|
20
|
+
|
|
21
|
+
* Changed `notify-release.yml` schedule with our bot user ([#4135](https://github.com/newrelic/node-newrelic/pull/4135)) ([a9318ae](https://github.com/newrelic/node-newrelic/commit/a9318aec1ff32fd8930431b20df7123f76ced513))
|
|
22
|
+
* Changed `versioned-security-agent.yml` schedule with our bot user ([#4134](https://github.com/newrelic/node-newrelic/pull/4134)) ([d7c6f64](https://github.com/newrelic/node-newrelic/commit/d7c6f64af2747a392ac905a1775aa70642a9f9b6))
|
|
23
|
+
* Changed smoke-test schedule with our bot user ([#4133](https://github.com/newrelic/node-newrelic/pull/4133)) ([400268e](https://github.com/newrelic/node-newrelic/commit/400268e98b9d559c1b46726a91f555aeff706bbe))
|
|
24
|
+
* Sharded the versioned tests w/ security agent workflow ([#4119](https://github.com/newrelic/node-newrelic/pull/4119)) ([fe200c4](https://github.com/newrelic/node-newrelic/commit/fe200c4fc1996ce7b115a7ab76e4b84659758d01))
|
|
25
|
+
* Updated test runner to accept suites as positional parameters ([#4117](https://github.com/newrelic/node-newrelic/pull/4117)) ([ac0985d](https://github.com/newrelic/node-newrelic/commit/ac0985d65291eb24637c90e4d96b0e3aff56ecfc))
|
|
26
|
+
|
|
27
|
+
#### Tests
|
|
28
|
+
|
|
29
|
+
* Added more scenarios to winston for constructing loggers ([#4120](https://github.com/newrelic/node-newrelic/pull/4120)) ([ea90fc3](https://github.com/newrelic/node-newrelic/commit/ea90fc30719824c8cdd7de67a9af88257ab97805))
|
|
30
|
+
* Added skip for winston test for ranges that do not support it ([#4121](https://github.com/newrelic/node-newrelic/pull/4121)) ([e75e15e](https://github.com/newrelic/node-newrelic/commit/e75e15e3ca3eb7344f9a9697efe9b49fef96f3af))
|
|
31
|
+
* Applied sampling to `pg-native` tests to reduce the matrix under test ([#4124](https://github.com/newrelic/node-newrelic/pull/4124)) ([b1ad2da](https://github.com/newrelic/node-newrelic/commit/b1ad2da853ee55ebe7477ff6a172e454a76d13bd))
|
|
32
|
+
* Fixed flaky amqplib async-work duration assertions ([#4118](https://github.com/newrelic/node-newrelic/pull/4118)) ([3544237](https://github.com/newrelic/node-newrelic/commit/3544237c908f908aad17cf4d6b713015f186be6e))
|
|
33
|
+
* Removed timing race from common.request unit tests ([#4113](https://github.com/newrelic/node-newrelic/pull/4113)) ([b22c25b](https://github.com/newrelic/node-newrelic/commit/b22c25b56f73b9821710e22988a4cdcbfb06d907))
|
|
34
|
+
|
|
35
|
+
### v14.2.1 (2026-07-09)
|
|
36
|
+
|
|
37
|
+
#### Bug fixes
|
|
38
|
+
|
|
39
|
+
* Updated `winston` instrumenting to assign `handleExceptions` to the first NrTransport to avoid registering duplicate `uncaughtException` handlers ([#4108](https://github.com/newrelic/node-newrelic/pull/4108)) ([d60a48d](https://github.com/newrelic/node-newrelic/commit/d60a48d3b9399d3a084c64cf53c3a62c7a4cab0b))
|
|
40
|
+
* This would create a linear memory leak because the `NrTransport` objects for loggers no longer in use, would not be freed because of the global `process.on('uncaughtException')` handler
|
|
41
|
+
|
|
42
|
+
#### Documentation
|
|
43
|
+
|
|
44
|
+
* Updated compatibility report ([#4099](https://github.com/newrelic/node-newrelic/pull/4099)) ([058fde6](https://github.com/newrelic/node-newrelic/commit/058fde6d64e3606b82875380ba51f9bc44a4b994))
|
|
45
|
+
|
|
46
|
+
#### Continuous integration
|
|
47
|
+
|
|
48
|
+
* Pinned CI npm to 11 so native versioned suites build on Node 22 ([#4114](https://github.com/newrelic/node-newrelic/pull/4114)) ([b844e94](https://github.com/newrelic/node-newrelic/commit/b844e94317e573410d96f8971308b2d1535570ca))
|
|
49
|
+
* Removed the 8 minute versioned shard timeout ([#4111](https://github.com/newrelic/node-newrelic/pull/4111)) ([0ef6374](https://github.com/newrelic/node-newrelic/commit/0ef63749b2739d7c7446a6b7c6d3ee80d2d360e5))
|
|
50
|
+
* Attempted to fix memcached CI issue again ([#4098](https://github.com/newrelic/node-newrelic/pull/4098)) ([f67fef5](https://github.com/newrelic/node-newrelic/commit/f67fef5febe71ffab853164aba06c76cd2c6c26d))
|
|
51
|
+
* Shard versioned tests across CI runners ([#4101](https://github.com/newrelic/node-newrelic/pull/4101)) ([1471734](https://github.com/newrelic/node-newrelic/commit/14717345d87ec46a4eba18c513e10e3caed40f7c))
|
|
52
|
+
|
|
53
|
+
#### Tests
|
|
54
|
+
|
|
55
|
+
* Added `samples` to prisma and next to cut down on versions under test on packages we recommend using hybrid ageninstrumentation ([#4112](https://github.com/newrelic/node-newrelic/pull/4112)) ([7d8bd7c](https://github.com/newrelic/node-newrelic/commit/7d8bd7cd2e3076d17ad25b75628956d1158fc23a))
|
|
56
|
+
* Resolved `grpc` `proto-loader` from the running suite's `node_modules` ([#4110](https://github.com/newrelic/node-newrelic/pull/4110)) ([5b5a9f8](https://github.com/newrelic/node-newrelic/commit/5b5a9f8d8e0f17557b9f202b0098e2ec84c9e028))
|
|
57
|
+
|
|
58
|
+
### v14.2.0 (2026-07-07)
|
|
59
|
+
|
|
60
|
+
#### Features
|
|
61
|
+
|
|
62
|
+
* Added Node.js 26 support ([#4084](https://github.com/newrelic/node-newrelic/pull/4084)) ([4a7f777](https://github.com/newrelic/node-newrelic/commit/4a7f7775d8598656f84bd1590a80e56458feb185))
|
|
63
|
+
* Added `span_id` and `trace_id` labels to `CpuProfiler` sample data ([#4049](https://github.com/newrelic/node-newrelic/pull/4049)) ([c2b1737](https://github.com/newrelic/node-newrelic/commit/c2b1737ac87a26b7fce6d170597301cf97d26b22))
|
|
64
|
+
* Added new GCP Cloud Run hostname logic to include `K_REVISION` ([#4069](https://github.com/newrelic/node-newrelic/pull/4069)) ([02855dc](https://github.com/newrelic/node-newrelic/commit/02855dc82b636c655e9808b50897b4dc5c7b1bba))
|
|
65
|
+
* Added `profiling.source_mapping.enabled` config option ([#4090](https://github.com/newrelic/node-newrelic/pull/4090)) ([43add80](https://github.com/newrelic/node-newrelic/commit/43add80d07cccb1fd48190a60895d88cbde17b00))
|
|
66
|
+
* Removed `spanId` in outgoing `tracestate` header ([#4091](https://github.com/newrelic/node-newrelic/pull/4091)) ([40a8bf1](https://github.com/newrelic/node-newrelic/commit/40a8bf1682860d11f2bf46fbfdaae840b6f3fa18))
|
|
67
|
+
|
|
68
|
+
#### Code refactoring
|
|
69
|
+
|
|
70
|
+
* Refactored `mongodb` instrumentation to subscribe to events emitted ([#4059](https://github.com/newrelic/node-newrelic/pull/4059)) ([d2777ba](https://github.com/newrelic/node-newrelic/commit/d2777ba6b29ab30f1a158e993283029bf40cff86))
|
|
71
|
+
|
|
72
|
+
#### Documentation
|
|
73
|
+
|
|
74
|
+
* Updated compatibility report ([#4078](https://github.com/newrelic/node-newrelic/pull/4078)) ([047a063](https://github.com/newrelic/node-newrelic/commit/047a063dd9e21097d2995dbd7f14ee564b96efcd))
|
|
75
|
+
|
|
76
|
+
#### Miscellaneous chores
|
|
77
|
+
|
|
78
|
+
* Addressed long standing CI flakes ([#4093](https://github.com/newrelic/node-newrelic/pull/4093)) ([ae973a0](https://github.com/newrelic/node-newrelic/commit/ae973a0d7bfd7da54bd0fe5e68d79fa77b180cfe))
|
|
79
|
+
* Fixed package version resolution on Node 26 ([#4094](https://github.com/newrelic/node-newrelic/pull/4094)) ([73d14a9](https://github.com/newrelic/node-newrelic/commit/73d14a98c3bd678be499e14533e66f86b7b9996b))
|
|
80
|
+
|
|
81
|
+
#### Continuous integration
|
|
82
|
+
|
|
83
|
+
* Updated `release-creation` to use `lts/*` for node-version ([#4080](https://github.com/newrelic/node-newrelic/pull/4080)) ([7eaed22](https://github.com/newrelic/node-newrelic/commit/7eaed22f92af2da5db31232b60b5bf35f0ca3e02))
|
|
84
|
+
* Updated `setup-node` job to include `check-latest` to ensure we are getting the latest version of node ([#4079](https://github.com/newrelic/node-newrelic/pull/4079)) ([4a425f6](https://github.com/newrelic/node-newrelic/commit/4a425f692c8eacac1e4967e7517c34605f67ad82))
|
|
85
|
+
|
|
29
86
|
### v14.1.2 (2026-06-25)
|
|
30
87
|
|
|
31
88
|
#### Bug fixes
|
package/lib/agent.js
CHANGED
|
@@ -397,7 +397,11 @@ Agent.prototype.start = function start(callback) {
|
|
|
397
397
|
}
|
|
398
398
|
logger.info('Starting New Relic for Node.js connection process.')
|
|
399
399
|
|
|
400
|
-
|
|
400
|
+
// Build the profiling source mapper once, so it can be reused
|
|
401
|
+
// across profiling start/stop cycles.
|
|
402
|
+
this.sourceMapperReady = this.profilingData.initSourceMapper()
|
|
403
|
+
|
|
404
|
+
this.collector.connect(async function onStartConnect(error, response) {
|
|
401
405
|
if (error || response.shouldShutdownRun()) {
|
|
402
406
|
agent.healthReporter.setStatus(HealthReporter.STATUS_CONNECT_ERROR)
|
|
403
407
|
agent.setState('errored')
|
|
@@ -408,6 +412,9 @@ Agent.prototype.start = function start(callback) {
|
|
|
408
412
|
|
|
409
413
|
if (agent.collector.isConnected()) {
|
|
410
414
|
const config = response.payload
|
|
415
|
+
// await for source mapper before starting the harvester
|
|
416
|
+
// which registers the profiling aggregator which could take the sourceMapper
|
|
417
|
+
await agent.sourceMapperReady
|
|
411
418
|
|
|
412
419
|
const shouldImmediatelyHarvest = !agent.config.no_immediate_harvest
|
|
413
420
|
agent.onConnect(shouldImmediatelyHarvest, () => {
|
|
@@ -453,7 +460,6 @@ Agent.prototype.onConnect = function onConnect(shouldImmediatelyHarvest, callbac
|
|
|
453
460
|
}
|
|
454
461
|
|
|
455
462
|
this._scheduleHarvests(shouldImmediatelyHarvest, callback)
|
|
456
|
-
|
|
457
463
|
this.setState('started')
|
|
458
464
|
}
|
|
459
465
|
|
|
@@ -37,6 +37,17 @@ class ProfilingAggregator extends BaseAggregator {
|
|
|
37
37
|
return this.pprofData
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Builds the profiling SourceMapper once during agent startup so the profilers
|
|
42
|
+
* resolve frames to their original source. Delegated to the manager, which caches
|
|
43
|
+
* and consumes it; a no-op unless both profiling and source_mapping are enabled.
|
|
44
|
+
*
|
|
45
|
+
* @returns {Promise<void>}
|
|
46
|
+
*/
|
|
47
|
+
initSourceMapper() {
|
|
48
|
+
return this.profilingManager.buildSourceMapper()
|
|
49
|
+
}
|
|
50
|
+
|
|
40
51
|
/**
|
|
41
52
|
* This overrides the default `start` method
|
|
42
53
|
* as we want to collect profiling data for `cpu` and/or `heap`
|
package/lib/config/index.js
CHANGED
|
@@ -141,6 +141,10 @@ function Config(config) {
|
|
|
141
141
|
|
|
142
142
|
this.browser_monitoring.loader = 'rum'
|
|
143
143
|
this.browser_monitoring.loader_version = ''
|
|
144
|
+
// default to empty object, this will get populated
|
|
145
|
+
// from `onConnect` if hybrid agent is enabled
|
|
146
|
+
// along with hybrid agent's metrics feature
|
|
147
|
+
this.otlp_resource_attributes = {}
|
|
144
148
|
|
|
145
149
|
// Settings to play nice with DLPs (see NODE-1044).
|
|
146
150
|
this.simple_compression = false // Disables subcomponent compression
|
|
@@ -357,12 +361,6 @@ Config.prototype._fromServer = function _fromServer(params, key) {
|
|
|
357
361
|
case 'high_security':
|
|
358
362
|
break
|
|
359
363
|
|
|
360
|
-
// interpret AI Monitoring account setting
|
|
361
|
-
case 'collect_ai':
|
|
362
|
-
this._disableOption(params.collect_ai, 'ai_monitoring')
|
|
363
|
-
this.emit('change', this)
|
|
364
|
-
break
|
|
365
|
-
|
|
366
364
|
// always accept these settings
|
|
367
365
|
case 'encoding_key':
|
|
368
366
|
this._alwaysUpdateIfChanged(params, key)
|
|
@@ -583,6 +581,41 @@ Config.prototype._fromServer = function _fromServer(params, key) {
|
|
|
583
581
|
}
|
|
584
582
|
break
|
|
585
583
|
|
|
584
|
+
// interpret AI Monitoring account setting.
|
|
585
|
+
// `ai_monitoring.enabled` from the connect response server-side config(SSC) takes precedence over
|
|
586
|
+
// SSC settings come in an `agent_config` object
|
|
587
|
+
// `collect_ai`; collect_ai is only the fallback when it is absent.
|
|
588
|
+
case 'collect_ai':
|
|
589
|
+
if (!params?.agent_config?.['ai_monitoring.enabled']) {
|
|
590
|
+
this._disableOption(params.collect_ai, 'ai_monitoring')
|
|
591
|
+
this.emit('change', this)
|
|
592
|
+
}
|
|
593
|
+
break
|
|
594
|
+
case 'ai_monitoring.enabled':
|
|
595
|
+
this._updateNestedIfChanged(
|
|
596
|
+
params,
|
|
597
|
+
this.ai_monitoring,
|
|
598
|
+
'ai_monitoring.enabled',
|
|
599
|
+
'enabled'
|
|
600
|
+
)
|
|
601
|
+
break
|
|
602
|
+
case 'ai_monitoring.streaming.enabled':
|
|
603
|
+
this._updateNestedIfChanged(
|
|
604
|
+
params,
|
|
605
|
+
this.ai_monitoring.streaming,
|
|
606
|
+
'ai_monitoring.streaming.enabled',
|
|
607
|
+
'enabled'
|
|
608
|
+
)
|
|
609
|
+
break
|
|
610
|
+
case 'ai_monitoring.record_content.enabled':
|
|
611
|
+
this._updateNestedIfChanged(
|
|
612
|
+
params,
|
|
613
|
+
this.ai_monitoring.record_content,
|
|
614
|
+
'ai_monitoring.record_content.enabled',
|
|
615
|
+
'enabled'
|
|
616
|
+
)
|
|
617
|
+
break
|
|
618
|
+
|
|
586
619
|
case 'profiling.enabled':
|
|
587
620
|
this._updateNestedIfChanged(
|
|
588
621
|
params,
|
|
@@ -592,6 +625,10 @@ Config.prototype._fromServer = function _fromServer(params, key) {
|
|
|
592
625
|
)
|
|
593
626
|
break
|
|
594
627
|
|
|
628
|
+
case 'otlp_resource_attributes':
|
|
629
|
+
this.otlp_resource_attributes = params.otlp_resource_attributes ?? {}
|
|
630
|
+
break
|
|
631
|
+
|
|
595
632
|
// These settings are not allowed from the server.
|
|
596
633
|
case 'attributes.enabled':
|
|
597
634
|
case 'attributes.exclude':
|
|
@@ -30,7 +30,11 @@ class AsyncLocalContextManager {
|
|
|
30
30
|
* @returns {object} The current active context.
|
|
31
31
|
*/
|
|
32
32
|
getContext() {
|
|
33
|
-
return this._asyncLocalStorage.getStore() || (
|
|
33
|
+
return this._asyncLocalStorage.getStore() || (
|
|
34
|
+
this.isOtelBridgeMode
|
|
35
|
+
? new OtelContext()
|
|
36
|
+
: new Context()
|
|
37
|
+
)
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
/**
|
package/lib/otel/fake-span.js
CHANGED
|
@@ -10,12 +10,29 @@
|
|
|
10
10
|
* within otel bridge. We have to create fake spans for new relic
|
|
11
11
|
* segments. The only thing needed is a method for `spanContext`
|
|
12
12
|
* which should return the spanId(segment id) and traceId(transaction trace id).
|
|
13
|
-
* We
|
|
13
|
+
* We hard code traceFlags to 1.
|
|
14
|
+
*
|
|
15
|
+
* Note: as of 2026-07 we stub methods on this object that do not make sense
|
|
16
|
+
* in the context of our bridge. For example, we internally manage the name
|
|
17
|
+
* and lifecyle of spans. So those methods are no-ops here.
|
|
18
|
+
*
|
|
19
|
+
* @see https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api._opentelemetry_api.Span.html
|
|
14
20
|
*/
|
|
15
21
|
module.exports = class FakeSpan {
|
|
22
|
+
#segment
|
|
23
|
+
#transaction
|
|
24
|
+
|
|
16
25
|
constructor(segment, transaction) {
|
|
17
|
-
this
|
|
18
|
-
this
|
|
26
|
+
this.#segment = segment
|
|
27
|
+
this.#transaction = transaction
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get segmentId() {
|
|
31
|
+
return this.#segment.id
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get traceId() {
|
|
35
|
+
return this.#transaction.traceId
|
|
19
36
|
}
|
|
20
37
|
|
|
21
38
|
spanContext() {
|
|
@@ -25,4 +42,103 @@ module.exports = class FakeSpan {
|
|
|
25
42
|
traceFlags: 1
|
|
26
43
|
}
|
|
27
44
|
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Adds a new attribute to the backing segment.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} key The attribute name to add
|
|
50
|
+
* @param {*} value Some serializable JavaScript value.
|
|
51
|
+
*
|
|
52
|
+
* @returns {FakeSpan}
|
|
53
|
+
*/
|
|
54
|
+
setAttribute(key, value) {
|
|
55
|
+
this.#segment.addAttribute(key, value)
|
|
56
|
+
return this
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Add multiple attributes to the backing segment at once.
|
|
61
|
+
*
|
|
62
|
+
* @param {object} attributes Each field name is the key, and each field
|
|
63
|
+
* value the value passed to {@link #setAttribute}.
|
|
64
|
+
*
|
|
65
|
+
* @returns {FakeSpan}
|
|
66
|
+
*/
|
|
67
|
+
setAttributes(attributes) {
|
|
68
|
+
for (const [k, v] of Object.entries(attributes)) {
|
|
69
|
+
this.setAttribute(k, v)
|
|
70
|
+
}
|
|
71
|
+
return this
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
addEvent(name, _attrsOrTime, _time) {
|
|
75
|
+
this.#segment.logger.warn(
|
|
76
|
+
'addEvent is not implemented. Not adding event: %s.',
|
|
77
|
+
name
|
|
78
|
+
)
|
|
79
|
+
return this
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Adds a new span link to the backing segment.
|
|
84
|
+
*
|
|
85
|
+
* @param {object} link An OTEL shaped span link.
|
|
86
|
+
*
|
|
87
|
+
* @see https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api._opentelemetry_api.Link.html
|
|
88
|
+
*
|
|
89
|
+
* @returns {FakeSpan}
|
|
90
|
+
*/
|
|
91
|
+
addLink(link) {
|
|
92
|
+
this.#segment.addSpanLink(link)
|
|
93
|
+
return this
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Adds multiple span links to the backing segment at once.
|
|
98
|
+
*
|
|
99
|
+
* @param {object[]} links A list of OTEL shaped span links.
|
|
100
|
+
*
|
|
101
|
+
* @see @see https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api._opentelemetry_api.Link.html
|
|
102
|
+
*
|
|
103
|
+
* @returns {FakeSpan}
|
|
104
|
+
*/
|
|
105
|
+
addLinks(links) {
|
|
106
|
+
for (const link of links) {
|
|
107
|
+
this.addLink(link)
|
|
108
|
+
}
|
|
109
|
+
return this
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
setStatus(otelSpanStatus) {
|
|
113
|
+
this.#segment.logger.warn(
|
|
114
|
+
'setStatus is not implemented. Not setting status: %s.',
|
|
115
|
+
otelSpanStatus?.code
|
|
116
|
+
)
|
|
117
|
+
return this
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
updateName(name) {
|
|
121
|
+
this.#segment.logger.warn(
|
|
122
|
+
'updateName is not implemented. Not setting name: %s.',
|
|
123
|
+
name
|
|
124
|
+
)
|
|
125
|
+
return this
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
end(_time) {
|
|
129
|
+
this.#segment.logger.warn(
|
|
130
|
+
'end is not implemented. Not ending span.'
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
isRecording() {
|
|
135
|
+
return true
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
recordException(exception, time) {
|
|
139
|
+
// This replicates what the original actually does.
|
|
140
|
+
// See:
|
|
141
|
+
// https://github.com/open-telemetry/opentelemetry-js/blob/9b05f66/packages/sdk-trace/src/Span.ts#L427-L451.
|
|
142
|
+
this.addEvent(exception, null, time)
|
|
143
|
+
}
|
|
28
144
|
}
|
|
@@ -115,7 +115,7 @@ class SetupMetrics extends SetupSignal {
|
|
|
115
115
|
{ agent, logger }
|
|
116
116
|
)
|
|
117
117
|
|
|
118
|
-
const resource = resourceFromAttributes({ 'entity.guid': config.entity_guid })
|
|
118
|
+
const resource = resourceFromAttributes({ 'entity.guid': config.entity_guid, ...config.otlp_resource_attributes })
|
|
119
119
|
// Assigning the resource after having received the `entity.guid` from
|
|
120
120
|
// the server is a key detail of this implementation. Unfortunately,
|
|
121
121
|
// we don't have real public access to the object that retains the
|
package/lib/profiling/index.js
CHANGED
|
@@ -16,17 +16,26 @@ class ProfilingManager {
|
|
|
16
16
|
this.samplingInterval = samplingInterval
|
|
17
17
|
this.profilers = new Map()
|
|
18
18
|
this.startTime = null
|
|
19
|
+
this.sourceMapper = null
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
register() {
|
|
22
23
|
if (this.config.include.includes('heap') && !this.profilers.has('HeapProfiler')) {
|
|
23
24
|
const { HeapProfiler } = require('./profilers')
|
|
24
|
-
this.profilers.set('HeapProfiler', new HeapProfiler({
|
|
25
|
+
this.profilers.set('HeapProfiler', new HeapProfiler({
|
|
26
|
+
logger: this.logger,
|
|
27
|
+
sourceMapper: this.sourceMapper
|
|
28
|
+
}))
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
if (this.config.include.includes('cpu') && !this.profilers.has('CpuProfiler')) {
|
|
28
32
|
const { CpuProfiler } = require('./profilers')
|
|
29
|
-
this.profilers.set('CpuProfiler', new CpuProfiler({
|
|
33
|
+
this.profilers.set('CpuProfiler', new CpuProfiler({
|
|
34
|
+
logger: this.logger,
|
|
35
|
+
samplingInterval: this.samplingInterval,
|
|
36
|
+
tracer: this.tracer,
|
|
37
|
+
sourceMapper: this.sourceMapper
|
|
38
|
+
}))
|
|
30
39
|
}
|
|
31
40
|
}
|
|
32
41
|
|
|
@@ -84,6 +93,31 @@ class ProfilingManager {
|
|
|
84
93
|
|
|
85
94
|
return results
|
|
86
95
|
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Builds the `pprof` SourceMapper once by scanning the app root for source
|
|
99
|
+
* maps and caches it for the profilers to resolve frames to their original
|
|
100
|
+
* source. A no-op unless `source_mapping` is enabled; on failure, the mapper
|
|
101
|
+
* stays `null` and frames fall back to compiled file/line.
|
|
102
|
+
*
|
|
103
|
+
* @returns {Promise<void>}
|
|
104
|
+
*/
|
|
105
|
+
async buildSourceMapper() {
|
|
106
|
+
if (!(this.config.enabled && this.config.source_mapping.enabled)) {
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// lazy-require so pprof's native binding only loads when source mapping is enabled
|
|
111
|
+
const { SourceMapper } = require('@datadog/pprof')
|
|
112
|
+
const searchDir = process.cwd()
|
|
113
|
+
try {
|
|
114
|
+
const start = Date.now()
|
|
115
|
+
this.sourceMapper = await SourceMapper.create([searchDir])
|
|
116
|
+
this.logger.debug(`Built profiling SourceMapper from ${searchDir} in ${Date.now() - start} ms.`)
|
|
117
|
+
} catch (error) {
|
|
118
|
+
this.logger.error({ error }, `Failed to build profiling SourceMapper from ${searchDir}, frames will report compiled file/line.`)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
87
121
|
}
|
|
88
122
|
|
|
89
123
|
module.exports = ProfilingManager
|
|
@@ -6,8 +6,17 @@
|
|
|
6
6
|
'use strict'
|
|
7
7
|
|
|
8
8
|
class BaseProfiler {
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* pprof `SourceMapper` to be shared across the entire agent
|
|
11
|
+
* instance, or `null`/`undefined` if disabled.
|
|
12
|
+
* When set, pprof resolves sample frames to their original
|
|
13
|
+
* source at serialization (e.g. TypeScript, minified code).
|
|
14
|
+
*/
|
|
15
|
+
sourceMapper
|
|
16
|
+
|
|
17
|
+
constructor({ logger, sourceMapper }) {
|
|
10
18
|
this.logger = logger
|
|
19
|
+
this.sourceMapper = sourceMapper
|
|
11
20
|
}
|
|
12
21
|
|
|
13
22
|
start() {
|
|
@@ -58,8 +58,8 @@ class CpuProfiler extends BaseProfiler {
|
|
|
58
58
|
*/
|
|
59
59
|
#asyncHook
|
|
60
60
|
|
|
61
|
-
constructor({ logger, samplingInterval, tracer }) {
|
|
62
|
-
super({ logger })
|
|
61
|
+
constructor({ logger, samplingInterval, tracer, sourceMapper }) {
|
|
62
|
+
super({ logger, sourceMapper })
|
|
63
63
|
this.#pprof = require('@datadog/pprof')
|
|
64
64
|
this.#kSampleCount = this.#pprof.time.constants.kSampleCount
|
|
65
65
|
this.#tracer = tracer
|
|
@@ -82,7 +82,8 @@ class CpuProfiler extends BaseProfiler {
|
|
|
82
82
|
// needed for trace_id + span_id label
|
|
83
83
|
withContexts: true,
|
|
84
84
|
// carry the sample context per async-context-frame when ACF is active
|
|
85
|
-
useCPED: this.#useCPED
|
|
85
|
+
useCPED: this.#useCPED,
|
|
86
|
+
sourceMapper: this.sourceMapper
|
|
86
87
|
})
|
|
87
88
|
|
|
88
89
|
if (this.#useCPED) {
|
|
@@ -11,8 +11,8 @@ class HeapProfiler extends BaseProfiler {
|
|
|
11
11
|
#pprof
|
|
12
12
|
#intervalBytes = 524288 // captures stack trace every 512 kb of allocated memory
|
|
13
13
|
#stackDepth = 64
|
|
14
|
-
constructor({ logger }) {
|
|
15
|
-
super({ logger })
|
|
14
|
+
constructor({ logger, sourceMapper }) {
|
|
15
|
+
super({ logger, sourceMapper })
|
|
16
16
|
this.#pprof = require('@datadog/pprof')
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -30,7 +30,7 @@ class HeapProfiler extends BaseProfiler {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
async collect() {
|
|
33
|
-
const profile = this.#pprof.heap.profile()
|
|
33
|
+
const profile = this.#pprof.heap.profile(undefined, this.sourceMapper)
|
|
34
34
|
return this.#pprof.encode(profile)
|
|
35
35
|
}
|
|
36
36
|
}
|
|
@@ -26,8 +26,23 @@ class AiMonitoringSubscriber extends Subscriber {
|
|
|
26
26
|
this.trackingPrefix = trackingPrefix
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Checks if AI monitoring is enabled. The subscriber itself is registered
|
|
31
|
+
* regardless of this value (gated only by `instrumentation.<lib>.enabled`),
|
|
32
|
+
* but AI monitoring behavior (segments, events, metrics) is gated on this.
|
|
33
|
+
* @returns {boolean} if AI monitoring is enabled
|
|
34
|
+
*/
|
|
35
|
+
get aiEnabled() {
|
|
36
|
+
return this.agent.config.ai_monitoring.enabled === true
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Checks if AI monitoring streaming is enabled. It is predicated on AI monitoring being enabled
|
|
41
|
+
* as if it isn't, no instrumentation should occur
|
|
42
|
+
* @returns {boolean} if we should be instrumenting streams
|
|
43
|
+
*/
|
|
44
|
+
get streamingEnabled() {
|
|
45
|
+
return this.aiEnabled && this.agent.config.ai_monitoring.streaming.enabled === true
|
|
31
46
|
}
|
|
32
47
|
|
|
33
48
|
set name(name) {
|
|
@@ -96,7 +111,7 @@ class AiMonitoringSubscriber extends Subscriber {
|
|
|
96
111
|
* @returns {Context} either new context or existing
|
|
97
112
|
*/
|
|
98
113
|
handler(data, ctx) {
|
|
99
|
-
if (!this.
|
|
114
|
+
if (!this.aiEnabled) {
|
|
100
115
|
this.logger.debug('`ai_monitoring.enabled` is set to false, not creating segment.')
|
|
101
116
|
return ctx
|
|
102
117
|
}
|
|
@@ -23,10 +23,6 @@ class AiMonitoringChatSubscriber extends AiMonitoringSubscriber {
|
|
|
23
23
|
super({ agent, logger, packageName, channelName, name, trackingPrefix })
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
get streamingEnabled() {
|
|
27
|
-
return this.agent.config.ai_monitoring.streaming.enabled === true
|
|
28
|
-
}
|
|
29
|
-
|
|
30
26
|
/**
|
|
31
27
|
* Function that must be implemented by inherited subscriber to create an llm completion message.
|
|
32
28
|
*
|
|
@@ -86,7 +82,7 @@ class AiMonitoringChatSubscriber extends AiMonitoringSubscriber {
|
|
|
86
82
|
* @param {Array} params.tags used only for langchain events at the moment
|
|
87
83
|
*/
|
|
88
84
|
recordChatCompletionEvents({ ctx, request, response, err, timeOfFirstToken, metadata = {}, tags = [] }) {
|
|
89
|
-
if (!this.
|
|
85
|
+
if (!this.aiEnabled) {
|
|
90
86
|
this.logger.debug('config.ai_monitoring.enabled is set to false, not creating chat completion events.')
|
|
91
87
|
return
|
|
92
88
|
}
|
|
@@ -46,7 +46,7 @@ class AiMonitoringEmbeddingSubscriber extends AiMonitoringSubscriber {
|
|
|
46
46
|
* @param {object} params.err error if present
|
|
47
47
|
*/
|
|
48
48
|
recordEmbedding({ ctx, request, response, err }) {
|
|
49
|
-
if (!this.
|
|
49
|
+
if (!this.aiEnabled) {
|
|
50
50
|
this.logger.debug('config.ai_monitoring.enabled is set to false, not creating embedding event.')
|
|
51
51
|
return
|
|
52
52
|
}
|
|
@@ -25,7 +25,7 @@ module.exports = class AnthropicChatCreateSubscriber extends AiMonitoringChatSub
|
|
|
25
25
|
|
|
26
26
|
asyncEnd(data) {
|
|
27
27
|
const { agent, logger } = this
|
|
28
|
-
if (!this.
|
|
28
|
+
if (!this.aiEnabled) {
|
|
29
29
|
logger.debug('`ai_monitoring.enabled` is set to false, not creating chat completion events.')
|
|
30
30
|
return
|
|
31
31
|
}
|
|
@@ -22,6 +22,16 @@ class BunyanLoggerSubscriber extends BunyanSubscriber {
|
|
|
22
22
|
const self = this
|
|
23
23
|
if (this.isLogForwardingEnabled()) {
|
|
24
24
|
const logger = data.self
|
|
25
|
+
// The `Logger` constructor runs for every logger, including every
|
|
26
|
+
// `logger.child(...)`. Child loggers inherit (copy) or share the parent's
|
|
27
|
+
// `streams` array, so re-adding our forwarder on each construction would
|
|
28
|
+
// accumulate duplicate `NRLogForwarder` streams and forward a single log
|
|
29
|
+
// line multiple times. Only add it if one isn't already present.
|
|
30
|
+
const alreadyForwarding = logger.streams?.some((s) => s.name === 'NRLogForwarder')
|
|
31
|
+
if (alreadyForwarding) {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
25
35
|
logger.addStream({
|
|
26
36
|
name: 'NRLogForwarder',
|
|
27
37
|
type: 'raw',
|
|
@@ -33,7 +33,7 @@ module.exports = class GoogleAdkAgentRunSubscriber extends AiMonitoringSubscribe
|
|
|
33
33
|
// generator is created but before it is consumed by the caller.
|
|
34
34
|
end(data) {
|
|
35
35
|
const { agent, logger } = this
|
|
36
|
-
if (!this.
|
|
36
|
+
if (!this.aiEnabled) {
|
|
37
37
|
logger.debug('Google ADK instrumentation is disabled, not instrumenting runAsync.')
|
|
38
38
|
return
|
|
39
39
|
}
|
|
@@ -36,7 +36,7 @@ module.exports = class GoogleAdkToolRunSubscriber extends AiMonitoringSubscriber
|
|
|
36
36
|
|
|
37
37
|
asyncEnd(data) {
|
|
38
38
|
const { agent, logger, toolName, toolInput } = this
|
|
39
|
-
if (!this.
|
|
39
|
+
if (!this.aiEnabled) {
|
|
40
40
|
logger.debug('Google ADK instrumentation is disabled, not instrumenting FunctionTool.runAsync.')
|
|
41
41
|
return
|
|
42
42
|
}
|
|
@@ -76,11 +76,6 @@ class GoogleGenAIGenerateContentStreamSubscriber extends GoogleGenAIGenerateCont
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
asyncEnd(data) {
|
|
79
|
-
// Check config to see if ai_monitoring is still enabled
|
|
80
|
-
if (!this.enabled) {
|
|
81
|
-
this.logger.debug('`ai_monitoring.enabled` is set to false, stream will not be instrumented.')
|
|
82
|
-
return
|
|
83
|
-
}
|
|
84
79
|
if (!this.streamingEnabled) {
|
|
85
80
|
this.logger.warn(
|
|
86
81
|
'`ai_monitoring.streaming.enabled` is set to `false`, stream will not be instrumented.'
|
|
@@ -12,10 +12,6 @@ class LangchainRunnableStreamSubscriber extends LangchainRunnableSubscriber {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
asyncEnd(data) {
|
|
15
|
-
if (!this.enabled) {
|
|
16
|
-
this.logger.debug('`ai_monitoring.enabled` is set to false, stream will not be instrumented.')
|
|
17
|
-
return
|
|
18
|
-
}
|
|
19
15
|
if (!this.streamingEnabled) {
|
|
20
16
|
this.logger.debug('`ai_monitoring.streaming.enabled` is set to false, stream will not be instrumented.')
|
|
21
17
|
this.agent.metrics.getOrCreateMetric(STREAMING_DISABLED).incrementCallCount()
|
|
@@ -27,7 +27,7 @@ class LangchainToolSubscriber extends AiMonitoringSubscriber {
|
|
|
27
27
|
asyncEnd(data) {
|
|
28
28
|
// Extract data and exit early if need be.
|
|
29
29
|
const { agent, logger } = this
|
|
30
|
-
if (!this.
|
|
30
|
+
if (!this.aiEnabled) {
|
|
31
31
|
logger.debug('Langchain instrumentation is disabled, not recording Llm events.')
|
|
32
32
|
return
|
|
33
33
|
}
|
|
@@ -21,10 +21,6 @@ class LangGraphStreamSubscriber extends AiMonitoringSubscriber {
|
|
|
21
21
|
this.events = ['asyncEnd']
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
get streamingEnabled() {
|
|
25
|
-
return this.agent.config.ai_monitoring.streaming.enabled
|
|
26
|
-
}
|
|
27
|
-
|
|
28
24
|
handler(data, ctx) {
|
|
29
25
|
// Store LangGraph AI agent name and update
|
|
30
26
|
// segment name to use it.
|
|
@@ -35,10 +31,7 @@ class LangGraphStreamSubscriber extends AiMonitoringSubscriber {
|
|
|
35
31
|
|
|
36
32
|
asyncEnd(data) {
|
|
37
33
|
const { agent, logger, aiAgentName } = this
|
|
38
|
-
|
|
39
|
-
logger.debug('LangGraph instrumentation is disabled, not instrumenting stream.')
|
|
40
|
-
return
|
|
41
|
-
}
|
|
34
|
+
|
|
42
35
|
if (!this.streamingEnabled) {
|
|
43
36
|
logger.debug('LangGraph streaming instrumentation is disabled, not instrumenting stream.')
|
|
44
37
|
this.agent.metrics.getOrCreateMetric(STREAMING_DISABLED).incrementCallCount()
|
|
@@ -12,11 +12,18 @@ const NrTransport = require('./nr-winston-transport.js')
|
|
|
12
12
|
* during initial construction and on any subsequent reconfiguration.
|
|
13
13
|
*
|
|
14
14
|
* Handles `NrTransport` injection (after `configure` runs, via `end`).
|
|
15
|
+
*
|
|
16
|
+
* @property {boolean} handlingExceptions Indicates if this instrumentation has
|
|
17
|
+
* already registered a global exception handler or not. We only need a single
|
|
18
|
+
* exception handler per process.
|
|
15
19
|
*/
|
|
16
20
|
module.exports = class WinstonConfigure extends ApplicationLogsSubscriber {
|
|
17
21
|
constructor({ agent, logger }) {
|
|
18
22
|
super({ agent, logger, channelName: 'nr_configure', packageName: 'winston' })
|
|
19
23
|
this.events = ['end']
|
|
24
|
+
// we only want to assign `handleExceptions` to the first NrTransport
|
|
25
|
+
// customers could be constructing multiple logger instances
|
|
26
|
+
this.handlingExceptions = false
|
|
20
27
|
}
|
|
21
28
|
|
|
22
29
|
handler(data, ctx) {
|
|
@@ -39,11 +46,12 @@ module.exports = class WinstonConfigure extends ApplicationLogsSubscriber {
|
|
|
39
46
|
const hasNrTransport = logger.transports?.some((t) => t.name === 'newrelic')
|
|
40
47
|
if (!hasNrTransport) {
|
|
41
48
|
const nrTransport = new NrTransport({ agent: this.agent })
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
nrTransport.handleExceptions =
|
|
49
|
+
// assigning `transport.handleExceptions` adds a global `process.on('uncaughtException')`
|
|
50
|
+
// handler. Doing it for more than one NrTransport instance will cause duplicate log lines
|
|
51
|
+
if (this.handlingExceptions === false && logger.transports.length > 0) {
|
|
52
|
+
// See: https://github.com/winstonjs/winston#handling-uncaught-exceptions-with-winston
|
|
53
|
+
nrTransport.handleExceptions = true
|
|
54
|
+
this.handlingExceptions = true
|
|
47
55
|
}
|
|
48
56
|
logger.add(nrTransport)
|
|
49
57
|
}
|
|
@@ -14,12 +14,15 @@ const { truncate } = require('../../util/application-logging')
|
|
|
14
14
|
*
|
|
15
15
|
* Note*: This copies the log line so no other transports will get the
|
|
16
16
|
* mutated data.
|
|
17
|
+
*
|
|
18
|
+
* @property {string} name name of the transport
|
|
19
|
+
* @property {Agent} agent the instance of agent
|
|
20
|
+
* @property {object} config the agent configuration
|
|
21
|
+
* @property {boolean} handleExceptions set in `lib/subscribers/winston/configure.js` for the first NrTransport instance
|
|
22
|
+
* to ensure that only one is handling uncaught exceptions in winston transports
|
|
17
23
|
*/
|
|
18
24
|
class NrTransport extends TransportStream {
|
|
19
25
|
constructor(opts = {}) {
|
|
20
|
-
// set this option to have winston handle uncaught exceptions
|
|
21
|
-
// See: https://github.com/winstonjs/winston#handling-uncaught-exceptions-with-winston
|
|
22
|
-
opts.handleExceptions = true
|
|
23
26
|
super(opts)
|
|
24
27
|
this.name = 'newrelic'
|
|
25
28
|
this.agent = opts.agent
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "newrelic",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.3.0",
|
|
4
4
|
"author": "New Relic Node.js agent team <nodejs@newrelic.com>",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"contributors": [
|
|
@@ -170,7 +170,7 @@
|
|
|
170
170
|
"lint:fix": "eslint --fix .",
|
|
171
171
|
"public-docs": "jsdoc -c ./jsdoc-conf.jsonc",
|
|
172
172
|
"publish-docs": "./bin/publish-docs.sh",
|
|
173
|
-
"services": "
|
|
173
|
+
"services": "./bin/start-services.sh",
|
|
174
174
|
"services:start": "npm run services",
|
|
175
175
|
"services:stop": "docker compose down",
|
|
176
176
|
"smoke": "time borp --timeout 180000 --reporter ./test/lib/test-reporter.mjs 'test/smoke/**/*.test.js'",
|