newrelic 12.6.1 → 12.8.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 +58 -0
- package/lib/agent.js +35 -27
- package/lib/aggregators/log-aggregator.js +13 -1
- package/lib/collector/facts.js +1 -2
- package/lib/config/default.js +16 -0
- package/lib/config/index.js +30 -0
- package/lib/instrumentation/aws-sdk/v3/dynamodb.js +57 -12
- package/lib/instrumentation/aws-sdk/v3/smithy-client.js +2 -2
- package/lib/instrumentation/core/http-outbound.js +25 -1
- package/lib/instrumentation/express.js +1 -1
- package/lib/instrumentation/nr-winston-transport.js +1 -1
- package/lib/instrumentation/pino/pino.js +2 -15
- package/lib/llm-events/aws-bedrock/bedrock-command.js +13 -4
- package/lib/metrics/names.js +2 -1
- package/lib/util/application-logging.js +58 -0
- package/lib/util/label-parser.js +7 -6
- package/package.json +1 -1
package/NEWS.md
CHANGED
|
@@ -1,3 +1,61 @@
|
|
|
1
|
+
### v12.8.0 (2024-11-19)
|
|
2
|
+
|
|
3
|
+
#### Features
|
|
4
|
+
|
|
5
|
+
* Added `labels.enabled` and `labels.exclude` to config defaults ([#2727](https://github.com/newrelic/node-newrelic/pull/2727)) ([36adb13](https://github.com/newrelic/node-newrelic/commit/36adb13caad41a5df5a5fa9345b9dcb9061db969))
|
|
6
|
+
* Added new configuration options to add custom tags (labels) to logs ([#2743](https://github.com/newrelic/node-newrelic/pull/2743)) ([d20c874](https://github.com/newrelic/node-newrelic/commit/d20c874638ebb19f8888003d4402bce17e56360f))
|
|
7
|
+
* With custom tags on logs, platform engineers can easily filter, search, and correlate log data for faster and more efficient troubleshooting, improved performance, and optimized resource utilization. To learn more about this feature see the [documentation](https://docs.newrelic.com/docs/logs/logs-context/custom-tags-agent-forwarder-logs/)
|
|
8
|
+
* Added support for Bedrock Claude 3.5 invoke model user request ([#2721](https://github.com/newrelic/node-newrelic/pull/2721)) ([a901a24](https://github.com/newrelic/node-newrelic/commit/a901a2499e73e0f1142054ead53e7b99df94b201))
|
|
9
|
+
|
|
10
|
+
* Moved `entity.guid`, `entity.name`, `entity.type`, and `hostname` to `common.attributes` on logs payload instead of in every log message ([#2736](https://github.com/newrelic/node-newrelic/pull/2736)) ([a7f14de](https://github.com/newrelic/node-newrelic/commit/a7f14def489000ab7492b01155069b1a084eeb36))
|
|
11
|
+
|
|
12
|
+
#### Bug fixes
|
|
13
|
+
|
|
14
|
+
* Corrected expected shape of user input in get Prompt() ([#2741](https://github.com/newrelic/node-newrelic/pull/2741)) ([a8eef8b](https://github.com/newrelic/node-newrelic/commit/a8eef8b0f80ec2b0db5834a722a69f58a32b5676))
|
|
15
|
+
|
|
16
|
+
#### Documentation
|
|
17
|
+
|
|
18
|
+
* Updated compatibility report ([#2755](https://github.com/newrelic/node-newrelic/pull/2755)) ([459a3f1](https://github.com/newrelic/node-newrelic/commit/459a3f19ee080a3a2e560636ce087f9e91bc529e))
|
|
19
|
+
|
|
20
|
+
#### Miscellaneous chores
|
|
21
|
+
|
|
22
|
+
* Fixed test reporter ([#2751](https://github.com/newrelic/node-newrelic/pull/2751)) ([5878ef9](https://github.com/newrelic/node-newrelic/commit/5878ef9844fd56038d08e3b2f8cd9e26ba8c4c92))
|
|
23
|
+
* Improved unit test speed ([#2728](https://github.com/newrelic/node-newrelic/pull/2728)) ([9640030](https://github.com/newrelic/node-newrelic/commit/96400304e451502ce2f09b569b46eaff271fd9ab))
|
|
24
|
+
|
|
25
|
+
#### Tests
|
|
26
|
+
|
|
27
|
+
* Fixed two assertions to work against older versions of `openai` ([#2726](https://github.com/newrelic/node-newrelic/pull/2726)) ([8996fc3](https://github.com/newrelic/node-newrelic/commit/8996fc3538735d7ad2d447bc7c4221b08c6c2be5))
|
|
28
|
+
* Migrated `ioredis`, `prisma` and `undici` versioned tests to `node:test` ([#2746](https://github.com/newrelic/node-newrelic/pull/2746)) ([449ae03](https://github.com/newrelic/node-newrelic/commit/449ae03883d2a5e92d11590372993eedc976ab99))
|
|
29
|
+
* Migrated `memcached`, `nestjs`, and `restify` versioned tests to `node:test` ([#2752](https://github.com/newrelic/node-newrelic/pull/2752)) ([653f614](https://github.com/newrelic/node-newrelic/commit/653f61410a0258683ac104cd8b8b2e7ae7c61043))
|
|
30
|
+
* Migrated `nextjs` versioned tests to `node:test` ([#2749](https://github.com/newrelic/node-newrelic/pull/2749)) ([d385af6](https://github.com/newrelic/node-newrelic/commit/d385af69c84a5425cee9ff19eaeadcba9e63a761))
|
|
31
|
+
* Migrated `redis` versioned tests to `node:test` ([#2725](https://github.com/newrelic/node-newrelic/pull/2725)) ([9752827](https://github.com/newrelic/node-newrelic/commit/97528270c70ecaac9161f4eb0cc78ffcc482be36))
|
|
32
|
+
* Updated `hapi` tests to `node:test` ([#2722](https://github.com/newrelic/node-newrelic/pull/2722)) ([636899b](https://github.com/newrelic/node-newrelic/commit/636899b487e196572a3bc0970e7338d0c9f113cf))
|
|
33
|
+
* Updated `kafkajs`, `langchain`, & `openai` tests to `node:test` ([#2723](https://github.com/newrelic/node-newrelic/pull/2723)) ([b6e4c5d](https://github.com/newrelic/node-newrelic/commit/b6e4c5d4c59032a615fea6d12623a881a6211917))
|
|
34
|
+
* Updated `koa` tests to `node:test` ([#2744](https://github.com/newrelic/node-newrelic/pull/2744)) ([86c22a4](https://github.com/newrelic/node-newrelic/commit/86c22a4813ec5a63ae8bd04f7d9d013979ac34ed))
|
|
35
|
+
* Updated `mongodb` tests to `node:test` ([#2738](https://github.com/newrelic/node-newrelic/pull/2738)) ([295e7e6](https://github.com/newrelic/node-newrelic/commit/295e7e6441b7220022ce4fcaaf33054945a678b0))
|
|
36
|
+
* Updated `pg` and `pg-esm` tests to `node:test` ([#2750](https://github.com/newrelic/node-newrelic/pull/2750)) ([40aea36](https://github.com/newrelic/node-newrelic/commit/40aea36320d15b201800431268be2c3d4c794a7b))
|
|
37
|
+
|
|
38
|
+
### v12.7.0 (2024-11-11)
|
|
39
|
+
|
|
40
|
+
#### Features
|
|
41
|
+
|
|
42
|
+
* Added `cloud.resource_id` attribute to dynamo spans ([#2701](https://github.com/newrelic/node-newrelic/pull/2701)) ([904f41b](https://github.com/newrelic/node-newrelic/commit/904f41b26637394a24aa13f31ff94b100ae6d090))
|
|
43
|
+
* Enhance Proxy Request Handling to Display Actual External URLs ([#2698](https://github.com/newrelic/node-newrelic/pull/2698)) ([3ef7bbe](https://github.com/newrelic/node-newrelic/commit/3ef7bbe595860234c021a02235e6fd0615da5f69))
|
|
44
|
+
* Thanks for the contribution @mstarzec386
|
|
45
|
+
|
|
46
|
+
#### Documentation
|
|
47
|
+
|
|
48
|
+
* Updated compatibility report ([#2712](https://github.com/newrelic/node-newrelic/pull/2712)) ([82f0e98](https://github.com/newrelic/node-newrelic/commit/82f0e9806c88d14cba2e0cdf47593e036107bd7d)) ([#2699](https://github.com/newrelic/node-newrelic/pull/2699)) ([4432c42](https://github.com/newrelic/node-newrelic/commit/4432c4215d68cc79333ee3828f1ecd55476c63d8))
|
|
49
|
+
|
|
50
|
+
#### Miscellaneous chores
|
|
51
|
+
|
|
52
|
+
* Added a benchmark script for our sql parser ([#2708](https://github.com/newrelic/node-newrelic/pull/2708)) ([9b6de68](https://github.com/newrelic/node-newrelic/commit/9b6de6852747230b87a9873faffba6e5b39669f3))
|
|
53
|
+
* Updated express-esm, generic-pool, grpc, & grpc-esm tests to node:test ([#2702](https://github.com/newrelic/node-newrelic/pull/2702)) ([a229bbf](https://github.com/newrelic/node-newrelic/commit/a229bbf0dd92c43fb2da077d8dce831b84c0c972))
|
|
54
|
+
|
|
55
|
+
#### Tests
|
|
56
|
+
|
|
57
|
+
* Migrated `mysql` and `mysql2` versioned tests to `node:test` ([#2711](https://github.com/newrelic/node-newrelic/pull/2711)) ([fc767e0](https://github.com/newrelic/node-newrelic/commit/fc767e08d8b546d14c53c07bc2cfe65f3fb55368))
|
|
58
|
+
|
|
1
59
|
### v12.6.1 (2024-11-07)
|
|
2
60
|
|
|
3
61
|
#### Features
|
package/lib/agent.js
CHANGED
|
@@ -40,6 +40,7 @@ const {
|
|
|
40
40
|
} = require('./util/attributes')
|
|
41
41
|
const synthetics = require('./synthetics')
|
|
42
42
|
const Harvester = require('./harvester')
|
|
43
|
+
const { createFeatureUsageMetrics } = require('./util/application-logging')
|
|
43
44
|
|
|
44
45
|
// Map of valid states to whether or not data collection is valid
|
|
45
46
|
const STATES = {
|
|
@@ -405,7 +406,7 @@ Agent.prototype.startStreaming = function startStreaming() {
|
|
|
405
406
|
*/
|
|
406
407
|
Agent.prototype.onConnect = function onConnect(shouldImmediatelyHarvest, callback) {
|
|
407
408
|
this.harvester.update(this.config)
|
|
408
|
-
|
|
409
|
+
createFeatureUsageMetrics(this)
|
|
409
410
|
|
|
410
411
|
if (this.config.certificates && this.config.certificates.length > 0) {
|
|
411
412
|
this.metrics.getOrCreateMetric(NAMES.FEATURES.CERTIFICATES).incrementCallCount()
|
|
@@ -821,6 +822,8 @@ Agent.prototype._listenForConfigChanges = function _listenForConfigChanges() {
|
|
|
821
822
|
* This method returns an object with the following keys/data:
|
|
822
823
|
* - `trace.id`: The current trace ID
|
|
823
824
|
* - `span.id`: The current span ID
|
|
825
|
+
*
|
|
826
|
+
* If `excludeServiceLinks` is false it will also include the following:
|
|
824
827
|
* - `entity.name`: The application name specified in the connect request as
|
|
825
828
|
* app_name. If multiple application names are specified this will only be
|
|
826
829
|
* the first name
|
|
@@ -830,17 +833,14 @@ Agent.prototype._listenForConfigChanges = function _listenForConfigChanges() {
|
|
|
830
833
|
* utilization.full_hostname. If utilization.full_hostname is null or empty,
|
|
831
834
|
* this will be the hostname specified in the connect request as host.
|
|
832
835
|
*
|
|
836
|
+
* @param {boolean} excludeServiceLinks flag to indicate if you should include `entity.guid`, `entity.type`, `entity.name` and `hostname` from linking metadata
|
|
833
837
|
* @returns {object} The LinkingMetadata object with the data above
|
|
834
838
|
*/
|
|
835
|
-
Agent.prototype.getLinkingMetadata = function getLinkingMetadata() {
|
|
839
|
+
Agent.prototype.getLinkingMetadata = function getLinkingMetadata(excludeServiceLinks = false) {
|
|
836
840
|
const segment = this.tracer.getSegment()
|
|
837
841
|
const config = this.config
|
|
838
842
|
|
|
839
|
-
const linkingMetadata = {
|
|
840
|
-
'entity.name': config.applications()[0],
|
|
841
|
-
'entity.type': 'SERVICE',
|
|
842
|
-
'hostname': config.getHostnameSafe()
|
|
843
|
-
}
|
|
843
|
+
const linkingMetadata = {}
|
|
844
844
|
|
|
845
845
|
if (config.distributed_tracing.enabled && segment) {
|
|
846
846
|
linkingMetadata['trace.id'] = segment.transaction.traceId
|
|
@@ -852,10 +852,37 @@ Agent.prototype.getLinkingMetadata = function getLinkingMetadata() {
|
|
|
852
852
|
logger.debug('getLinkingMetadata with no active transaction')
|
|
853
853
|
}
|
|
854
854
|
|
|
855
|
+
if (!excludeServiceLinks) {
|
|
856
|
+
this.getServiceLinkingMetadata(linkingMetadata)
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
return linkingMetadata
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
/**
|
|
863
|
+
* This methods returns an object with the following keys/data:
|
|
864
|
+
* - `entity.name`: The application name specified in the connect request as
|
|
865
|
+
* app_name. If multiple application names are specified this will only be
|
|
866
|
+
* the first name
|
|
867
|
+
* - `entity.type`: The string "SERVICE"
|
|
868
|
+
* - `entity.guid`: The entity ID returned in the connect reply as entity_guid
|
|
869
|
+
* - `hostname`: The hostname as specified in the connect request as
|
|
870
|
+
* utilization.full_hostname. If utilization.full_hostname is null or empty,
|
|
871
|
+
* this will be the hostname specified in the connect request as host.
|
|
872
|
+
*
|
|
873
|
+
* @param {object} linkingMetadata object to add service linking keys
|
|
874
|
+
* @returns {object} with service linking metadata
|
|
875
|
+
*/
|
|
876
|
+
Agent.prototype.getServiceLinkingMetadata = function getServiceLinkingMetadata(
|
|
877
|
+
linkingMetadata = {}
|
|
878
|
+
) {
|
|
879
|
+
const config = this.config
|
|
855
880
|
if (config.entity_guid) {
|
|
856
881
|
linkingMetadata['entity.guid'] = config.entity_guid
|
|
857
882
|
}
|
|
858
|
-
|
|
883
|
+
linkingMetadata['entity.name'] = config.applications()[0]
|
|
884
|
+
linkingMetadata['entity.type'] = 'SERVICE'
|
|
885
|
+
linkingMetadata.hostname = config.getHostnameSafe()
|
|
859
886
|
return linkingMetadata
|
|
860
887
|
}
|
|
861
888
|
|
|
@@ -927,23 +954,4 @@ function generateEventHarvestSupportMetrics(agent, harvestConfig) {
|
|
|
927
954
|
}
|
|
928
955
|
}
|
|
929
956
|
|
|
930
|
-
/**
|
|
931
|
-
* Increments the call counts of application logging supportability metrics
|
|
932
|
-
* on every connect cycle
|
|
933
|
-
*
|
|
934
|
-
* @param {object} agent Instantiation of Node.js agent
|
|
935
|
-
*/
|
|
936
|
-
function generateLoggingSupportMetrics(agent) {
|
|
937
|
-
const loggingConfig = agent.config.application_logging
|
|
938
|
-
const logNames = NAMES.LOGGING
|
|
939
|
-
|
|
940
|
-
const configKeys = ['metrics', 'forwarding', 'local_decorating']
|
|
941
|
-
configKeys.forEach((configValue) => {
|
|
942
|
-
const configFlag =
|
|
943
|
-
loggingConfig.enabled && loggingConfig[`${configValue}`].enabled ? 'enabled' : 'disabled'
|
|
944
|
-
const metricName = logNames[`${configValue.toUpperCase()}`]
|
|
945
|
-
agent.metrics.getOrCreateMetric(`${metricName}${configFlag}`).incrementCallCount()
|
|
946
|
-
})
|
|
947
|
-
}
|
|
948
|
-
|
|
949
957
|
module.exports = Agent
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
'use strict'
|
|
7
7
|
|
|
8
8
|
const logger = require('../logger').child({ component: 'logs_aggregator' })
|
|
9
|
+
const { isLogLabelingEnabled } = require('../../lib/util/application-logging')
|
|
9
10
|
const EventAggregator = require('./event-aggregator')
|
|
10
11
|
|
|
11
12
|
const NAMES = require('../metrics/names')
|
|
@@ -56,7 +57,18 @@ class LogAggregator extends EventAggregator {
|
|
|
56
57
|
return
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
let commonAttrs = this.agent.getServiceLinkingMetadata()
|
|
61
|
+
|
|
62
|
+
if (isLogLabelingEnabled(this.agent.config)) {
|
|
63
|
+
commonAttrs = { ...commonAttrs, ...this.agent.config.loggingLabels }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return [
|
|
67
|
+
{
|
|
68
|
+
common: { attributes: commonAttrs },
|
|
69
|
+
logs: formattedLogs
|
|
70
|
+
}
|
|
71
|
+
]
|
|
60
72
|
}
|
|
61
73
|
|
|
62
74
|
add(logLine) {
|
package/lib/collector/facts.js
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
const fetchSystemInfo = require('../system-info')
|
|
9
9
|
const defaultLogger = require('../logger').child({ component: 'facts' })
|
|
10
10
|
const os = require('os')
|
|
11
|
-
const parseLabels = require('../util/label-parser')
|
|
12
11
|
|
|
13
12
|
module.exports = facts
|
|
14
13
|
|
|
@@ -49,7 +48,7 @@ async function facts(agent, callback, { logger = defaultLogger } = {}) {
|
|
|
49
48
|
environment: environment,
|
|
50
49
|
settings: agent.config.publicSettings(),
|
|
51
50
|
high_security: agent.config.high_security,
|
|
52
|
-
labels:
|
|
51
|
+
labels: agent.config.parsedLabels,
|
|
53
52
|
metadata: Object.keys(process.env).reduce((obj, key) => {
|
|
54
53
|
if (key.startsWith('NEW_RELIC_METADATA_')) {
|
|
55
54
|
obj[key] = process.env[key]
|
package/lib/config/default.js
CHANGED
|
@@ -1185,6 +1185,22 @@ defaultConfig.definition = () => ({
|
|
|
1185
1185
|
max_samples_stored: {
|
|
1186
1186
|
formatter: int,
|
|
1187
1187
|
default: 10000
|
|
1188
|
+
},
|
|
1189
|
+
labels: {
|
|
1190
|
+
/**
|
|
1191
|
+
* If `true`, the agent attaches labels to log records.
|
|
1192
|
+
*/
|
|
1193
|
+
enabled: {
|
|
1194
|
+
formatter: boolean,
|
|
1195
|
+
default: false
|
|
1196
|
+
},
|
|
1197
|
+
/**
|
|
1198
|
+
* A case-insensitive array containing the labels to exclude from log records.
|
|
1199
|
+
*/
|
|
1200
|
+
exclude: {
|
|
1201
|
+
formatter: array,
|
|
1202
|
+
default: []
|
|
1203
|
+
}
|
|
1188
1204
|
}
|
|
1189
1205
|
},
|
|
1190
1206
|
metrics: {
|
package/lib/config/index.js
CHANGED
|
@@ -24,6 +24,7 @@ const harvestConfigValidator = require('./harvest-config-validator')
|
|
|
24
24
|
const mergeServerConfig = new MergeServerConfig()
|
|
25
25
|
const { boolean: isTruthular } = require('./formatters')
|
|
26
26
|
const configDefinition = definition()
|
|
27
|
+
const parseLabels = require('../util/label-parser')
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* CONSTANTS -- we gotta lotta 'em
|
|
@@ -195,9 +196,38 @@ function Config(config) {
|
|
|
195
196
|
|
|
196
197
|
// 9. Set instance attribute filter using updated context
|
|
197
198
|
this.attributeFilter = new AttributeFilter(this)
|
|
199
|
+
|
|
200
|
+
// 10. Setup labels for both `collector/facts` and application logging
|
|
201
|
+
this.parsedLabels = parseLabels(this.labels, logger)
|
|
202
|
+
this.loggingLabels = this._setApplicationLoggingLabels()
|
|
198
203
|
}
|
|
199
204
|
util.inherits(Config, EventEmitter)
|
|
200
205
|
|
|
206
|
+
/**
|
|
207
|
+
* Compares the labels list to the application logging excluded label list and removes any labels that need to be excluded.
|
|
208
|
+
* Then prefixing each label with "tags."
|
|
209
|
+
*
|
|
210
|
+
* assigns labels to `config.loggingLabels`
|
|
211
|
+
*/
|
|
212
|
+
Config.prototype._setApplicationLoggingLabels = function setApplicationLoggingLabels() {
|
|
213
|
+
if (this.application_logging.forwarding.labels.enabled === false) {
|
|
214
|
+
return
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
this.application_logging.forwarding.labels.exclude =
|
|
218
|
+
this.application_logging.forwarding.labels.exclude.map((k) => k.toLowerCase())
|
|
219
|
+
|
|
220
|
+
return this.parsedLabels.reduce((filteredLabels, label) => {
|
|
221
|
+
if (
|
|
222
|
+
!this.application_logging.forwarding.labels.exclude.includes(label.label_type.toLowerCase())
|
|
223
|
+
) {
|
|
224
|
+
filteredLabels[`tags.${label.label_type}`] = label.label_value
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return filteredLabels
|
|
228
|
+
}, {})
|
|
229
|
+
}
|
|
230
|
+
|
|
201
231
|
/**
|
|
202
232
|
* Because this module and logger depend on each other, the logger needs
|
|
203
233
|
* a way to inject the actual logger instance once it's constructed.
|
|
@@ -72,21 +72,66 @@ function dynamoMiddleware(shim, config, next, context) {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Wraps the deserialize middleware step to add the
|
|
77
|
+
* cloud.resource_id segment attributes for the AWS command
|
|
78
|
+
*
|
|
79
|
+
* @param {Shim} shim
|
|
80
|
+
* @param {Object} config AWS command configuration
|
|
81
|
+
* @param {function} next next function in middleware chain
|
|
82
|
+
* @returns {function}
|
|
83
|
+
*/
|
|
84
|
+
function resourceIdMiddlerware(shim, config, next) {
|
|
85
|
+
return async function wrappedResourceIdMiddlerware(args) {
|
|
86
|
+
let region
|
|
87
|
+
try {
|
|
88
|
+
region = await config.region()
|
|
89
|
+
const segment = shim.getSegment()
|
|
90
|
+
|
|
91
|
+
const accountId = shim.agent.config.cloud.aws.account_id
|
|
92
|
+
|
|
93
|
+
if (accountId) {
|
|
94
|
+
const attributes = segment.getAttributes()
|
|
95
|
+
segment.addAttribute(
|
|
96
|
+
'cloud.resource_id',
|
|
97
|
+
`arn:aws:dynamodb:${region}:${accountId}:table/${attributes.collection}`
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
} catch (err) {
|
|
101
|
+
shim.logger.debug(err, 'Failed to add AWS cloud resource id to segment')
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return next(args)
|
|
87
105
|
}
|
|
88
106
|
}
|
|
89
107
|
|
|
108
|
+
const dynamoMiddlewareConfig = [
|
|
109
|
+
{
|
|
110
|
+
middleware: dynamoMiddleware,
|
|
111
|
+
init(shim) {
|
|
112
|
+
shim.setDatastore(shim.DYNAMODB)
|
|
113
|
+
return true
|
|
114
|
+
},
|
|
115
|
+
type: InstrumentationDescriptor.TYPE_DATASTORE,
|
|
116
|
+
config: {
|
|
117
|
+
name: 'NewRelicDynamoMiddleware',
|
|
118
|
+
step: 'initialize',
|
|
119
|
+
priority: 'high',
|
|
120
|
+
override: true
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
middleware: resourceIdMiddlerware,
|
|
125
|
+
type: InstrumentationDescriptor.TYPE_GENERIC,
|
|
126
|
+
config: {
|
|
127
|
+
name: 'NewRelicCloudResource',
|
|
128
|
+
step: 'deserialize',
|
|
129
|
+
priority: 'low',
|
|
130
|
+
override: true
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
|
|
90
135
|
module.exports = {
|
|
91
136
|
dynamoMiddlewareConfig
|
|
92
137
|
}
|
|
@@ -17,8 +17,8 @@ const middlewareByClient = {
|
|
|
17
17
|
BedrockRuntime: [...middlewareConfig, bedrockMiddlewareConfig],
|
|
18
18
|
SNS: [...middlewareConfig, snsMiddlewareConfig],
|
|
19
19
|
SQS: [...middlewareConfig, sqsMiddlewareConfig],
|
|
20
|
-
DynamoDB: [...middlewareConfig, dynamoMiddlewareConfig],
|
|
21
|
-
DynamoDBDocument: [...middlewareConfig, dynamoMiddlewareConfig]
|
|
20
|
+
DynamoDB: [...middlewareConfig, ...dynamoMiddlewareConfig],
|
|
21
|
+
DynamoDBDocument: [...middlewareConfig, ...dynamoMiddlewareConfig]
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
module.exports = function instrumentSmithyClient(shim, smithyClientExport) {
|
|
@@ -15,6 +15,7 @@ const copy = require('../../util/copy')
|
|
|
15
15
|
const symbols = require('../../symbols')
|
|
16
16
|
const http = require('http')
|
|
17
17
|
const synthetics = require('../../synthetics')
|
|
18
|
+
const { URL } = require('node:url')
|
|
18
19
|
|
|
19
20
|
const NAMES = require('../../metrics/names')
|
|
20
21
|
const DEFAULT_HOST = 'localhost'
|
|
@@ -93,6 +94,27 @@ function extractHostPort(opts) {
|
|
|
93
94
|
return { host, hostname, port }
|
|
94
95
|
}
|
|
95
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Extracts the host, hostname, and port from HTTP request options when using a proxy
|
|
99
|
+
*
|
|
100
|
+
* @param {object} opts HTTP request options
|
|
101
|
+
* @returns {object|null} { host, hostname, port } if proxy request is detected, or null otherwise.
|
|
102
|
+
*/
|
|
103
|
+
function extractHostPortViaProxy(opts) {
|
|
104
|
+
const pathname = opts.pathname || opts.path
|
|
105
|
+
|
|
106
|
+
if (pathname && (pathname.startsWith('https://') || pathname.startsWith('http://'))) {
|
|
107
|
+
const url = new URL(pathname)
|
|
108
|
+
return {
|
|
109
|
+
host: url.host,
|
|
110
|
+
hostname: url.hostname,
|
|
111
|
+
port: url.port || url.protocol === 'https:' ? '443' : '80'
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return null
|
|
116
|
+
}
|
|
117
|
+
|
|
96
118
|
/**
|
|
97
119
|
* Instruments an outbound HTTP request.
|
|
98
120
|
*
|
|
@@ -103,7 +125,9 @@ function extractHostPort(opts) {
|
|
|
103
125
|
*/
|
|
104
126
|
module.exports = function instrumentOutbound(agent, opts, makeRequest) {
|
|
105
127
|
opts = parseOpts(opts)
|
|
106
|
-
|
|
128
|
+
|
|
129
|
+
const viaProxy = extractHostPortViaProxy(opts)
|
|
130
|
+
const { host, hostname, port } = viaProxy ? viaProxy : extractHostPort(opts)
|
|
107
131
|
|
|
108
132
|
if (!hostname || port < 1) {
|
|
109
133
|
logger.warn('Invalid host name (%s) or port (%s) for outbound request.', hostname, port)
|
|
@@ -127,7 +127,7 @@ function wrapResponse(shim, response) {
|
|
|
127
127
|
if (!shim.isFunction(cb)) {
|
|
128
128
|
++cbIdx
|
|
129
129
|
cb = function defaultRenderCB(err, str) {
|
|
130
|
-
//
|
|
130
|
+
// https://github.com/expressjs/express/blob/4.x/lib/response.js#L961-L962
|
|
131
131
|
if (err) {
|
|
132
132
|
return res.req.next(err)
|
|
133
133
|
}
|
|
@@ -35,7 +35,7 @@ class NrTransport extends TransportStream {
|
|
|
35
35
|
* @param {Function} callback callback to invoke once we are done
|
|
36
36
|
*/
|
|
37
37
|
log(logLine, callback) {
|
|
38
|
-
const metadata = this.agent.getLinkingMetadata()
|
|
38
|
+
const metadata = this.agent.getLinkingMetadata(true)
|
|
39
39
|
const formattedLine = reformatLogLine(logLine, metadata)
|
|
40
40
|
this.agent.logs.add(formattedLine)
|
|
41
41
|
callback()
|
|
@@ -45,7 +45,6 @@ module.exports = function instrument(shim, tools) {
|
|
|
45
45
|
* @param {object} params.tools exported `pino/lib/tools`
|
|
46
46
|
*/
|
|
47
47
|
function wrapAsJson({ shim, tools }) {
|
|
48
|
-
const symbols = shim.require('./lib/symbols')
|
|
49
48
|
const { agent } = shim
|
|
50
49
|
const { config, metrics } = agent
|
|
51
50
|
|
|
@@ -88,12 +87,10 @@ function wrapAsJson({ shim, tools }) {
|
|
|
88
87
|
const logLine = asJson.apply(this, args)
|
|
89
88
|
|
|
90
89
|
if (isLogForwardingEnabled(config, agent)) {
|
|
91
|
-
const chindings = this[symbols.chindingsSym]
|
|
92
90
|
const formatLogLine = reformatLogLine({
|
|
93
91
|
msg: useMergeObj === true ? args[0].msg : args[1],
|
|
94
92
|
logLine,
|
|
95
93
|
agent,
|
|
96
|
-
chindings,
|
|
97
94
|
level,
|
|
98
95
|
logger: shim.logger
|
|
99
96
|
})
|
|
@@ -114,22 +111,12 @@ function wrapAsJson({ shim, tools }) {
|
|
|
114
111
|
* @param {object} params.logLine log line
|
|
115
112
|
* @param {string} params.msg message of log line
|
|
116
113
|
* @param {object} params.agent instance of agent
|
|
117
|
-
* @param {string} params.chindings serialized string of all common log line data
|
|
118
114
|
* @param {string} params.level log level
|
|
119
115
|
* @param {object} params.logger instance of agent logger
|
|
120
116
|
* @returns {function} wrapped log formatter function
|
|
121
117
|
*/
|
|
122
|
-
function reformatLogLine({ logLine, msg, agent,
|
|
123
|
-
const metadata = agent.getLinkingMetadata()
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* pino adds this already for us at times
|
|
127
|
-
* since asJson manually constructs the json string,
|
|
128
|
-
* it will have hostname twice if we do not delete ours.
|
|
129
|
-
*/
|
|
130
|
-
if (chindings.includes('hostname')) {
|
|
131
|
-
delete metadata.hostname
|
|
132
|
-
}
|
|
118
|
+
function reformatLogLine({ logLine, msg, agent, level, logger }) {
|
|
119
|
+
const metadata = agent.getLinkingMetadata(true)
|
|
133
120
|
|
|
134
121
|
const agentMeta = Object.assign({}, { timestamp: Date.now() }, metadata)
|
|
135
122
|
// eslint-disable-next-line eqeqeq
|
|
@@ -84,10 +84,19 @@ class BedrockCommand {
|
|
|
84
84
|
) {
|
|
85
85
|
result = this.#body.prompt
|
|
86
86
|
} else if (this.isClaude3() === true) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
const collected = []
|
|
88
|
+
for (const message of this.#body?.messages) {
|
|
89
|
+
if (message?.role === 'assistant') {
|
|
90
|
+
continue
|
|
91
|
+
}
|
|
92
|
+
if (typeof message?.content === 'string') {
|
|
93
|
+
collected.push(message?.content)
|
|
94
|
+
continue
|
|
95
|
+
}
|
|
96
|
+
const mappedMsgObj = message?.content.map((msgContent) => msgContent.text)
|
|
97
|
+
collected.push(mappedMsgObj)
|
|
98
|
+
}
|
|
99
|
+
result = collected.join(' ')
|
|
91
100
|
}
|
|
92
101
|
return result
|
|
93
102
|
}
|
package/lib/metrics/names.js
CHANGED
|
@@ -337,7 +337,8 @@ const LOGGING = {
|
|
|
337
337
|
SENT: `${LOGGING_FORWARDING_PREFIX}/Sent`,
|
|
338
338
|
FORWARDING: `${LOGGING_FORWARDING_PREFIX}/${NODEJS.PREFIX}`,
|
|
339
339
|
METRICS: `${SUPPORTABILITY.LOGGING}/Metrics/${NODEJS.PREFIX}`,
|
|
340
|
-
LOCAL_DECORATING: `${SUPPORTABILITY.LOGGING}/LocalDecorating/${NODEJS.PREFIX}
|
|
340
|
+
LOCAL_DECORATING: `${SUPPORTABILITY.LOGGING}/LocalDecorating/${NODEJS.PREFIX}`,
|
|
341
|
+
LABELS: `${SUPPORTABILITY.LOGGING}/Labels/${NODEJS.PREFIX}`
|
|
341
342
|
}
|
|
342
343
|
|
|
343
344
|
const KAFKA = {
|
|
@@ -75,6 +75,16 @@ utils.isLogForwardingEnabled = function isLogForwardingEnabled(config, agent) {
|
|
|
75
75
|
)
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Checks if application_logging.forwarding.labels is enabled
|
|
80
|
+
*
|
|
81
|
+
* @param {object} config agent config
|
|
82
|
+
* @returns {boolean} is labeling enabled
|
|
83
|
+
*/
|
|
84
|
+
utils.isLogLabelingEnabled = function isLogLabelingEnabled(config) {
|
|
85
|
+
return !!config.application_logging.forwarding.labels.enabled
|
|
86
|
+
}
|
|
87
|
+
|
|
78
88
|
/**
|
|
79
89
|
* Increments both `Logging/lines` and `Logging/lines/<level>` call count
|
|
80
90
|
*
|
|
@@ -108,3 +118,51 @@ function getLogLevel(level) {
|
|
|
108
118
|
}
|
|
109
119
|
return logLevel
|
|
110
120
|
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Increments the enabled/disabled metrics for the top line application logging features:
|
|
124
|
+
* 1. Supportability/Logging/Metrics/Nodejs/<enabled|disabled>
|
|
125
|
+
* 2. Supportability/Logging/Forwarding/Nodejs/<enabled|disabled>
|
|
126
|
+
* 3. Supportability/Logging/LocalDecorating/Nodejs/<enabled|disabled>
|
|
127
|
+
* 4. Supportability/Logging/Labels/Nodejs/<enabled|disabled>
|
|
128
|
+
*
|
|
129
|
+
* Run in `agent.onConnect`
|
|
130
|
+
*
|
|
131
|
+
* @param {Agent} agent instance
|
|
132
|
+
*/
|
|
133
|
+
utils.createFeatureUsageMetrics = function createFeatureUsageMetrics(agent) {
|
|
134
|
+
const { config, metrics } = agent
|
|
135
|
+
const loggingConfig = config.application_logging.enabled
|
|
136
|
+
metrics
|
|
137
|
+
.getOrCreateMetric(
|
|
138
|
+
`${LOGGING.METRICS}${
|
|
139
|
+
loggingConfig && config.application_logging.metrics.enabled ? 'enabled' : 'disabled'
|
|
140
|
+
}`
|
|
141
|
+
)
|
|
142
|
+
.incrementCallCount()
|
|
143
|
+
metrics
|
|
144
|
+
.getOrCreateMetric(
|
|
145
|
+
`${LOGGING.FORWARDING}${
|
|
146
|
+
loggingConfig && config.application_logging.forwarding.enabled ? 'enabled' : 'disabled'
|
|
147
|
+
}`
|
|
148
|
+
)
|
|
149
|
+
.incrementCallCount()
|
|
150
|
+
metrics
|
|
151
|
+
.getOrCreateMetric(
|
|
152
|
+
`${LOGGING.LOCAL_DECORATING}${
|
|
153
|
+
loggingConfig && config.application_logging.local_decorating.enabled
|
|
154
|
+
? 'enabled'
|
|
155
|
+
: 'disabled'
|
|
156
|
+
}`
|
|
157
|
+
)
|
|
158
|
+
.incrementCallCount()
|
|
159
|
+
metrics
|
|
160
|
+
.getOrCreateMetric(
|
|
161
|
+
`${LOGGING.LABELS}${
|
|
162
|
+
loggingConfig && config.application_logging.forwarding.labels.enabled
|
|
163
|
+
? 'enabled'
|
|
164
|
+
: 'disabled'
|
|
165
|
+
}`
|
|
166
|
+
)
|
|
167
|
+
.incrementCallCount()
|
|
168
|
+
}
|
package/lib/util/label-parser.js
CHANGED
|
@@ -11,10 +11,11 @@ module.exports.fromMap = fromMap
|
|
|
11
11
|
|
|
12
12
|
// this creates a copy of trim that can be used with map
|
|
13
13
|
const trim = Function.prototype.call.bind(String.prototype.trim)
|
|
14
|
-
const logger = require('../logger').child({ component: 'label-parser' })
|
|
15
14
|
const stringify = require('json-stringify-safe')
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
// pass in parent logger to avoid circular deps
|
|
17
|
+
function parse(labels, parentLogger) {
|
|
18
|
+
const logger = parentLogger.child({ component: 'label-parser' })
|
|
18
19
|
let results
|
|
19
20
|
|
|
20
21
|
if (!labels) {
|
|
@@ -25,8 +26,8 @@ function parse(labels) {
|
|
|
25
26
|
results = fromMap(labels)
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
results.warnings.forEach(function logWarnings(
|
|
29
|
-
logger.warn(
|
|
29
|
+
results.warnings.forEach(function logWarnings(message) {
|
|
30
|
+
logger.warn(message)
|
|
30
31
|
})
|
|
31
32
|
|
|
32
33
|
return results.labels
|
|
@@ -106,11 +107,11 @@ function fromMap(map) {
|
|
|
106
107
|
try {
|
|
107
108
|
warnings.unshift('Partially Invalid Label Setting: ' + stringify(map))
|
|
108
109
|
} catch (err) {
|
|
109
|
-
|
|
110
|
+
warnings.unshift('Failed to stringify labels: ' + err.message)
|
|
110
111
|
}
|
|
111
112
|
}
|
|
112
113
|
|
|
113
|
-
return { labels
|
|
114
|
+
return { labels, warnings }
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
function truncate(str, max) {
|