newrelic 8.10.0 → 8.11.2

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,28 +1,73 @@
1
- ### v8.10.0 (2022-04-18)
1
+ ### v8.11.2 (2022-05-23)
2
2
 
3
- * Added instrumentation for `mysql2/promise`.
4
- * This previously only existed in our standalone `@newrelic/mysql`, but now gives feature partiy between the two.
3
+ * Fixed winston instrumentation to no longer coerce every log line to be json.
5
4
 
6
- * Removed unused native CPU metric sampler. This logic was no longer getting touched if running Node.js > 6.1.0.
5
+ ### v8.11.1 (2022-05-13)
7
6
 
8
- * Fixed promise interceptor from re-throwing errors.
9
-
10
- * Added transaction naming documentation ported from a discussion forum post: https://discuss.newrelic.com/t/relic-solution-the-philosophy-of-naming-your-node-agent-transactions/.
11
-
12
- * Added `promises.tap.js` to mysql2 versioned tests.
7
+ * Fixed an issue with winston instrumentation that caused agent to crash when creating a winston logger from an existing instantiated logger.
13
8
 
9
+ ### v8.11.0 (2022-05-11)
10
+
11
+ * Added application logging for Winston in the Node.js agent
12
+
13
+ * Ability to forward logs, send log metrics, and perform local log decoration
14
+
15
+ * Added application log aggregator to collect logs with adaptive sampling and appropriate max samples stored.
16
+
17
+ * Added `application_logging` configuration and env vars with appropriate defaults.
18
+
19
+ * Added `application_logging.enabled` configuration value, defaulting to true.
20
+
21
+ * Set `application_logging.forwarding.enabled` to false when High Security Mode (HSM) is set.
22
+
23
+ * Enabled log forwarding by default in the example config.
24
+
25
+ * Added sent, seen and dropped metrics that collected on every harvest cycle around log lines.
26
+
27
+ * Added supportability metrics for some popular logging frameworks.
28
+
29
+ * Added supportability metrics to record if the logging features are enabled.
30
+
31
+ * Added a storage mechanisms to transactions to keep logs until transaction ends.
32
+
33
+ * Removed distributed tracing setting from example config
34
+
35
+ * Fixed a typo in lib/instrumentation/core/child_process.js
36
+ Thanks to Eito Katagiri (@eitoball) for the contribution
37
+
38
+ * Support automatic instrumentation of Redis v4
39
+
40
+ * Bumped [moment](https://github.com/moment/moment) from 2.29.1 to 2.29.2.
41
+
42
+ * Bumped `tap` to 16.x.
43
+
44
+ * Updated `ansi-regex` to resolve a dev dependency audit warning.
45
+
46
+ ### v8.10.0 (2022-04-18)
47
+
48
+ * Added instrumentation for `mysql2/promise`.
49
+ * This previously only existed in our standalone `@newrelic/mysql`, but now gives feature partiy between the two.
50
+
51
+ * Removed unused native CPU metric sampler. This logic was no longer getting touched if running Node.js > 6.1.0.
52
+
53
+ * Fixed promise interceptor from re-throwing errors.
54
+
55
+ * Added transaction naming documentation ported from a discussion forum post: https://discuss.newrelic.com/t/relic-solution-the-philosophy-of-naming-your-node-agent-transactions/.
56
+
57
+ * Added `promises.tap.js` to mysql2 versioned tests.
58
+
14
59
  * Updated @newrelic/test-utilities to latest.
15
- * Removed unused test file in restify versioned tests.
16
- * Added `--strict` flag to versioned test runner to properly fail CI runs when test files are not included.
17
-
18
- ### v8.9.1 (2022-03-22)
19
-
20
- * Fixed `shim.wrapReturn` to call `Reflect.construct` in construct Proxy trap. Also including `newTarget` to work with inherited classes.
21
-
22
- * Added link to New Relic Node.js Examples repository.
23
-
24
- * Excluded installing dependencies in `versioned-external` folders when running integration tests.
25
-
60
+ * Removed unused test file in restify versioned tests.
61
+ * Added `--strict` flag to versioned test runner to properly fail CI runs when test files are not included.
62
+
63
+ ### v8.9.1 (2022-03-22)
64
+
65
+ * Fixed `shim.wrapReturn` to call `Reflect.construct` in construct Proxy trap. Also including `newTarget` to work with inherited classes.
66
+
67
+ * Added link to New Relic Node.js Examples repository.
68
+
69
+ * Excluded installing dependencies in `versioned-external` folders when running integration tests.
70
+
26
71
  ### v8.9.0 (2022-03-15)
27
72
 
28
73
  * Added support for `initializeUnorderedBulkOp`, and `initializeOrderedBulkOp` in mongodb v3 instrumentation.
@@ -3292,12 +3292,12 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3292
3292
 
3293
3293
  ### tap
3294
3294
 
3295
- This product includes source derived from [tap](https://github.com/tapjs/node-tap) ([v15.1.6](https://github.com/tapjs/node-tap/tree/v15.1.6)), distributed under the [ISC License](https://github.com/tapjs/node-tap/blob/v15.1.6/LICENSE):
3295
+ This product includes source derived from [tap](https://github.com/tapjs/node-tap) ([v16.0.1](https://github.com/tapjs/node-tap/tree/v16.0.1)), distributed under the [ISC License](https://github.com/tapjs/node-tap/blob/v16.0.1/LICENSE):
3296
3296
 
3297
3297
  ```
3298
3298
  The ISC License
3299
3299
 
3300
- Copyright (c) Isaac Z. Schlueter and Contributors
3300
+ Copyright (c) 2011-2022 Isaac Z. Schlueter and Contributors
3301
3301
 
3302
3302
  Permission to use, copy, modify, and/or distribute this software for any
3303
3303
  purpose with or without fee is hereby granted, provided that the above
package/api.js CHANGED
@@ -150,32 +150,7 @@ API.prototype.getLinkingMetadata = function getLinkingMetadata(omitSupportabilit
150
150
  metric.incrementCallCount()
151
151
  }
152
152
 
153
- const agent = this.agent
154
-
155
- const segment = agent.tracer.getSegment()
156
- const config = agent.config
157
-
158
- const linkingMetadata = {
159
- 'entity.name': config.applications()[0],
160
- 'entity.type': 'SERVICE',
161
- 'hostname': config.getHostnameSafe()
162
- }
163
-
164
- if (config.distributed_tracing.enabled && segment) {
165
- linkingMetadata['trace.id'] = segment.transaction.traceId
166
- const spanId = segment.getSpanId()
167
- if (spanId) {
168
- linkingMetadata['span.id'] = spanId
169
- }
170
- } else {
171
- logger.debug('getLinkingMetadata with no active transaction')
172
- }
173
-
174
- if (config.entity_guid) {
175
- linkingMetadata['entity.guid'] = config.entity_guid
176
- }
177
-
178
- return linkingMetadata
153
+ return this.agent.getLinkingMetadata()
179
154
  }
180
155
 
181
156
  /**
package/lib/agent.js CHANGED
@@ -16,6 +16,7 @@ const ErrorEventAggregator = require('./errors/error-event-aggregator')
16
16
  const EventEmitter = require('events').EventEmitter
17
17
  const hashes = require('./util/hashes')
18
18
  const logger = require('./logger')
19
+ const LogAggregator = require('./aggregators/log-aggregator')
19
20
  const MetricMapper = require('./metrics/mapper')
20
21
  const MetricNormalizer = require('./metrics/normalizer')
21
22
  const MetricAggregator = require('./metrics/metric-aggregator')
@@ -137,6 +138,16 @@ function Agent(config) {
137
138
  this.metrics
138
139
  )
139
140
 
141
+ this.logs = new LogAggregator(
142
+ {
143
+ periodMs: config.event_harvest_config.report_period_ms,
144
+ limit: config.event_harvest_config.harvest_limits.log_event_data
145
+ },
146
+ this.collector,
147
+ this.metrics,
148
+ this
149
+ )
150
+
140
151
  this.errors = new ErrorCollector(config, errorTraceAggregator, errorEventAggregator, this.metrics)
141
152
 
142
153
  this._contextManager = createContextManager(this.config)
@@ -366,6 +377,20 @@ Agent.prototype.forceHarvestAll = function forceHarvestAll(callback) {
366
377
  promises.push(errorEventPromise)
367
378
  }
368
379
 
380
+ if (
381
+ agent.config.application_logging.enabled &&
382
+ agent.config.application_logging.forwarding.enabled
383
+ ) {
384
+ const logEventPromise = new Promise((resolve) => {
385
+ agent.logs.once('finished log_event_data data send.', function onLogEventsFinished() {
386
+ resolve()
387
+ })
388
+ agent.logs.send()
389
+ })
390
+
391
+ promises.push(logEventPromise)
392
+ }
393
+
369
394
  Promise.all(promises).then(() => {
370
395
  // Get out of the promise so callback errors aren't treated as
371
396
  // promise rejections.
@@ -381,6 +406,7 @@ Agent.prototype.stopAggregators = function stopAggregators() {
381
406
  this.spanEventAggregator.stop()
382
407
  this.transactionEventAggregator.stop()
383
408
  this.customEventAggregator.stop()
409
+ this.logs.stop()
384
410
  }
385
411
 
386
412
  Agent.prototype.startStreaming = function startStreaming() {
@@ -415,6 +441,13 @@ Agent.prototype.startAggregators = function startAggregators() {
415
441
  if (this.config.custom_insights_events.enabled) {
416
442
  this.customEventAggregator.start()
417
443
  }
444
+
445
+ if (
446
+ this.config.application_logging.enabled &&
447
+ this.config.application_logging.forwarding.enabled
448
+ ) {
449
+ this.logs.start()
450
+ }
418
451
  }
419
452
 
420
453
  /**
@@ -427,6 +460,8 @@ Agent.prototype.startAggregators = function startAggregators() {
427
460
  Agent.prototype.onConnect = function onConnect(shouldImmediatelyHarvest, callback) {
428
461
  this._reconfigureAggregators(this.config)
429
462
 
463
+ generateLoggingSupportMetrics(this)
464
+
430
465
  if (this.config.certificates && this.config.certificates.length > 0) {
431
466
  this.metrics.getOrCreateMetric(NAMES.FEATURES.CERTIFICATES).incrementCallCount()
432
467
  }
@@ -444,6 +479,7 @@ Agent.prototype._reconfigureAggregators = function _reconfigureAggregators(confi
444
479
  this.spanEventAggregator.reconfigure(config)
445
480
  this.transactionEventAggregator.reconfigure(config)
446
481
  this.customEventAggregator.reconfigure(config)
482
+ this.logs.reconfigure(config)
447
483
  }
448
484
 
449
485
  Agent.prototype._scheduleHarvests = function _scheduleHarvests(shouldImmediatelyHarvest, callback) {
@@ -587,6 +623,7 @@ Agent.prototype.harvestSync = function harvestSync() {
587
623
  this.spanEventAggregator.send()
588
624
  this.transactionEventAggregator.send()
589
625
  this.customEventAggregator.send()
626
+ this.logs.send()
590
627
 
591
628
  // Write serverless output
592
629
  collector.flushPayloadSync()
@@ -886,6 +923,76 @@ Agent.prototype._listenForConfigChanges = function _listenForConfigChanges() {
886
923
  })
887
924
  }
888
925
 
926
+ /**
927
+ * This method returns an object with the following keys/data:
928
+ * - `trace.id`: The current trace ID
929
+ * - `span.id`: The current span ID
930
+ * - `entity.name`: The application name specified in the connect request as
931
+ * app_name. If multiple application names are specified this will only be
932
+ * the first name
933
+ * - `entity.type`: The string "SERVICE"
934
+ * - `entity.guid`: The entity ID returned in the connect reply as entity_guid
935
+ * - `hostname`: The hostname as specified in the connect request as
936
+ * utilization.full_hostname. If utilization.full_hostname is null or empty,
937
+ * this will be the hostname specified in the connect request as host.
938
+ *
939
+ * @returns {LinkingMetadata} The linking object with the data above
940
+ */
941
+ Agent.prototype.getLinkingMetadata = function getLinkingMetadata() {
942
+ const segment = this.tracer.getSegment()
943
+ const config = this.config
944
+
945
+ const linkingMetadata = {
946
+ 'entity.name': config.applications()[0],
947
+ 'entity.type': 'SERVICE',
948
+ 'hostname': config.getHostnameSafe()
949
+ }
950
+
951
+ if (config.distributed_tracing.enabled && segment) {
952
+ linkingMetadata['trace.id'] = segment.transaction.traceId
953
+ const spanId = segment.getSpanId()
954
+ if (spanId) {
955
+ linkingMetadata['span.id'] = spanId
956
+ }
957
+ } else {
958
+ logger.debug('getLinkingMetadata with no active transaction')
959
+ }
960
+
961
+ if (config.entity_guid) {
962
+ linkingMetadata['entity.guid'] = config.entity_guid
963
+ }
964
+
965
+ return linkingMetadata
966
+ }
967
+
968
+ /**
969
+ * Formats the NR-LINKING blob that matches the spec
970
+ *
971
+ * @returns {string} formatted NR-LINKING string
972
+ */
973
+ Agent.prototype.getNRLinkingMetadata = function getNRLinkingMetadata() {
974
+ const metadata = this.getLinkingMetadata()
975
+ const nrLinkingMeta = [
976
+ getValue('entity.guid'),
977
+ getValue('hostname'),
978
+ getValue('trace.id'),
979
+ getValue('span.id'),
980
+ encodeURIComponent(getValue('entity.name'))
981
+ ]
982
+
983
+ return ` NR-LINKING|${nrLinkingMeta.join('|')}|`
984
+
985
+ /**
986
+ * Retries value for a given key but defaults to `` if falsey
987
+ *
988
+ * @param {string} key in linking metadata
989
+ * @returns {string}
990
+ */
991
+ function getValue(key) {
992
+ return metadata[key] || ''
993
+ }
994
+ }
995
+
889
996
  function generateEventHarvestSupportMetrics(agent, harvestConfig) {
890
997
  const harvestLimits = harvestConfig.harvest_limits
891
998
 
@@ -918,6 +1025,31 @@ function generateEventHarvestSupportMetrics(agent, harvestConfig) {
918
1025
  const spanLimitMetric = agent.metrics.getOrCreateMetric(harvestLimitNames.SPAN)
919
1026
  spanLimitMetric.recordValue(spanLimit)
920
1027
  }
1028
+
1029
+ const logLimit = harvestLimits.log_event_data
1030
+ if (logLimit) {
1031
+ const logLimitMetric = agent.metrics.getOrCreateMetric(harvestLimitNames.LOG)
1032
+ logLimitMetric.recordValue(logLimit)
1033
+ }
1034
+ }
1035
+
1036
+ /**
1037
+ * Increments the call counts of application logging supportability metrics
1038
+ * on every connect cycle
1039
+ *
1040
+ * @param {Agent} agent
1041
+ */
1042
+ function generateLoggingSupportMetrics(agent) {
1043
+ const loggingConfig = agent.config.application_logging
1044
+ const logNames = NAMES.LOGGING
1045
+
1046
+ const configKeys = ['metrics', 'forwarding', 'local_decorating']
1047
+ configKeys.forEach((configValue) => {
1048
+ const configFlag =
1049
+ loggingConfig.enabled && loggingConfig[`${configValue}`].enabled ? 'enabled' : 'disabled'
1050
+ const metricName = logNames[`${configValue.toUpperCase()}`]
1051
+ agent.metrics.getOrCreateMetric(`${metricName}${configFlag}`).incrementCallCount()
1052
+ })
921
1053
  }
922
1054
 
923
1055
  module.exports = Agent
@@ -0,0 +1,57 @@
1
+ /*
2
+ * Copyright 2020 New Relic Corporation. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ 'use strict'
7
+
8
+ const logger = require('../logger').child({ component: 'logs_aggregator' })
9
+ const EventAggregator = require('./event-aggregator')
10
+
11
+ const NAMES = require('../metrics/names')
12
+
13
+ /**
14
+ * Aggregates log events up to a certain limit.
15
+ *
16
+ * @private
17
+ * @class
18
+ */
19
+ class LogAggregator extends EventAggregator {
20
+ constructor(opts, collector, metrics, agent) {
21
+ opts = opts || {}
22
+ opts.method = opts.method || 'log_event_data'
23
+ opts.metricNames = NAMES.LOGGING
24
+
25
+ super(opts, collector, metrics)
26
+ this.agent = agent
27
+ }
28
+
29
+ _toPayloadSync() {
30
+ const events = this.events
31
+
32
+ if (events.length === 0) {
33
+ logger.debug('No log events to send.')
34
+ return
35
+ }
36
+
37
+ const eventData = events.toArray()
38
+ return [{ logs: eventData }]
39
+ }
40
+
41
+ add(logLine) {
42
+ const transaction = this.agent.getTransaction()
43
+ if (transaction) {
44
+ transaction.logs.add(logLine)
45
+ } else {
46
+ super.add(logLine)
47
+ }
48
+ }
49
+
50
+ addBatch(logs, priority) {
51
+ logs.forEach((logLine) => {
52
+ super.add(logLine, priority)
53
+ })
54
+ }
55
+ }
56
+
57
+ module.exports = LogAggregator
@@ -35,6 +35,10 @@ const FAILURE_DISCARD_DATA = new Set([400, 403, 404, 405, 407, 411, 413, 414, 41
35
35
 
36
36
  const AGENT_RUN_BEHAVIOR = CollectorResponse.AGENT_RUN_BEHAVIOR
37
37
 
38
+ /**
39
+ * @param errors
40
+ * @param name
41
+ */
38
42
  function dumpErrors(errors, name) {
39
43
  let index = 1
40
44
 
@@ -49,6 +53,9 @@ function dumpErrors(errors, name) {
49
53
  })
50
54
  }
51
55
 
56
+ /**
57
+ * @param agent
58
+ */
52
59
  function CollectorAPI(agent) {
53
60
  this._agent = agent
54
61
  this._reqHeadersMap = null
@@ -73,13 +80,16 @@ function CollectorAPI(agent) {
73
80
  customEvents: new RemoteMethod('custom_event_data', agent.config, initialEndpoint),
74
81
  queryData: new RemoteMethod('sql_trace_data', agent.config, initialEndpoint),
75
82
  errorEvents: new RemoteMethod('error_event_data', agent.config, initialEndpoint),
76
- spanEvents: new RemoteMethod('span_event_data', agent.config, initialEndpoint)
83
+ spanEvents: new RemoteMethod('span_event_data', agent.config, initialEndpoint),
84
+ logEvents: new RemoteMethod('log_event_data', agent.config, initialEndpoint)
77
85
  }
78
86
  }
79
87
 
80
88
  /**
81
89
  * Updates all methods except preconnect w/ new host/port pairs sent down from server
82
90
  * during preconnect (via redirect_host). Preconnect does not update.
91
+ *
92
+ * @param endpoint
83
93
  */
84
94
  CollectorAPI.prototype._updateEndpoints = function _updateEndpoints(endpoint) {
85
95
  logger.trace('Updating endpoints to: ', endpoint)
@@ -121,7 +131,7 @@ CollectorAPI.prototype.connect = function connect(callback) {
121
131
  * fix the misconfiguration by setting `proxy` to a fully qualified URL
122
132
  *
123
133
  * @param {Error} error
124
- * @returns {Boolean}
134
+ * @returns {boolean}
125
135
  */
126
136
  function isProxyMisconfigured(error) {
127
137
  const config = api._agent.config
@@ -134,6 +144,10 @@ CollectorAPI.prototype.connect = function connect(callback) {
134
144
  )
135
145
  }
136
146
 
147
+ /**
148
+ * @param error
149
+ * @param response
150
+ */
137
151
  function retry(error, response) {
138
152
  metric.incrementCallCount()
139
153
 
@@ -205,6 +219,10 @@ CollectorAPI.prototype._login = function _login(callback) {
205
219
 
206
220
  methods.preconnect.invoke(payload, onPreConnect)
207
221
 
222
+ /**
223
+ * @param error
224
+ * @param response
225
+ */
208
226
  function onPreConnect(error, response) {
209
227
  if (error || !SUCCESS.has(response.status)) {
210
228
  return callback(error, response)
@@ -276,6 +294,10 @@ CollectorAPI.prototype._connect = function _connect(env, callback) {
276
294
 
277
295
  methods.connect.invoke(env, onConnect)
278
296
 
297
+ /**
298
+ * @param error
299
+ * @param res
300
+ */
279
301
  function onConnect(error, res) {
280
302
  if (error || !SUCCESS.has(res.status)) {
281
303
  return callback(error, res)
@@ -371,6 +393,10 @@ CollectorAPI.prototype.error_event_data = function errorEvents(events, callback)
371
393
  }
372
394
 
373
395
  CollectorAPI.prototype.analytic_event_data = transactionEvents
396
+ /**
397
+ * @param events
398
+ * @param callback
399
+ */
374
400
  function transactionEvents(events, callback) {
375
401
  if (!callback) {
376
402
  throw new TypeError('callback is required')
@@ -467,6 +493,10 @@ CollectorAPI.prototype.span_event_data = function spanEvents(events, callback) {
467
493
  * @param {Function} callback The continuation / error handler.
468
494
  */
469
495
  CollectorAPI.prototype.transaction_sample_data = transactionSampleData
496
+ /**
497
+ * @param traces
498
+ * @param callback
499
+ */
470
500
  function transactionSampleData(traces, callback) {
471
501
  if (!callback) {
472
502
  throw new TypeError('callback is required')
@@ -478,11 +508,36 @@ function transactionSampleData(traces, callback) {
478
508
  this._sendData(this._methods.traces, traces, callback)
479
509
  }
480
510
 
511
+ /**
512
+ * Send already-formatted log data by calling log_event_data. For
513
+ * performance reasons, the API methods do no validation, but the
514
+ * collector expects data in an exact format. It expects a JSON array
515
+ * containing the following 2 elements:
516
+ *
517
+ * 1. An array of logs.
518
+ * 2. A list of common attributes(optional).
519
+ *
520
+ * @param {Array} logsRecords The encoded log entries.
521
+ * @param logRecords
522
+ * @param {Function} callback The continuation / error handler.
523
+ */
524
+ CollectorAPI.prototype.log_event_data = function errorData(logRecords, callback) {
525
+ if (!callback) {
526
+ throw new TypeError('callback is required')
527
+ }
528
+ if (!logRecords) {
529
+ return callback(new TypeError('must pass logRecords to send'))
530
+ }
531
+
532
+ this._sendData(this._methods.logEvents, logRecords, callback)
533
+ }
534
+
481
535
  /**
482
536
  * Sends no data aside from the message itself. Clears the run ID, which
483
537
  * effectively disconnects the agent from the collector.
484
538
  *
485
539
  * @param Function callback Runs after the run ID has been cleared.
540
+ * @param callback
486
541
  */
487
542
  CollectorAPI.prototype.shutdown = function shutdown(callback) {
488
543
  if (!callback) {
@@ -494,6 +549,10 @@ CollectorAPI.prototype.shutdown = function shutdown(callback) {
494
549
  const agent = this._agent
495
550
  this._methods.shutdown.invoke(null, this._reqHeadersMap, onShutdown)
496
551
 
552
+ /**
553
+ * @param error
554
+ * @param response
555
+ */
497
556
  function onShutdown(error, response) {
498
557
  if (error) {
499
558
  dumpErrors([error], 'shutdown')
@@ -613,6 +672,11 @@ CollectorAPI.prototype.isConnected = function isConnected() {
613
672
  * @param {Function} cb - CollectorAPI method invocation callback
614
673
  */
615
674
  CollectorAPI.prototype._handleResponseCode = _handleResponseCode
675
+ /**
676
+ * @param response
677
+ * @param endpoint
678
+ * @param cb
679
+ */
616
680
  function _handleResponseCode(response, endpoint, cb) {
617
681
  const code = response.status
618
682
 
@@ -648,6 +712,11 @@ function _handleResponseCode(response, endpoint, cb) {
648
712
  return setImmediate(cb, null, CollectorResponse.discard(null))
649
713
  }
650
714
 
715
+ /**
716
+ * @param endpoint
717
+ * @param code
718
+ * @param action
719
+ */
651
720
  function logFailure(endpoint, code, action) {
652
721
  logger.error('Agent endpoint %s returned %s status. %s.', endpoint, code, action)
653
722
  }
@@ -10,6 +10,12 @@ const fetchSystemInfo = require('../system-info')
10
10
  const logger = require('../logger').child({ component: 'facts' })
11
11
  const os = require('os')
12
12
  const parseLabels = require('../util/label-parser')
13
+ const NAMES = require('../metrics/names')
14
+
15
+ // For now static and tentative list of which logging libraries we
16
+ // want to track. Later might come up with a better way of populating
17
+ // this list.
18
+ const LOG_LIBRARIES = ['winston', 'bunyan', 'pino', 'loglevel', 'npmlog', 'fancy-log']
13
19
 
14
20
  module.exports = facts
15
21
 
@@ -30,6 +36,8 @@ function facts(agent, callback) {
30
36
  const systemInfo = data.systemInfo || Object.create(null)
31
37
  const environment = data.environment || []
32
38
 
39
+ countSupportabilityForLogLibraries(environment, agent)
40
+
33
41
  const hostname = agent.config.getHostnameSafe()
34
42
  const results = {
35
43
  utilization: {
@@ -58,12 +66,22 @@ function facts(agent, callback) {
58
66
 
59
67
  logger.debug('New Relic metadata %o', results.metadata)
60
68
 
69
+ /**
70
+ * WARNING: This may not make sense if you are familiar with our config
71
+ * and updating of config from server. But the intention here is to always
72
+ * send the values from user config of harvest limits because on connect these
73
+ * values get reconfigured based on harvest cycle intervals. So if you originally
74
+ * had 1000 and a harvest of 5 seconds the new value of the harvest limit would be 83.
75
+ * Then every subsequent connect request it would continue to decrease until it eventually hit 0
76
+ * and we would never be sampling a certain piece of data.
77
+ */
61
78
  results.event_harvest_config = {
62
79
  harvest_limits: {
63
80
  analytic_event_data: agent.config.transaction_events.max_samples_stored,
64
81
  custom_event_data: agent.config.custom_insights_events.max_samples_stored,
65
82
  error_event_data: agent.config.error_collector.max_event_samples_stored,
66
- span_event_data: agent.config.span_events.max_samples_stored
83
+ span_event_data: agent.config.span_events.max_samples_stored,
84
+ log_event_data: agent.config.application_logging.forwarding.max_samples_stored
67
85
  }
68
86
  }
69
87
 
@@ -120,6 +138,8 @@ function getAllIPAddresses() {
120
138
  * On the server, the host will still be used as part of the key to determine if two agent
121
139
  * connections are the same real agent or a separate one.
122
140
  * https://github.com/newrelic/node-newrelic/issues/654
141
+ *
142
+ * @param appNames
123
143
  */
124
144
  function getIdentifierOverride(appNames) {
125
145
  const identifier = [
@@ -130,3 +150,23 @@ function getIdentifierOverride(appNames) {
130
150
 
131
151
  return identifier
132
152
  }
153
+
154
+ /**
155
+ * This function searches the computed environment for the presence of
156
+ * some popular logging libraries. If present, it increments the
157
+ * corresponding supportability metrics.
158
+ *
159
+ * @param {Array} environment the environment computed by
160
+ * @param {object} agent the New Relic agent
161
+ */
162
+ function countSupportabilityForLogLibraries(environment, agent) {
163
+ const packages = environment.find((elt) => elt[0] === 'Packages')[1]
164
+ packages.forEach((pkg) => {
165
+ pkg = JSON.parse(pkg)[0]
166
+ if (LOG_LIBRARIES.includes(pkg)) {
167
+ agent.metrics
168
+ .getOrCreateMetric(`${NAMES.SUPPORTABILITY.NODEJS_DEPENDENCIES}/${pkg}`)
169
+ .incrementCallCount()
170
+ }
171
+ })
172
+ }