Skip to content

Commit add6827

Browse files
refactor: Migrated redis 4+ instrumentation to subscribe to events emitted (#3773)
Co-authored-by: Bob Evans <robert.evans25@gmail.com>
1 parent bdd35fb commit add6827

File tree

17 files changed

+457
-372
lines changed

17 files changed

+457
-372
lines changed

lib/instrumentation/@node-redis/client.js

Lines changed: 0 additions & 114 deletions
This file was deleted.

lib/instrumentation/@redis/client.js

Lines changed: 0 additions & 7 deletions
This file was deleted.

lib/instrumentation/redis.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const {
1414
module.exports = function initialize(_agent, redis, _moduleName, shim) {
1515
const proto = redis?.RedisClient?.prototype
1616
if (!proto) {
17+
// This will happen when redis >= 4.0.0
1718
shim.logger.warn('Skipping redis instrumentation due to unrecognized module shape')
1819
return false
1920
}

lib/instrumentations.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,7 @@ module.exports = function instrumentations() {
1414
'@grpc/grpc-js': { module: './instrumentation/grpc-js' },
1515
'@hapi/hapi': { type: InstrumentationDescriptor.TYPE_WEB_FRAMEWORK },
1616
'@hapi/vision': { type: InstrumentationDescriptor.TYPE_WEB_FRAMEWORK },
17-
'@node-redis/client': { type: InstrumentationDescriptor.TYPE_DATASTORE },
1817
'@prisma/client': { type: InstrumentationDescriptor.TYPE_DATASTORE },
19-
'@redis/client': { type: InstrumentationDescriptor.TYPE_DATASTORE },
2018
'aws-sdk': { module: './instrumentation/aws-sdk' },
2119
bluebird: { type: InstrumentationDescriptor.TYPE_PROMISE },
2220
connect: { type: InstrumentationDescriptor.TYPE_WEB_FRAMEWORK },

lib/subscriber-configs.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ const subscribers = {
2323
...require('./subscribers/mysql/config'),
2424
...require('./subscribers/mysql2/config'),
2525
...require('./subscribers/nestjs/config'),
26+
...require('./subscribers/node-redis-client/config'),
2627
...require('./subscribers/openai/config'),
2728
...require('./subscribers/pino/config'),
2829
...require('./subscribers/pg/config'),
30+
...require('./subscribers/redis-client/config'),
2931
...require('./subscribers/undici/config')
3032
}
3133

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
'use strict'
7+
const BaseCmdQueueAddCmdSubsriber = require('../redis-client/add-command')
8+
9+
module.exports = class CmdQueueAddCmdSubscriber extends BaseCmdQueueAddCmdSubsriber {
10+
constructor({ agent, logger }) {
11+
super({ agent, logger, packageName: '@node-redis/client' })
12+
}
13+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
const addCommand = {
7+
path: './node-redis-client/add-command',
8+
instrumentations: [{
9+
channelName: 'nr_addCommand',
10+
module: { name: '@node-redis/client', versionRange: '>=1', filePath: 'dist/lib/client/commands-queue.js' },
11+
functionQuery: {
12+
className: 'RedisCommandsQueue',
13+
methodName: 'addCommand',
14+
kind: 'Async'
15+
}
16+
}]
17+
}
18+
19+
const sendCommand = {
20+
path: './node-redis-client/send-command',
21+
instrumentations: [
22+
{
23+
channelName: 'nr_sendCommand',
24+
module: { name: '@node-redis/client', versionRange: '>=1.1.0', filePath: 'dist/lib/client/index.js' },
25+
functionQuery: {
26+
className: 'RedisClient',
27+
methodName: 'sendCommand',
28+
kind: 'Async'
29+
}
30+
},
31+
{
32+
channelName: 'nr_sendCommand',
33+
module: { name: '@node-redis/client', versionRange: '>=1.0.0 <1.1.0', filePath: 'dist/lib/client/index.js' },
34+
functionQuery: {
35+
expressionName: '_RedisClient_sendCommand',
36+
kind: 'Sync'
37+
}
38+
}
39+
]
40+
}
41+
42+
const clientMulti = {
43+
path: './node-redis-client/multi',
44+
instrumentations: [
45+
{
46+
channelName: 'nr_multi',
47+
module: { name: '@node-redis/client', versionRange: '>=1', filePath: 'dist/lib/client/index.js' },
48+
functionQuery: {
49+
className: 'RedisClient',
50+
methodName: 'multi',
51+
kind: 'Sync'
52+
}
53+
}
54+
]
55+
}
56+
57+
module.exports = {
58+
'@node-redis/client': [
59+
addCommand,
60+
sendCommand,
61+
clientMulti,
62+
]
63+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
'use strict'
7+
const BaseMultiSubscriber = require('../redis-client/multi')
8+
9+
module.exports = class ClientMultiSubscriber extends BaseMultiSubscriber {
10+
constructor({ agent, logger }) {
11+
super({ agent, logger, packageName: '@node-redis/client' })
12+
}
13+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
const BaseSendCommandSubscriber = require('../redis-client/send-command')
7+
8+
module.exports = class ClientSendCommandSubscriber extends BaseSendCommandSubscriber {
9+
constructor({ agent, logger }) {
10+
super({ agent, logger, packageName: '@node-redis/client' })
11+
}
12+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
'use strict'
7+
const DbOperationSubscriber = require('../db-operation')
8+
const { redisClientOpts } = require('../../symbols')
9+
10+
/**
11+
* Listens to events on `RedisCommandQueue.addCommand` to create
12+
* the segment with necessary datastore parameters for a given Redis
13+
* operation.
14+
*
15+
* Relies on `ctx[redisClientOpts]` being set for the `host`, `port_path_or_id`,
16+
* and `database` parameters.
17+
*/
18+
module.exports = class CmdQueueAddCmdSubscriber extends DbOperationSubscriber {
19+
constructor({ agent, logger, packageName = '@redis/client' }) {
20+
super({ agent, logger, packageName, channelName: 'nr_addCommand', system: 'Redis' })
21+
this.events = ['asyncEnd']
22+
}
23+
24+
handler(data, ctx) {
25+
const { arguments: args } = data
26+
const [cmd, key, value] = args[0]
27+
this.setParameters({ ctx, key, value })
28+
this.operation = cmd?.toLowerCase() || 'other'
29+
return super.handler(data, ctx)
30+
}
31+
32+
setParameters({ ctx, key, value }) {
33+
const clientParams = ctx[redisClientOpts] ?? {}
34+
this.parameters = Object.assign({}, clientParams)
35+
this.parameters.product = this.system
36+
37+
if (this.agent.config.attributes.enabled) {
38+
if (key) {
39+
this.parameters.key = JSON.stringify(key)
40+
}
41+
if (value) {
42+
this.parameters.value = JSON.stringify(value)
43+
}
44+
}
45+
}
46+
}

0 commit comments

Comments
 (0)