Skip to content

Commit 6daf011

Browse files
committed
update tests for metrics
1 parent a307c57 commit 6daf011

File tree

22 files changed

+173
-78
lines changed

22 files changed

+173
-78
lines changed

lib/shimmer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ const shimmer = (module.exports = {
306306
registerHooks(agent) {
307307
// add the packages from the subscriber based instrumentation
308308
// this is only added to add tracking metrics
309-
pkgsToHook.push(...Object.keys(subscriptions), ...trackingPkgs)
309+
Array.prototype.push.apply(pkgsToHook, trackingPkgs)
310310
this._ritm = new Hook(pkgsToHook, function onHook(exports, name, basedir) {
311311
return _postLoad(agent, exports, name, basedir)
312312
})

lib/subscribers/base.js

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,36 +30,41 @@ const ArrayPrototypeSplice = makeCall(Array.prototype.splice)
3030

3131
/**
3232
* @property {object} agent A New Relic Node.js agent instance.
33-
* @property {object} logger An agent logger instance.
33+
* @property {TracingChannel} channel The tracing channel instance this subscriber will be monitoring.
34+
* @property {string} channelName A unique name for the diagnostics channel
35+
* that will be registered.
3436
* @property {object} config The agent configuration object.
37+
* @property {string} id A unique identifier for the subscriber, combining the prefix, package
38+
* name, and channel name.
39+
* @property {object} logger An agent logger instance.
3540
* @property {string} packageName The name of the module being instrumented.
3641
* This is the same string one would pass to the `require` function.
37-
* @property {string} channelName A unique name for the diagnostics channel
38-
* that will be registered.
42+
* @property {AsyncLocalStorage} store The async local storage instance used for context management.
43+
* @property {number} [callback=null] Position of callback if it needs to be wrapped for instrumentation.
44+
* -1 means last argument.
3945
* @property {string[]} [events=[]] Set of tracing channel event names to
4046
* register handlers for. For any name in the set, a corresponding method
4147
* must exist on the subscriber instance. The method will be passed the
4248
* event object. Possible event names are `start`, `end`, `asyncStart`,
4349
* `asyncEnd`, and `error`.
44-
*
45-
* See {@link https://nodejs.org/api/diagnostics_channel.html#class-tracingchannel}
46-
* @property {boolean} [opaque=false] If true, any children segments will not be created.
50+
* See {@link https://nodejs.org/api/diagnostics_channel.html#class-tracingchannel}
4751
* @property {boolean} [internal=false] If true, any children segments from the same library
4852
* will not be created.
53+
* @property {boolean} [opaque=false] If true, any children segments will not be created.
4954
* @property {string} [prefix='orchestrion:'] String to prepend to diagnostics
5055
* channel event names. This provides a namespace for the events we are
5156
* injecting into a module.
52-
* @property {boolean} [requireActiveTx=true] If true, the subscriber will only handle events
53-
* when there is an active transaction.
5457
* @property {boolean} [propagateContext=false] If true, it will bind `asyncStart` to the store
5558
* and re-propagate the active context. It will also attach the `transaction` to the event in
5659
* `start.bindStore`. This is used for functions that queue async code and context is lost.
57-
* @property {string} id A unique identifier for the subscriber, combining the prefix, package
58-
* name, and channel name.
59-
* @property {TracingChannel} channel The tracing channel instance this subscriber will be monitoring.
60-
* @property {AsyncLocalStorage} store The async local storage instance used for context management.
61-
* @property {number} [callback=null] Position of callback if it needs to be wrapped for instrumentation.
62-
* -1 means last argument.
60+
* @property {boolean} [requireActiveTx=true] If true, the subscriber will only handle events
61+
* when there is an active transaction.
62+
* @property {object} [targetModuleMeta] Defines the target module's name and
63+
* version string, i.e. is an object `{ name, version }`. This is only necessary
64+
* when target instrumentation can surface an unexpected name for the
65+
* `packageName` property. For example, `express` uses multiple modules to
66+
* compose its core functionality. We want to track things under the `express`
67+
* name, but `packageName` will be set to `router` is most cases.
6368
*/
6469
class Subscriber {
6570
#usageMetricRecorded = false
@@ -239,10 +244,11 @@ class Subscriber {
239244
*/
240245
const handler = (data) => {
241246
if (this.#usageMetricRecorded === false) {
247+
const meta = this.targetModuleMeta
242248
recordSupportabilityMetric({
243249
agent: this.agent,
244-
moduleName: this.packageName,
245-
moduleVersion: data.moduleVersion
250+
moduleName: meta?.name || this.packageName,
251+
moduleVersion: meta?.version || data.moduleVersion
246252
})
247253
this.#usageMetricRecorded = true
248254
}

lib/subscribers/express/base.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
const MiddlewareSubscriber = require('../middleware')
7+
const resolvePackageVersion = require('../resolve-package-version')
78

89
/**
910
* Special error handler to ignore if the error passed to next handler
@@ -18,9 +19,24 @@ function errorHandler(err) {
1819
}
1920

2021
class ExpressSubscriber extends MiddlewareSubscriber {
22+
#resolvedMeta
23+
2124
constructor({ agent, logger, packageName = 'express', channelName }) {
2225
super({ agent, logger, packageName, channelName, system: 'Expressjs', errorHandler })
2326
}
27+
28+
get targetModuleMeta() {
29+
if (this.#resolvedMeta !== undefined) {
30+
return this.#resolvedMeta
31+
}
32+
33+
const version = resolvePackageVersion('express')
34+
this.#resolvedMeta = {
35+
name: 'express',
36+
version
37+
}
38+
return this.#resolvedMeta
39+
}
2440
}
2541

2642
module.exports = ExpressSubscriber

test/lib/custom-assertions/assert-pkg-tracking-metrics.js

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,18 @@
44
*/
55

66
'use strict'
7-
const NAMES = require('#agentlib/metrics/names.js')
8-
const assertMetrics = require('./assert-metrics')
7+
98
const semver = require('semver')
9+
const assertMetrics = require('./assert-metrics')
10+
11+
const {
12+
FEATURES: {
13+
INSTRUMENTATION: {
14+
SUBSCRIBER_USED,
15+
ON_REQUIRE
16+
}
17+
}
18+
} = require('#agentlib/metrics/names.js')
1019

1120
/**
1221
* assertion to verify tracking metrics for a given
@@ -16,19 +25,29 @@ const semver = require('semver')
1625
* @param {string} params.pkg name of package
1726
* @param {string} params.version version of package
1827
* @param {Agent} params.agent agent instance
28+
* @param {boolean} params.subscriberType When true, the metrics are expected
29+
* to have been generated from a subscriber based instrumentation. Otherwise,
30+
* the metrics are expected to be generated from a shimmer based
31+
* instrumentation.
1932
* @param {object} [deps] Injected dependencies.
2033
* @param {object} [deps.assert] Assertion library to use.
2134
*/
2235
module.exports = function assertPackageMetrics(
23-
{ pkg, version, agent },
36+
{ pkg, version, agent, subscriberType = false },
2437
{ assert = require('node:assert') } = {}
2538
) {
26-
const metrics = [
27-
[{ name: `${NAMES.FEATURES.INSTRUMENTATION.ON_REQUIRE}/${pkg}` }]
28-
]
39+
const metrics = []
40+
const prefix = subscriberType === true
41+
? `${SUBSCRIBER_USED}/${pkg}`
42+
: `${ON_REQUIRE}/${pkg}`
2943

44+
metrics.push([{ name: prefix }])
3045
if (version) {
31-
metrics.push([{ name: `${NAMES.FEATURES.INSTRUMENTATION.ON_REQUIRE}/${pkg}/Version/${semver.major(version)}` }])
46+
const major = semver.major(version)
47+
const suffix = subscriberType === true
48+
? `/${major}`
49+
: `/Version/${major}`
50+
metrics.push([{ name: `${prefix}${suffix}` }])
3251
}
3352

3453
assertMetrics(agent.metrics, metrics, false, false, { assert })

test/versioned/amqplib/callback.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ test('amqplib callback instrumentation', async function (t) {
6262

6363
await t.test('should log tracking metrics', function(t) {
6464
const { agent } = t.nr
65-
assertPackageMetrics({ agent, pkg: 'amqplib', version })
65+
assertPackageMetrics({ agent, pkg: 'amqplib', version, subscriberType: true })
6666
})
6767

6868
await t.test('connect in a transaction', function (t, end) {

test/versioned/amqplib/promises.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ test('amqplib promise instrumentation', async function (t) {
5555

5656
await t.test('should log tracking metrics', function(t) {
5757
const { agent } = t.nr
58-
assertPackageMetrics({ agent, pkg: 'amqplib', version })
58+
assertPackageMetrics({ agent, pkg: 'amqplib', version, subscriberType: true })
5959
})
6060

6161
await t.test('connect in a transaction', async function (t) {

test/versioned/bunyan/bunyan.test.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,10 @@ test('logging enabled/disabled', async (t) => {
5151

5252
await t.test('should log tracking metrics', function(t) {
5353
setup(t.nr, { application_logging: { enabled: true } })
54-
const { agent } = t.nr
54+
const { agent, bunyan } = t.nr
5555
const { version } = require('bunyan/package.json')
56-
assertPackageMetrics({ agent, pkg: 'bunyan', version })
56+
bunyan.createLogger({ name: 'test-logger' })
57+
assertPackageMetrics({ agent, pkg: 'bunyan', version, subscriberType: true })
5758
})
5859

5960
await t.test('logging enabled', (t) => {

test/versioned/elastic/elasticsearch.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ test('Elasticsearch instrumentation', async (t) => {
407407
expected['Datastore/instance/ElasticSearch/' + HOST_ID] = 5
408408
checkMetrics(unscoped, expected)
409409
const pkgName = '@elastic/elasticsearch'
410-
assertPackageMetrics({ agent, pkg: pkgName, version: pkgVersion })
410+
assertPackageMetrics({ agent, pkg: pkgName, version: pkgVersion, subscriberType: true })
411411
})
412412
})
413413

test/versioned/express/app-use.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ test('app should be at top of stack when mounted', async function (t) {
3939

4040
const express = require('express')
4141
const { version } = require('express/package.json')
42-
assertPackageMetrics({ agent, pkg: 'express', version })
4342
const main = express()
4443
const app = express()
4544
const app2 = express()
@@ -67,6 +66,7 @@ test('app should be at top of stack when mounted', async function (t) {
6766
// store finished transactions
6867
const finishedTransactions = {}
6968
agent.on('transactionFinished', function (tx) {
69+
assertPackageMetrics({ agent, pkg: 'express', version, subscriberType: true })
7070
finishedTransactions[tx.id] = tx
7171
})
7272

test/versioned/express/ignoring.test.js

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,45 +8,41 @@
88
const test = require('node:test')
99
const helper = require('../../lib/agent_helper')
1010
const API = require('../../../api')
11-
const tsplan = require('@matteo.collina/tspl')
11+
const promiseResolvers = require('../../lib/promise-resolvers')
1212
const { setup, teardown } = require('./utils')
1313

14-
test.beforeEach(async (ctx) => {
15-
await setup(ctx)
16-
})
17-
18-
test.afterEach(teardown)
19-
2014
test('ignoring an Express route', async function (t) {
21-
const { agent, app, port, isExpress5 } = t.nr
22-
const plan = tsplan(t, { plan: 8 })
15+
t.plan(8)
16+
17+
await setup(t)
18+
t.after(() => { teardown(t) })
2319

20+
const { agent, app, port } = t.nr
21+
const { promise, resolve } = promiseResolvers()
2422
const api = new API(agent)
2523

2624
agent.on('transactionFinished', function (transaction) {
27-
plan.equal(
25+
t.assert.equal(
2826
transaction.name,
2927
'WebTransaction/Expressjs/GET//polling/:id',
3028
'transaction has expected name even on error'
3129
)
3230

33-
plan.ok(transaction.ignore, 'transaction is ignored')
31+
t.assert.ok(transaction.ignore, 'transaction is ignored')
3432

35-
plan.ok(!agent.traces.trace, 'should have no transaction trace')
33+
t.assert.ok(!agent.traces.trace, 'should have no transaction trace')
3634

37-
const metrics = agent.metrics._metrics.unscoped
38-
// loading k2 adds instrumentation metrics for things it loads
39-
let expectedMetrics = isExpress5 ? 10 : 8
40-
if (helper.isSecurityAgentEnabled(agent) === true) {
41-
expectedMetrics = isExpress5 ? 21 : 19
42-
}
43-
plan.equal(
44-
Object.keys(metrics).length,
45-
expectedMetrics,
35+
const metrics = Object.keys(agent.metrics._metrics.unscoped).filter(
36+
(k) => k.startsWith('Supportability/') === false
37+
)
38+
t.assert.equal(
39+
metrics.length,
40+
0,
4641
'only supportability metrics added to agent collection'
4742
)
43+
4844
const errors = agent.errors.traceAggregator.errors
49-
plan.equal(errors.length, 0, 'no errors noticed')
45+
t.assert.equal(errors.length, 0, 'no errors noticed')
5046
})
5147

5248
app.get('/polling/:id', function (req, res) {
@@ -57,9 +53,14 @@ test('ignoring an Express route', async function (t) {
5753

5854
const url = 'http://localhost:' + port + '/polling/31337'
5955
helper.makeGetRequest(url, function (error, res, body) {
60-
plan.ifError(error)
61-
plan.equal(res.statusCode, 400, 'got expected error')
62-
plan.deepEqual(body, { status: 'pollpollpoll' }, 'got expected response')
56+
t.assert.ifError(error)
57+
t.assert.equal(res.statusCode, 400, 'got expected error')
58+
t.assert.deepEqual(body, { status: 'pollpollpoll' }, 'got expected response')
59+
60+
// The request finished callback is invoked after the transactionFinished
61+
// callback. So we must trigger the end of the test here.
62+
resolve()
6363
})
64-
await plan.completed
64+
65+
await promise
6566
})

0 commit comments

Comments
 (0)