newrelic 6.4.0 → 6.6.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/.eslintignore +1 -1
- package/{.eslintrc → .eslintrc.js} +2 -1
- package/.travis.yml +5 -6
- package/NEWS.md +105 -1
- package/THIRD_PARTY_NOTICES.md +420 -0
- package/api.js +72 -1
- package/index.js +3 -3
- package/lib/agent.js +22 -8
- package/lib/aggregators/base-aggregator.js +1 -1
- package/lib/attributes.js +15 -6
- package/lib/collector/serverless.js +8 -0
- package/lib/config/default.js +23 -1
- package/lib/config/env.js +7 -1
- package/lib/config/hsm.js +6 -1
- package/lib/config/index.js +36 -8
- package/lib/config/lasp.js +16 -1
- package/lib/errors/error-collector.js +51 -90
- package/lib/errors/helper.js +13 -13
- package/lib/errors/index.js +36 -18
- package/lib/grpc/connection/states.js +9 -0
- package/lib/grpc/connection.js +344 -0
- package/lib/grpc/endpoints/infinite-tracing/v1.proto +29 -0
- package/lib/header-processing.js +75 -0
- package/lib/instrumentation/core/child_process.js +12 -0
- package/lib/instrumentation/core/http-outbound.js +4 -0
- package/lib/instrumentation/core/http.js +98 -145
- package/lib/instrumentation/hapi/hapi-17.js +10 -3
- package/lib/metrics/names.js +12 -1
- package/lib/proxy/grpc.js +15 -0
- package/lib/serverless/aws-lambda.js +2 -0
- package/lib/spans/create-span-event-aggregator.js +112 -0
- package/lib/spans/map-to-streaming-type.js +44 -0
- package/lib/spans/span-event-aggregator.js +5 -0
- package/lib/spans/span-event.js +22 -9
- package/lib/spans/span-streamer.js +109 -0
- package/lib/spans/streaming-span-attributes.js +59 -0
- package/lib/spans/streaming-span-event-aggregator.js +98 -0
- package/lib/spans/streaming-span-event.js +261 -0
- package/lib/transaction/index.js +36 -20
- package/lib/transaction/trace/index.js +13 -10
- package/lib/transaction/trace/segment.js +12 -1
- package/lib/transaction/tracecontext.js +4 -0
- package/package.json +10 -10
- package/stub_api.js +5 -0
|
@@ -12,6 +12,7 @@ const urltils = require('../../util/urltils')
|
|
|
12
12
|
const properties = require('../../util/properties')
|
|
13
13
|
const psemver = require('../../util/process-version')
|
|
14
14
|
const headerAttributes = require('../../header-attributes')
|
|
15
|
+
const headerProcessing = require('../../header-processing')
|
|
15
16
|
|
|
16
17
|
const NAMES = require('../../metrics/names')
|
|
17
18
|
const DESTS = require('../../config/attribute-filter').DESTINATIONS
|
|
@@ -22,14 +23,12 @@ const DESTS = require('../../config/attribute-filter').DESTINATIONS
|
|
|
22
23
|
*
|
|
23
24
|
*/
|
|
24
25
|
const SHOULD_WRAP_HTTPS = psemver.satisfies('>=9.0.0 || 8.9.0')
|
|
26
|
+
const SHOULD_FORMAT_ARGS = psemver.satisfies('>=10.0.0')
|
|
25
27
|
const NR_CONNECTION_PROP = '__NR__connection'
|
|
26
|
-
const REQUEST_HEADER = 'x-request-start'
|
|
27
|
-
const QUEUE_HEADER = 'x-queue-start'
|
|
28
28
|
const NEWRELIC_ID_HEADER = 'x-newrelic-id'
|
|
29
29
|
const NEWRELIC_APP_DATA_HEADER = 'x-newrelic-app-data'
|
|
30
30
|
const NEWRELIC_TRANSACTION_HEADER = 'x-newrelic-transaction'
|
|
31
31
|
const NEWRELIC_SYNTHETICS_HEADER = 'x-newrelic-synthetics'
|
|
32
|
-
const CONTENT_LENGTH_REGEX = /^Content-Length$/i
|
|
33
32
|
const TRANSACTION_INFO_KEY = '__NR_transactionInfo'
|
|
34
33
|
|
|
35
34
|
|
|
@@ -113,36 +112,11 @@ function wrapEmitWithTransaction(agent, emit, isHTTPS) {
|
|
|
113
112
|
'Applying user naming rules for RUM.')
|
|
114
113
|
transaction.applyUserNamingRules(request.url)
|
|
115
114
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
* Queue time is provided by certain providers by stamping the request
|
|
120
|
-
* header with the time the request arrived at the router.
|
|
121
|
-
*
|
|
122
|
-
* Units for queue time are
|
|
123
|
-
*/
|
|
124
|
-
var qtime = request.headers[REQUEST_HEADER] || request.headers[QUEUE_HEADER]
|
|
125
|
-
if (qtime) {
|
|
126
|
-
var split = qtime.split('=')
|
|
127
|
-
if (split.length > 1) {
|
|
128
|
-
qtime = split[1]
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
var start = parseFloat(qtime)
|
|
132
|
-
|
|
133
|
-
if (isNaN(start)) {
|
|
134
|
-
logger.warn('Queue time header parsed as NaN (%s)', qtime)
|
|
135
|
-
} else {
|
|
136
|
-
// nano seconds
|
|
137
|
-
if (start > 1e18) start = start / 1e6
|
|
138
|
-
// micro seconds
|
|
139
|
-
else if (start > 1e15) start = start / 1e3
|
|
140
|
-
// seconds
|
|
141
|
-
else if (start < 1e12) start = start * 1e3
|
|
142
|
-
|
|
143
|
-
transaction.queueTime = Date.now() - start
|
|
144
|
-
}
|
|
115
|
+
const queueTimeStamp = headerProcessing.getQueueTime(logger, request.headers)
|
|
116
|
+
if (queueTimeStamp) {
|
|
117
|
+
transaction.queueTime = Date.now() - queueTimeStamp
|
|
145
118
|
}
|
|
119
|
+
|
|
146
120
|
if (agent.config.distributed_tracing.enabled) {
|
|
147
121
|
// Node http headers are automatically lowercase
|
|
148
122
|
transaction.acceptDistributedTraceHeaders(transport, request.headers)
|
|
@@ -179,6 +153,10 @@ function wrapEmitWithTransaction(agent, emit, isHTTPS) {
|
|
|
179
153
|
|
|
180
154
|
if (response) {
|
|
181
155
|
if (response.statusCode != null) {
|
|
156
|
+
/*
|
|
157
|
+
TODO: remove with next major release
|
|
158
|
+
httpResponseCode attribute is deprecated
|
|
159
|
+
*/
|
|
182
160
|
var responseCode = String(response.statusCode)
|
|
183
161
|
transaction.trace.attributes.addAttribute(
|
|
184
162
|
DESTS.TRANS_COMMON,
|
|
@@ -187,6 +165,16 @@ function wrapEmitWithTransaction(agent, emit, isHTTPS) {
|
|
|
187
165
|
)
|
|
188
166
|
|
|
189
167
|
if (/^\d+$/.test(responseCode)) {
|
|
168
|
+
transaction.trace.attributes.addAttribute(
|
|
169
|
+
DESTS.TRANS_COMMON,
|
|
170
|
+
'http.statusCode',
|
|
171
|
+
responseCode
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
/*
|
|
175
|
+
TODO: remove with next major release
|
|
176
|
+
response.status attribute is deprecated
|
|
177
|
+
*/
|
|
190
178
|
transaction.trace.attributes.addAttribute(
|
|
191
179
|
DESTS.TRANS_COMMON,
|
|
192
180
|
'response.status',
|
|
@@ -194,7 +182,18 @@ function wrapEmitWithTransaction(agent, emit, isHTTPS) {
|
|
|
194
182
|
)
|
|
195
183
|
}
|
|
196
184
|
}
|
|
185
|
+
|
|
197
186
|
if (response.statusMessage !== undefined) {
|
|
187
|
+
transaction.trace.attributes.addAttribute(
|
|
188
|
+
DESTS.TRANS_COMMON,
|
|
189
|
+
'http.statusText',
|
|
190
|
+
response.statusMessage
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
/*
|
|
194
|
+
TODO: remove with next major release
|
|
195
|
+
httpResponseMessage attribute is deprecated
|
|
196
|
+
*/
|
|
198
197
|
transaction.trace.attributes.addAttribute(
|
|
199
198
|
DESTS.TRANS_COMMON,
|
|
200
199
|
'httpResponseMessage',
|
|
@@ -202,8 +201,7 @@ function wrapEmitWithTransaction(agent, emit, isHTTPS) {
|
|
|
202
201
|
)
|
|
203
202
|
}
|
|
204
203
|
|
|
205
|
-
|
|
206
|
-
var headers = (response.getHeaders && response.getHeaders()) || response._headers
|
|
204
|
+
var headers = response.getHeaders()
|
|
207
205
|
if (headers) {
|
|
208
206
|
headerAttributes.collectResponseHeaders(headers, transaction)
|
|
209
207
|
}
|
|
@@ -356,25 +354,12 @@ function wrapWriteHead(agent, writeHead) {
|
|
|
356
354
|
var new_headers = arguments[arguments.length - 1]
|
|
357
355
|
|
|
358
356
|
if (typeof new_headers === 'object') {
|
|
359
|
-
|
|
360
|
-
if (CONTENT_LENGTH_REGEX.test(header)) {
|
|
361
|
-
contentLength = new_headers[header]
|
|
362
|
-
break
|
|
363
|
-
}
|
|
364
|
-
}
|
|
357
|
+
contentLength = headerProcessing.getContentLengthFromHeaders(new_headers)
|
|
365
358
|
}
|
|
366
359
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
// Outbound headers can be capitalized in any way, use regex instead
|
|
371
|
-
// of direct lookup.
|
|
372
|
-
for (var userHeader in this._headers) { // jshint ignore: line
|
|
373
|
-
if (CONTENT_LENGTH_REGEX.test(userHeader)) {
|
|
374
|
-
contentLength = this._headers[userHeader]
|
|
375
|
-
break
|
|
376
|
-
}
|
|
377
|
-
}
|
|
360
|
+
const currentHeaders = this.getHeaders()
|
|
361
|
+
if (contentLength === -1 && currentHeaders) {
|
|
362
|
+
contentLength = headerProcessing.getContentLengthFromHeaders(currentHeaders)
|
|
378
363
|
}
|
|
379
364
|
// Stored on the tx so we can push a metric with this time instead of
|
|
380
365
|
// actual duration.
|
|
@@ -411,8 +396,60 @@ function wrapWriteHead(agent, writeHead) {
|
|
|
411
396
|
}
|
|
412
397
|
}
|
|
413
398
|
|
|
399
|
+
// Taken from the Node code base, internal/url.js
|
|
400
|
+
function urlToOptions(_url) {
|
|
401
|
+
const options = {
|
|
402
|
+
protocol: _url.protocol,
|
|
403
|
+
hostname: typeof _url.hostname === 'string' && _url.hostname.startsWith('[') ?
|
|
404
|
+
_url.hostname.slice(1, -1) :
|
|
405
|
+
_url.hostname,
|
|
406
|
+
hash: _url.hash,
|
|
407
|
+
search: _url.search,
|
|
408
|
+
pathname: _url.pathname,
|
|
409
|
+
path: `${_url.pathname || ''}${_url.search || ''}`,
|
|
410
|
+
href: _url.href
|
|
411
|
+
}
|
|
412
|
+
if (_url.port !== '') {
|
|
413
|
+
options.port = Number(_url.port)
|
|
414
|
+
}
|
|
415
|
+
if (_url.username || _url.password) {
|
|
416
|
+
options.auth = `${_url.username}:${_url.password}`
|
|
417
|
+
}
|
|
418
|
+
return options
|
|
419
|
+
}
|
|
420
|
+
|
|
414
421
|
function wrapRequest(agent, request) {
|
|
415
|
-
|
|
422
|
+
// URL as a global was not added until Node v10
|
|
423
|
+
const URLClass = global.URL ? global.URL : url.URL
|
|
424
|
+
return function wrappedRequest(input, options, cb) {
|
|
425
|
+
let reqArgs = arguments
|
|
426
|
+
if (SHOULD_FORMAT_ARGS) {
|
|
427
|
+
// Support new function signatures in Node v10+. If the first argument is a URL,
|
|
428
|
+
// merge it into the options object. This code is copied from Node internals.
|
|
429
|
+
if (typeof input === 'string') {
|
|
430
|
+
const urlStr = input
|
|
431
|
+
input = urlToOptions(new URLClass(urlStr))
|
|
432
|
+
} else if (input.constructor && input.constructor.name === 'URL') {
|
|
433
|
+
input = urlToOptions(input)
|
|
434
|
+
} else {
|
|
435
|
+
cb = options
|
|
436
|
+
options = input
|
|
437
|
+
input = null
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (typeof options === 'function') {
|
|
441
|
+
cb = options
|
|
442
|
+
options = input || {}
|
|
443
|
+
} else {
|
|
444
|
+
options = Object.assign(input || {}, options)
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
reqArgs = [options, cb]
|
|
448
|
+
} else {
|
|
449
|
+
// Node v8 function signature had options as the first argument
|
|
450
|
+
options = input
|
|
451
|
+
}
|
|
452
|
+
|
|
416
453
|
// Don't pollute metrics and calls with NR connections
|
|
417
454
|
const internalOnly = options && options[NR_CONNECTION_PROP]
|
|
418
455
|
if (internalOnly) {
|
|
@@ -430,13 +467,12 @@ function wrapRequest(agent, request) {
|
|
|
430
467
|
logOpts.port
|
|
431
468
|
)
|
|
432
469
|
}
|
|
433
|
-
return request.apply(this,
|
|
470
|
+
return request.apply(this, reqArgs)
|
|
434
471
|
}
|
|
435
472
|
|
|
436
|
-
const args = agent.tracer.slice(
|
|
473
|
+
const args = agent.tracer.slice(reqArgs)
|
|
437
474
|
const context = this
|
|
438
475
|
|
|
439
|
-
// hostname & port logic pulled directly from node's 0.10 lib/http.js
|
|
440
476
|
return instrumentOutbound(agent, options, function makeRequest(opts) {
|
|
441
477
|
args[0] = opts
|
|
442
478
|
return request.apply(context, args)
|
|
@@ -444,28 +480,6 @@ function wrapRequest(agent, request) {
|
|
|
444
480
|
}
|
|
445
481
|
}
|
|
446
482
|
|
|
447
|
-
function wrapLegacyRequest(agent, request) {
|
|
448
|
-
return function wrappedLegacyRequest(method, path, headers) {
|
|
449
|
-
var makeRequest = request.bind(this, method, path, headers)
|
|
450
|
-
|
|
451
|
-
if (agent.tracer.getTransaction()) {
|
|
452
|
-
return instrumentOutbound(agent, this, makeRequest)
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
logger.trace('No transaction, not recording external to %s:%s', this.host, this.port)
|
|
456
|
-
return makeRequest()
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
function wrapLegacyClient(agent, proto) {
|
|
461
|
-
shimmer.wrapMethod(
|
|
462
|
-
proto,
|
|
463
|
-
'http.Client.prototype',
|
|
464
|
-
'request',
|
|
465
|
-
wrapLegacyRequest.bind(null, agent)
|
|
466
|
-
)
|
|
467
|
-
}
|
|
468
|
-
|
|
469
483
|
module.exports = function initialize(agent, http, moduleName) {
|
|
470
484
|
if (!http) {
|
|
471
485
|
logger.debug('Did not get http module, not instrumenting!')
|
|
@@ -522,7 +536,7 @@ module.exports = function initialize(agent, http, moduleName) {
|
|
|
522
536
|
// change originally also appeared in 8.9.0 but was reverted in 8.9.1.
|
|
523
537
|
//
|
|
524
538
|
// TODO: Remove `SHOULD_WRAP_HTTPS` after deprecating Node <9.
|
|
525
|
-
if (SHOULD_WRAP_HTTPS ||
|
|
539
|
+
if (SHOULD_WRAP_HTTPS || !IS_HTTPS) {
|
|
526
540
|
shimmer.wrapMethod(
|
|
527
541
|
http,
|
|
528
542
|
'http',
|
|
@@ -530,21 +544,12 @@ module.exports = function initialize(agent, http, moduleName) {
|
|
|
530
544
|
wrapRequest.bind(null, agent)
|
|
531
545
|
)
|
|
532
546
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
// instrumenting external requests.
|
|
540
|
-
if (!IS_HTTPS && psemver.satisfies('>=8')) {
|
|
541
|
-
shimmer.wrapMethod(
|
|
542
|
-
http,
|
|
543
|
-
'http',
|
|
544
|
-
'get',
|
|
545
|
-
wrapRequest.bind(null, agent)
|
|
546
|
-
)
|
|
547
|
-
}
|
|
547
|
+
shimmer.wrapMethod(
|
|
548
|
+
http,
|
|
549
|
+
'http',
|
|
550
|
+
'get',
|
|
551
|
+
wrapRequest.bind(null, agent)
|
|
552
|
+
)
|
|
548
553
|
}
|
|
549
554
|
|
|
550
555
|
shimmer.wrapMethod(
|
|
@@ -569,58 +574,6 @@ module.exports = function initialize(agent, http, moduleName) {
|
|
|
569
574
|
}
|
|
570
575
|
}
|
|
571
576
|
)
|
|
572
|
-
|
|
573
|
-
// http.Client is deprecated, but still in use
|
|
574
|
-
// TODO: Remove this once Node <7 is deprecated.
|
|
575
|
-
var DeprecatedClient, deprecatedCreateClient
|
|
576
|
-
function clearGetters() {
|
|
577
|
-
if (DeprecatedClient) {
|
|
578
|
-
delete http.Client
|
|
579
|
-
http.Client = DeprecatedClient
|
|
580
|
-
}
|
|
581
|
-
if (deprecatedCreateClient) {
|
|
582
|
-
delete http.createClient
|
|
583
|
-
http.createClient = deprecatedCreateClient
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
// TODO: Remove this once Node <7 is deprecated.
|
|
588
|
-
DeprecatedClient = shimmer.wrapDeprecated(
|
|
589
|
-
http,
|
|
590
|
-
'http',
|
|
591
|
-
'Client',
|
|
592
|
-
{
|
|
593
|
-
get: function get() {
|
|
594
|
-
var example = new DeprecatedClient(80, 'localhost')
|
|
595
|
-
wrapLegacyClient(agent, example.constructor.prototype)
|
|
596
|
-
clearGetters()
|
|
597
|
-
|
|
598
|
-
return DeprecatedClient
|
|
599
|
-
},
|
|
600
|
-
set: function set(NewClient) {
|
|
601
|
-
DeprecatedClient = NewClient
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
)
|
|
605
|
-
|
|
606
|
-
// TODO: Remove this once Node <7 is deprecated.
|
|
607
|
-
deprecatedCreateClient = shimmer.wrapDeprecated(
|
|
608
|
-
http,
|
|
609
|
-
'http',
|
|
610
|
-
'createClient',
|
|
611
|
-
{
|
|
612
|
-
get: function get() {
|
|
613
|
-
var example = deprecatedCreateClient(80, 'localhost')
|
|
614
|
-
wrapLegacyClient(agent, example.constructor.prototype)
|
|
615
|
-
clearGetters()
|
|
616
|
-
|
|
617
|
-
return deprecatedCreateClient
|
|
618
|
-
},
|
|
619
|
-
set: function set(newCreateClient) {
|
|
620
|
-
deprecatedCreateClient = newCreateClient
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
)
|
|
624
577
|
}
|
|
625
578
|
|
|
626
579
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const shared = require('./shared')
|
|
4
|
+
const record = require('../../metrics/recorders/generic')
|
|
4
5
|
|
|
5
6
|
// TODO: abstract and consolidate mostly-shared hapi functionality
|
|
6
7
|
module.exports = function initialize(agent, hapi, moduleName, shim) {
|
|
@@ -154,14 +155,20 @@ function wrapPreHandlers(shim, container, path) {
|
|
|
154
155
|
}
|
|
155
156
|
return container
|
|
156
157
|
} else if (shim.isFunction(container)) {
|
|
157
|
-
return
|
|
158
|
+
return wrapPreHandler(shim, container, path)
|
|
158
159
|
} else if (container.method && shim.isFunction(container.method)) {
|
|
159
160
|
return shim.wrap(container, 'method', function wrapHandler(shim, handler) {
|
|
160
|
-
return
|
|
161
|
+
return wrapPreHandler(shim, handler, path)
|
|
161
162
|
})
|
|
162
163
|
}
|
|
163
164
|
}
|
|
164
165
|
|
|
166
|
+
function wrapPreHandler(shim, container, path) {
|
|
167
|
+
return shim.record(container, (shim) => {
|
|
168
|
+
return {name: [shim.HAPI, ' pre handler: ','(',path,')'].join(''), recorder: record}
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
|
|
165
172
|
function wrapRouteHandler(shim, handler, path) {
|
|
166
173
|
return shim.recordMiddleware(handler, {
|
|
167
174
|
route: path,
|
package/lib/metrics/names.js
CHANGED
|
@@ -16,7 +16,8 @@ const SUPPORTABILITY = {
|
|
|
16
16
|
DEPENDENCIES: 'Supportability/InstalledDependencies',
|
|
17
17
|
NODEJS: 'Supportability/Nodejs',
|
|
18
18
|
REGISTRATION: 'Supportability/Registration',
|
|
19
|
-
EVENT_HARVEST: 'Supportability/EventHarvest'
|
|
19
|
+
EVENT_HARVEST: 'Supportability/EventHarvest',
|
|
20
|
+
INFINITE_TRACING: 'Supportability/InfiniteTracing'
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
const ERRORS = {
|
|
@@ -231,6 +232,15 @@ const SPAN_EVENTS = {
|
|
|
231
232
|
DROPPED: SUPPORTABILITY.PREFIX + 'SpanEvent/Discarded'
|
|
232
233
|
}
|
|
233
234
|
|
|
235
|
+
const INFINITE_TRACING = {
|
|
236
|
+
SEEN: SUPPORTABILITY.INFINITE_TRACING + '/Span/Seen',
|
|
237
|
+
SENT: SUPPORTABILITY.INFINITE_TRACING + '/Span/Sent',
|
|
238
|
+
SPAN_RESPONSE_ERROR: SUPPORTABILITY.INFINITE_TRACING + '/Span/Response/Error',
|
|
239
|
+
SPAN_RESPONSE_GRPC_UNIMPLEMENTED: SUPPORTABILITY.INFINITE_TRACING + '/Span/gRPC/UNIMPLEMENTED',
|
|
240
|
+
SPAN_RESPONSE_GRPC_STATUS: SUPPORTABILITY.INFINITE_TRACING + '/Span/gRPC/%s',
|
|
241
|
+
DRAIN_DURATION: SUPPORTABILITY.INFINITE_TRACING + '/Drain/Duration'
|
|
242
|
+
}
|
|
243
|
+
|
|
234
244
|
module.exports = {
|
|
235
245
|
ACTION_DELIMITER: '/',
|
|
236
246
|
ALL: ALL,
|
|
@@ -253,6 +263,7 @@ module.exports = {
|
|
|
253
263
|
GC: GC,
|
|
254
264
|
HAPI: HAPI,
|
|
255
265
|
HTTP: 'HttpDispatcher',
|
|
266
|
+
INFINITE_TRACING: INFINITE_TRACING,
|
|
256
267
|
LOOP: LOOP,
|
|
257
268
|
MEMCACHE: MEMCACHE,
|
|
258
269
|
MEMORY: MEMORY,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
class ProxyGrpc {
|
|
3
|
+
constructor(grpcLibrary = '@grpc/grpc-js') {
|
|
4
|
+
this.library = require(grpcLibrary)
|
|
5
|
+
|
|
6
|
+
// add methods or objets from base grpc class that we need as needed
|
|
7
|
+
this.credentials = this.library.credentials
|
|
8
|
+
this.Metadata = this.library.Metadata
|
|
9
|
+
this.loadPackageDefinition = this.library.loadPackageDefinition
|
|
10
|
+
this.status = this.library.status
|
|
11
|
+
this.Server = this.library.Server
|
|
12
|
+
this.ServerCredentials = this.library.ServerCredentials
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
module.exports = (new ProxyGrpc)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const psemver = require('../util/process-version')
|
|
4
|
+
const logger = require('../logger')
|
|
5
|
+
const SpanEventAggregator = require('./span-event-aggregator')
|
|
6
|
+
const StreamingSpanEventAggregator = require('./streaming-span-event-aggregator')
|
|
7
|
+
|
|
8
|
+
function createSpanEventAggregator(config, collector, metrics) {
|
|
9
|
+
let shouldCreateStreaming = false
|
|
10
|
+
if (config.infinite_tracing.trace_observer.host) {
|
|
11
|
+
// TODO: ideally this validation and configuration clearing would happen
|
|
12
|
+
// in the config. Since we don't currently have a way to generate
|
|
13
|
+
// support metrics in the config, keeping this related logic together here.
|
|
14
|
+
// If logic happened prior, could merely check for existance of trace_observer.host.
|
|
15
|
+
shouldCreateStreaming = validateInfiniteTracing(config.infinite_tracing.trace_observer)
|
|
16
|
+
|
|
17
|
+
if (!shouldCreateStreaming) {
|
|
18
|
+
// Explicitly disable for any downstream consumers
|
|
19
|
+
config.infinite_tracing.trace_observer.host = ''
|
|
20
|
+
config.infinite_tracing.trace_observer.port = ''
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (shouldCreateStreaming) {
|
|
25
|
+
return createStreamingAggregator(config, collector, metrics)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return createStandardAggregator(config, collector, metrics)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function createStreamingAggregator(config, collector, metrics) {
|
|
32
|
+
logger.trace('Creating streaming span event aggregator for infinite tracing.')
|
|
33
|
+
|
|
34
|
+
// loading the class here to ensure its behind a feature flag
|
|
35
|
+
// and won't trigger a grpc load in node 8
|
|
36
|
+
const GrpcConnection = require('../grpc/connection')
|
|
37
|
+
const connection = new GrpcConnection(config.infinite_tracing.trace_observer, metrics)
|
|
38
|
+
const SpanStreamer = require('./span-streamer')
|
|
39
|
+
const spanStreamer = new SpanStreamer(
|
|
40
|
+
config.license_key,
|
|
41
|
+
connection,
|
|
42
|
+
metrics
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
const opts = {
|
|
46
|
+
periodMs: 1000,
|
|
47
|
+
limit: 50000,
|
|
48
|
+
span_streamer: spanStreamer
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const aggregator = new StreamingSpanEventAggregator(opts, collector, metrics)
|
|
52
|
+
|
|
53
|
+
return aggregator
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function createStandardAggregator(config, collector, metrics) {
|
|
57
|
+
logger.trace('Creating standard span event aggregator.')
|
|
58
|
+
|
|
59
|
+
const opts = {
|
|
60
|
+
periodMs: config.event_harvest_config.report_period_ms,
|
|
61
|
+
limit: config.event_harvest_config.harvest_limits.span_event_data
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const aggregator = new SpanEventAggregator(opts, collector, metrics)
|
|
65
|
+
return aggregator
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function validateInfiniteTracing(trace_observer) {
|
|
69
|
+
if (!psemver.satisfies('>=10.10.0')) {
|
|
70
|
+
logger.warn(
|
|
71
|
+
'Infinite tracing disabled: this version of Node is not supported (must be >=10.10.0)'
|
|
72
|
+
)
|
|
73
|
+
return false
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
trace_observer.host = trace_observer.host.trim()
|
|
77
|
+
|
|
78
|
+
if (!validateHostName(trace_observer.host)) {
|
|
79
|
+
logger.warn('Infinite tracing disabled: invalid infinite_tracing.trace_observer.host value')
|
|
80
|
+
|
|
81
|
+
return false
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (typeof trace_observer.port !== 'string') {
|
|
85
|
+
trace_observer.port = String(trace_observer.port)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
trace_observer.port = trace_observer.port.trim()
|
|
89
|
+
|
|
90
|
+
if (!validatePortValue(trace_observer.port)) {
|
|
91
|
+
logger.warn('Infinite tracing disabled: invalid infinite_tracing.trace_observer.port value')
|
|
92
|
+
|
|
93
|
+
return false
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return true
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function validateHostName(host) {
|
|
100
|
+
// Regular expression for validating a hostname
|
|
101
|
+
const hostReg = /(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\.)+[a-zA-Z]{2,63}$)/
|
|
102
|
+
|
|
103
|
+
return hostReg.test(host)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function validatePortValue(port) {
|
|
107
|
+
if (port.length === 0) return false
|
|
108
|
+
|
|
109
|
+
return !isNaN(port)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
module.exports = createSpanEventAggregator
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const STRING_TYPE = 'string_value'
|
|
4
|
+
const BOOL_TYPE = 'bool_value'
|
|
5
|
+
const INT_TYPE = 'int_value'
|
|
6
|
+
const DOUBLE_TYPE = 'double_value'
|
|
7
|
+
|
|
8
|
+
function mapToStreamingType(value) {
|
|
9
|
+
if (value === null || value === undefined) {
|
|
10
|
+
return
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const valueType = typeof value
|
|
14
|
+
|
|
15
|
+
let protoTypeString = null
|
|
16
|
+
switch (valueType) {
|
|
17
|
+
case 'string': {
|
|
18
|
+
protoTypeString = STRING_TYPE
|
|
19
|
+
break
|
|
20
|
+
}
|
|
21
|
+
case 'boolean': {
|
|
22
|
+
protoTypeString = BOOL_TYPE
|
|
23
|
+
break
|
|
24
|
+
}
|
|
25
|
+
case 'number': {
|
|
26
|
+
const isInteger = Number.isInteger(value)
|
|
27
|
+
protoTypeString = isInteger ? INT_TYPE : DOUBLE_TYPE
|
|
28
|
+
break
|
|
29
|
+
}
|
|
30
|
+
default: {
|
|
31
|
+
protoTypeString = null
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (protoTypeString) {
|
|
36
|
+
return {
|
|
37
|
+
[protoTypeString]: value
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = mapToStreamingType
|
|
@@ -35,6 +35,11 @@ class SpanEventAggregator extends EventAggregator {
|
|
|
35
35
|
return [this.runId, metrics, eventData]
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
start() {
|
|
39
|
+
logger.debug('starting SpanEventAggregator')
|
|
40
|
+
return super.start()
|
|
41
|
+
}
|
|
42
|
+
|
|
38
43
|
send() {
|
|
39
44
|
if (spanLogger.traceEnabled()) {
|
|
40
45
|
spanLogger.trace({
|
package/lib/spans/span-event.js
CHANGED
|
@@ -12,6 +12,7 @@ const CATEGORIES = {
|
|
|
12
12
|
DATASTORE: 'datastore',
|
|
13
13
|
GENERIC: 'generic'
|
|
14
14
|
}
|
|
15
|
+
|
|
15
16
|
const EMPTY_USER_ATTRS = Object.freeze(Object.create(null))
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -48,7 +49,8 @@ class SpanIntrinsics {
|
|
|
48
49
|
* @class
|
|
49
50
|
*/
|
|
50
51
|
class SpanEvent {
|
|
51
|
-
constructor(attributes) {
|
|
52
|
+
constructor(attributes, customAttributes) {
|
|
53
|
+
this.customAttributes = customAttributes
|
|
52
54
|
this.attributes = attributes
|
|
53
55
|
this.intrinsics = new SpanIntrinsics()
|
|
54
56
|
}
|
|
@@ -78,14 +80,15 @@ class SpanEvent {
|
|
|
78
80
|
*/
|
|
79
81
|
static fromSegment(segment, parentId = null, isRoot = false) {
|
|
80
82
|
const attributes = segment.attributes.get(DESTINATIONS.SPAN_EVENT)
|
|
83
|
+
const customAttributes = segment.customAttributes.get(DESTINATIONS.SPAN_EVENT)
|
|
81
84
|
|
|
82
85
|
let span = null
|
|
83
86
|
if (HttpSpanEvent.testSegment(segment)) {
|
|
84
|
-
span = new HttpSpanEvent(attributes)
|
|
87
|
+
span = new HttpSpanEvent(attributes, customAttributes)
|
|
85
88
|
} else if (DatastoreSpanEvent.testSegment(segment)) {
|
|
86
|
-
span = new DatastoreSpanEvent(attributes)
|
|
89
|
+
span = new DatastoreSpanEvent(attributes, customAttributes)
|
|
87
90
|
} else {
|
|
88
|
-
span = new SpanEvent(attributes)
|
|
91
|
+
span = new SpanEvent(attributes, customAttributes)
|
|
89
92
|
}
|
|
90
93
|
|
|
91
94
|
const tx = segment.transaction
|
|
@@ -120,11 +123,21 @@ class SpanEvent {
|
|
|
120
123
|
toJSON() {
|
|
121
124
|
return [
|
|
122
125
|
_filterNulls(this.intrinsics),
|
|
123
|
-
|
|
126
|
+
this.customAttributes ?
|
|
127
|
+
_filterNulls(this.customAttributes) :
|
|
128
|
+
EMPTY_USER_ATTRS,
|
|
124
129
|
_filterNulls(this.attributes)
|
|
125
130
|
]
|
|
126
131
|
}
|
|
127
132
|
|
|
133
|
+
addCustomAttribute(key, value, truncateExempt = false) {
|
|
134
|
+
const {attributeFilter} = Config.getInstance()
|
|
135
|
+
const dest = attributeFilter.filterSegment(DESTINATIONS.SPAN_EVENT, key)
|
|
136
|
+
if (dest & DESTINATIONS.SPAN_EVENT) {
|
|
137
|
+
this.customAttributes[key] = truncateExempt ? value : _truncate(value)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
128
141
|
addAttribute(key, value, truncateExempt = false) {
|
|
129
142
|
const {attributeFilter} = Config.getInstance()
|
|
130
143
|
const dest = attributeFilter.filterSegment(DESTINATIONS.SPAN_EVENT, key)
|
|
@@ -141,8 +154,8 @@ class SpanEvent {
|
|
|
141
154
|
* @class
|
|
142
155
|
*/
|
|
143
156
|
class HttpSpanEvent extends SpanEvent {
|
|
144
|
-
constructor(attributes) {
|
|
145
|
-
super(attributes)
|
|
157
|
+
constructor(attributes, customAttributes) {
|
|
158
|
+
super(attributes, customAttributes)
|
|
146
159
|
|
|
147
160
|
this.intrinsics.category = CATEGORIES.HTTP
|
|
148
161
|
this.intrinsics.component = attributes.library || HTTP_LIBRARY
|
|
@@ -175,8 +188,8 @@ class HttpSpanEvent extends SpanEvent {
|
|
|
175
188
|
* @class.
|
|
176
189
|
*/
|
|
177
190
|
class DatastoreSpanEvent extends SpanEvent {
|
|
178
|
-
constructor(attributes) {
|
|
179
|
-
super(attributes)
|
|
191
|
+
constructor(attributes, customAttributes) {
|
|
192
|
+
super(attributes, customAttributes)
|
|
180
193
|
|
|
181
194
|
this.intrinsics.category = CATEGORIES.DATASTORE
|
|
182
195
|
this.intrinsics['span.kind'] = CLIENT_KIND
|