Skip to content

Commit

Permalink
refactor: Updated instrumentation/core/http.js to reduce the cognitiv…
Browse files Browse the repository at this point in the history
…e complexity to an allowable value (#1922)
  • Loading branch information
bizob2828 committed Dec 20, 2023
1 parent 9003791 commit 4c30d97
Showing 1 changed file with 120 additions and 84 deletions.
204 changes: 120 additions & 84 deletions lib/instrumentation/core/http.js
Expand Up @@ -5,8 +5,6 @@

'use strict'

/* eslint sonarjs/cognitive-complexity: ["error", 42] -- TODO: https://issues.newrelic.com/browse/NEWRELIC-5252 */

const shimmer = require('../../shimmer')
const logger = require('../../logger').child({ component: 'http' })
const recordWeb = require('../../metrics/recorders/http')
Expand Down Expand Up @@ -35,8 +33,6 @@ const symbols = require('../../symbols')
function wrapEmitWithTransaction(agent, emit, isHTTPS) {
const tracer = agent.tracer
const transport = isHTTPS ? 'HTTPS' : 'HTTP'
let serverPort = null

return tracer.transactionProxy(function wrappedHandler(evnt, request, response) {
const transaction = tracer.getTransaction()
if (!transaction) {
Expand Down Expand Up @@ -91,15 +87,7 @@ function wrapEmitWithTransaction(agent, emit, isHTTPS) {

segment.addSpanAttribute('request.uri', transaction.url)

// store the port on which this transaction runs
if (this.address instanceof Function) {
const address = this.address()
if (address) {
serverPort = address.port
}
}
transaction.port = serverPort

transaction.port = parsePort(this)
// need to set any config-driven names early for RUM
logger.trace(
{ url: request.url, transaction: transaction.id },
Expand All @@ -114,68 +102,102 @@ function wrapEmitWithTransaction(agent, emit, isHTTPS) {

synthetics.assignHeadersToTransaction(agent.config, transaction, request.headers)

if (agent.config.distributed_tracing.enabled) {
// Node http headers are automatically lowercase
transaction.acceptDistributedTraceHeaders(transport, request.headers)
} else if (agent.config.cross_application_tracer.enabled) {
const { id, transactionId } = cat.extractCatHeaders(request.headers)
const { externalId, externalTransaction } = cat.parseCatData(
id,
transactionId,
agent.config.encoding_key
)
cat.assignCatToTransaction(externalId, externalTransaction, transaction)
}
maybeAddDtCatHeaders({ transaction, request, transport, agent })

function instrumentedFinish() {
// Remove listeners so this doesn't get called twice.
response.removeListener('finish', instrumentedFinish)
response.removeListener('close', instrumentedFinish)
response.once('finish', instrumentedFinish.bind(response, segment, transaction))
response.once('close', instrumentedFinish.bind(response, segment, transaction))

// Naming must happen before the segment and transaction are ended,
// because metrics recording depends on naming's side effects.
transaction.finalizeNameFromUri(transaction.parsedUrl, response.statusCode)
return tracer.bindFunction(emit, segment).apply(this, arguments)
})
}

if (response) {
if (response.statusCode != null) {
const responseCode = String(response.statusCode)
/**
* Gets the port from the Server object
*
* @param {object} server http(s) server
* @returns {number|null} parsed port
*/
function parsePort(server) {
let serverPort = null
// store the port on which this transaction runs
if (server.address instanceof Function) {
const address = server.address()
if (address) {
serverPort = address.port
}
}
return serverPort
}

if (/^\d+$/.test(responseCode)) {
transaction.trace.attributes.addAttribute(
DESTS.TRANS_COMMON,
'http.statusCode',
responseCode
)
function maybeAddDtCatHeaders({ agent, request, transaction, transport }) {
if (agent.config.distributed_tracing.enabled) {
// Node http headers are automatically lowercase
transaction.acceptDistributedTraceHeaders(transport, request.headers)
} else if (agent.config.cross_application_tracer.enabled) {
const { id, transactionId } = cat.extractCatHeaders(request.headers)
const { externalId, externalTransaction } = cat.parseCatData(
id,
transactionId,
agent.config.encoding_key
)
cat.assignCatToTransaction(externalId, externalTransaction, transaction)
}
}

segment.addSpanAttribute('http.statusCode', responseCode)
}
}
/**
* Adds instrumentation to response on finish/close.
* It will add `http.statusCode`, `http.statusText`
* to the transaction trace and span.
* It will also assign the response headers to the transaction
*
* @param {TraceSegment} segment active segment
* @param {Transaction} transaction active transaction
*/
function instrumentedFinish(segment, transaction) {
// Remove listeners so this doesn't get called twice.
this.removeListener('finish', instrumentedFinish)
this.removeListener('close', instrumentedFinish)

// Naming must happen before the segment and transaction are ended,
// because metrics recording depends on naming's side effects.
transaction.finalizeNameFromUri(transaction.parsedUrl, this.statusCode)

if (this) {
const { statusCode, statusMessage } = this

if (statusCode != null) {
const responseCode = String(statusCode)

if (/^\d+$/.test(responseCode)) {
transaction.trace.attributes.addAttribute(
DESTS.TRANS_COMMON,
'http.statusCode',
responseCode
)

if (response.statusMessage !== undefined) {
transaction.trace.attributes.addAttribute(
DESTS.TRANS_COMMON,
'http.statusText',
response.statusMessage
)
segment.addSpanAttribute('http.statusCode', responseCode)
}
}

segment.addSpanAttribute('http.statusText', response.statusMessage)
}
if (statusMessage !== undefined) {
transaction.trace.attributes.addAttribute(
DESTS.TRANS_COMMON,
'http.statusText',
statusMessage
)

const headers = response.getHeaders()
if (headers) {
headerAttributes.collectResponseHeaders(headers, transaction)
}
}
segment.addSpanAttribute('http.statusText', statusMessage)
}

// And we are done! End the segment and transaction.
segment.end()
transaction.end()
const headers = this.getHeaders()
if (headers) {
headerAttributes.collectResponseHeaders(headers, transaction)
}
response.once('finish', instrumentedFinish)
response.once('close', instrumentedFinish)
}

return tracer.bindFunction(emit, segment).apply(this, arguments)
})
// And we are done! End the segment and transaction.
segment.end()
transaction.end()
}

function storeTxInfo(transaction, request, response) {
Expand Down Expand Up @@ -341,28 +363,42 @@ function urlToOptions(_url) {
return options
}

function wrapRequest(agent, request) {
return function wrappedRequest(input, options, cb) {
// If the first argument is a URL, merge it into the options object.
// This code is copied from Node internals.
if (typeof input === 'string') {
const urlStr = input
input = urlToOptions(new URL(urlStr))
} else if (input.constructor && input.constructor.name === 'URL') {
input = urlToOptions(input)
} else {
cb = options
options = input
input = null
}
/**
* http.request and http.get signatures vary. This function
* will parse the options and callback
*
* @param {*} input first arg of http.request and http.get
* @param {*} options request opts of callback
* @param {Function} cb if present it is the callback
* @returns {Array} [options, cb]
*/
function parseRequest(input, options, cb) {
// If the first argument is a URL, merge it into the options object.
// This code is copied from Node internals.
if (typeof input === 'string') {
const urlStr = input
input = urlToOptions(new URL(urlStr))
} else if (input.constructor && input.constructor.name === 'URL') {
input = urlToOptions(input)
} else {
cb = options
options = input
input = null
}

if (typeof options === 'function') {
cb = options
options = input || {}
} else {
options = Object.assign(input || {}, options)
}
if (typeof options === 'function') {
cb = options
options = input || {}
} else {
options = Object.assign(input || {}, options)
}

return [options, cb]
}

function wrapRequest(agent, request) {
return function wrappedRequest(input, options, cb) {
;[options, cb] = parseRequest(input, options, cb)
const reqArgs = [options, cb]

// Don't pollute metrics and calls with NR connections
Expand Down

0 comments on commit 4c30d97

Please sign in to comment.