Skip to content

Commit

Permalink
move recorders to lib/metrics/recorders
Browse files Browse the repository at this point in the history
  • Loading branch information
svetlanabrennan committed Oct 16, 2024
1 parent b917b3e commit 8554500
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 122 deletions.
50 changes: 3 additions & 47 deletions lib/db/parsed-statement.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@

'use strict'

const { DB, ALL } = require('../metrics/names')
const { DESTINATIONS } = require('../config/attribute-filter')
const _recordMetrics = require('../../lib/metrics/recorders/record-metrics')

function ParsedStatement(type, operation, collection, raw) {
this.type = type
Expand All @@ -22,53 +21,10 @@ function ParsedStatement(type, operation, collection, raw) {
}

ParsedStatement.prototype.recordMetrics = function recordMetrics(segment, scope) {
const duration = segment.getDurationInMillis()
const exclusive = segment.getExclusiveDurationInMillis()
const transaction = segment.transaction
const type = transaction.isWeb() ? DB.WEB : DB.OTHER
const thisTypeSlash = this.type + '/'
const operation = DB.OPERATION + '/' + thisTypeSlash + this.operation

// Note, an operation metric should _always_ be created even if the action was
// a statement. This is part of the spec.

// Rollups
transaction.measure(operation, null, duration, exclusive)
transaction.measure(DB.PREFIX + type, null, duration, exclusive)
transaction.measure(DB.PREFIX + thisTypeSlash + type, null, duration, exclusive)
transaction.measure(DB.PREFIX + thisTypeSlash + ALL, null, duration, exclusive)
transaction.measure(DB.ALL, null, duration, exclusive)

// If we can parse the SQL statement, create a 'statement' metric, and use it
// as the scoped metric for transaction breakdowns. Otherwise, skip the
// 'statement' metric and use the 'operation' metric as the scoped metric for
// transaction breakdowns.
let collection
if (this.collection) {
collection = DB.STATEMENT + '/' + thisTypeSlash + this.collection + '/' + this.operation
transaction.measure(collection, null, duration, exclusive)
if (scope) {
transaction.measure(collection, scope, duration, exclusive)
}
} else if (scope) {
transaction.measure(operation, scope, duration, exclusive)
}

// This recorder is side-effectful Because we are depending on the recorder
// setting the transaction name, recorders must always be run before generating
// the final transaction trace
segment.name = collection || operation

// Datastore instance metrics.
const attributes = segment.attributes.get(DESTINATIONS.TRANS_SEGMENT)
if (attributes.host && attributes.port_path_or_id) {
const instanceName =
DB.INSTANCE + '/' + thisTypeSlash + attributes.host + '/' + attributes.port_path_or_id
transaction.measure(instanceName, null, duration, exclusive)
}
_recordMetrics.bind(this)(segment, scope)

if (this.raw) {
transaction.agent.queries.add(segment, this.type.toLowerCase(), this.raw, this.trace)
segment.transaction.agent.queries.add(segment, this.type.toLowerCase(), this.raw, this.trace)
}
}

Expand Down
29 changes: 29 additions & 0 deletions lib/metrics/recorders/middleware-metric-recorder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2024 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

'use strict'

/**
* Creates a recorder for middleware metrics.
*
* @private
* @param {object} _shim instance of shim
* @param {string} metricName name of metric
* @returns {Function} recorder for middleware
*/
function makeMiddlewareRecorder(_shim, metricName) {
return function middlewareMetricRecorder(segment, scope) {
const duration = segment.getDurationInMillis()
const exclusive = segment.getExclusiveDurationInMillis()
const transaction = segment.transaction

if (scope) {
transaction.measure(metricName, scope, duration, exclusive)
}
transaction.measure(metricName, null, duration, exclusive)
}
}

module.exports = makeMiddlewareRecorder
58 changes: 58 additions & 0 deletions lib/metrics/recorders/record-metrics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2024 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

'use strict'

const { DB, ALL } = require('../../metrics/names')
const { DESTINATIONS } = require('../../config/attribute-filter')

function _recordMetrics(segment, scope) {
const duration = segment.getDurationInMillis()
const exclusive = segment.getExclusiveDurationInMillis()
const transaction = segment.transaction
const type = transaction.isWeb() ? DB.WEB : DB.OTHER
const thisTypeSlash = this.type + '/'
const operation = DB.OPERATION + '/' + thisTypeSlash + this.operation

// Note, an operation metric should _always_ be created even if the action was
// a statement. This is part of the spec.

// Rollups
transaction.measure(operation, null, duration, exclusive)
transaction.measure(DB.PREFIX + type, null, duration, exclusive)
transaction.measure(DB.PREFIX + thisTypeSlash + type, null, duration, exclusive)
transaction.measure(DB.PREFIX + thisTypeSlash + ALL, null, duration, exclusive)
transaction.measure(DB.ALL, null, duration, exclusive)

// If we can parse the SQL statement, create a 'statement' metric, and use it
// as the scoped metric for transaction breakdowns. Otherwise, skip the
// 'statement' metric and use the 'operation' metric as the scoped metric for
// transaction breakdowns.
let collection
if (this.collection) {
collection = DB.STATEMENT + '/' + thisTypeSlash + this.collection + '/' + this.operation
transaction.measure(collection, null, duration, exclusive)
if (scope) {
transaction.measure(collection, scope, duration, exclusive)
}
} else if (scope) {
transaction.measure(operation, scope, duration, exclusive)
}

// This recorder is side-effectful Because we are depending on the recorder
// setting the transaction name, recorders must always be run before generating
// the final transaction trace
segment.name = collection || operation

// Datastore instance metrics.
const attributes = segment.attributes.get(DESTINATIONS.TRANS_SEGMENT)
if (attributes.host && attributes.port_path_or_id) {
const instanceName =
DB.INSTANCE + '/' + thisTypeSlash + attributes.host + '/' + attributes.port_path_or_id
transaction.measure(instanceName, null, duration, exclusive)
}
}

module.exports = _recordMetrics
62 changes: 62 additions & 0 deletions lib/metrics/recorders/record-operation-metrics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2024 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

'use strict'

const metrics = require('../../metrics/names')

/**
* Records all the metrics required for database operations.
*
* - `_recordOperationMetrics(segment [, scope])`
*
* @private
* @this DatastoreShim
* @implements {MetricFunction}
* @param {TraceSegment} segment - The segment being recorded.
* @param {string} [scope] - The scope of the segment.
* @see DatastoreShim#recordOperation
* @see MetricFunction
*/
function recordOperationMetrics(segment, scope) {
if (!segment) {
return
}

Check warning on line 26 in lib/metrics/recorders/record-operation-metrics.js

View check run for this annotation

Codecov / codecov/patch

lib/metrics/recorders/record-operation-metrics.js#L25-L26

Added lines #L25 - L26 were not covered by tests

const duration = segment.getDurationInMillis()
const exclusive = segment.getExclusiveDurationInMillis()
const transaction = segment.transaction
const type = transaction.isWeb() ? 'allWeb' : 'allOther'
const operation = segment.name

if (scope) {
transaction.measure(operation, scope, duration, exclusive)
}

transaction.measure(operation, null, duration, exclusive)
transaction.measure(metrics.DB.PREFIX + type, null, duration, exclusive)
transaction.measure(metrics.DB.ALL, null, duration, exclusive)
transaction.measure(this._metrics.ALL, null, duration, exclusive)
transaction.measure(
metrics.DB.PREFIX + this._metrics.PREFIX + '/' + type,
null,
duration,
exclusive
)

const attributes = segment.getAttributes()
if (attributes.host && attributes.port_path_or_id) {
const instanceName = [
metrics.DB.INSTANCE,
this._metrics.PREFIX,
attributes.host,
attributes.port_path_or_id
].join('/')

transaction.measure(instanceName, null, duration, exclusive)
}
}

module.exports = recordOperationMetrics
55 changes: 2 additions & 53 deletions lib/shim/datastore-shim.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const Shim = require('./shim')
const urltils = require('../util/urltils')
const util = require('util')
const specs = require('./specs')
const recordOperationMetrics = require('../../lib/metrics/recorders/record-operation-metrics')
const { DatastoreParameters } = specs.params

/**
Expand Down Expand Up @@ -284,7 +285,7 @@ function recordOperation(nodule, properties, opSpec) {
if (!segDesc?.name?.startsWith(shim._metrics.OPERATION)) {
segDesc.name = shim._metrics.OPERATION + segDesc.name
}
segDesc.recorder = _recordOperationMetrics.bind(shim)
segDesc.recorder = recordOperationMetrics.bind(shim)
}

return segDesc
Expand Down Expand Up @@ -612,58 +613,6 @@ function _recordQueryMetrics(parsed, segment, scope) {
}
}

/**
* Records all the metrics required for database operations.
*
* - `_recordOperationMetrics(segment [, scope])`
*
* @private
* @this DatastoreShim
* @implements {MetricFunction}
* @param {TraceSegment} segment - The segment being recorded.
* @param {string} [scope] - The scope of the segment.
* @see DatastoreShim#recordOperation
* @see MetricFunction
*/
function _recordOperationMetrics(segment, scope) {
if (!segment) {
return
}

const duration = segment.getDurationInMillis()
const exclusive = segment.getExclusiveDurationInMillis()
const transaction = segment.transaction
const type = transaction.isWeb() ? 'allWeb' : 'allOther'
const operation = segment.name

if (scope) {
transaction.measure(operation, scope, duration, exclusive)
}

transaction.measure(operation, null, duration, exclusive)
transaction.measure(metrics.DB.PREFIX + type, null, duration, exclusive)
transaction.measure(metrics.DB.ALL, null, duration, exclusive)
transaction.measure(this._metrics.ALL, null, duration, exclusive)
transaction.measure(
metrics.DB.PREFIX + this._metrics.PREFIX + '/' + type,
null,
duration,
exclusive
)

const attributes = segment.getAttributes()
if (attributes.host && attributes.port_path_or_id) {
const instanceName = [
metrics.DB.INSTANCE,
this._metrics.PREFIX,
attributes.host,
attributes.port_path_or_id
].join('/')

transaction.measure(instanceName, null, duration, exclusive)
}
}

/**
* Extracts the query string from the arguments according to the given spec.
*
Expand Down
24 changes: 2 additions & 22 deletions lib/shim/webframework-shim/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const {
} = require('./common')
const { assignCLMSymbol } = require('../../util/code-level-metrics')
const { RecorderSpec } = require('../specs')
const makeMiddlewareRecorder = require('../../metrics/recorders/middleware-metric-recorder')

const MIDDLEWARE_TYPE_DETAILS = {
APPLICATION: { name: 'Mounted App: ', path: true, record: false },
Expand Down Expand Up @@ -88,7 +89,7 @@ function constructRecorder({ txInfo, typeDetails, shim, metricName }) {
let recorder = null
if (typeDetails.record) {
const stackPath = txInfo.transaction.nameState.getPath() || ''
recorder = _makeMiddlewareRecorder(shim, metricName + '/' + stackPath)
recorder = makeMiddlewareRecorder(shim, metricName + '/' + stackPath)
}
return recorder
}
Expand Down Expand Up @@ -325,27 +326,6 @@ module.exports._recordMiddleware = function _recordMiddleware(shim, middleware,
)
}

/**
* Creates a recorder for middleware metrics.
*
* @private
* @param {object} _shim instance of shim
* @param {string} metricName name of metric
* @returns {Function} recorder for middleware
*/
function _makeMiddlewareRecorder(_shim, metricName) {
return function middlewareMetricRecorder(segment, scope) {
const duration = segment.getDurationInMillis()
const exclusive = segment.getExclusiveDurationInMillis()
const transaction = segment.transaction

if (scope) {
transaction.measure(metricName, scope, duration, exclusive)
}
transaction.measure(metricName, null, duration, exclusive)
}
}

/**
* Wrap the `next` middleware function and push on our name state if we find it. We only want to
* push the name state if there is a next so that we can safely remove it
Expand Down

0 comments on commit 8554500

Please sign in to comment.