Skip to content

Commit 2065426

Browse files
authored
fix(agent): Fallback methods for loading traced plugins from layers
2 parents fb5e18e + b631d39 commit 2065426

File tree

9 files changed

+235
-86
lines changed

9 files changed

+235
-86
lines changed

src/index.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import Perf from 'performance-node';
2-
32
import pkg from '../package.json'; // eslint-disable-line import/extensions
43
import { addToReport, addTraceData } from './addToReport';
54

6-
const load = plugin => {
5+
const loadPlugin = plugin => {
76
/*eslint-disable camelcase, no-undef*/
87
if (typeof __non_webpack_require__ === 'function') {
98
return __non_webpack_require__(`./plugins/${plugin}`);
@@ -144,7 +143,7 @@ class TracePlugin {
144143

145144
if (context.config[conf] && context.config[conf].enabled) {
146145
// getting plugin; allows this to be loaded only if enabled.
147-
await load(`${k}`).then(mod => {
146+
await loadPlugin(`${k}`).then(mod => {
148147
plugins[k].wrap = mod.wrap;
149148
plugins[k].unwrap = mod.unwrap;
150149
context[namespace] = {
@@ -153,7 +152,7 @@ class TracePlugin {
153152
data: {},
154153
config: context.config[conf]
155154
};
156-
plugins[k].wrap(context[namespace]);
155+
return plugins[k].wrap(context[namespace]);
157156
});
158157
}
159158
});

src/loadHelper.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { debuglog } from 'util';
2+
3+
const debug = debuglog('@iopipe:trace:loadHelper');
4+
5+
// The module being traced might not be in the lambda NODE_PATH,
6+
// particularly if IOpipe has been installed with lambda layers.
7+
// So here's an attempt at a fallback.
8+
9+
const deriveTargetPath = manualPath => {
10+
const path = manualPath ? manualPath : process.env.LAMBDA_TASK_ROOT;
11+
if (!path) {
12+
return false;
13+
}
14+
const targetPath = `${path}/node_modules`;
15+
process.env.IOPIPE_TARGET_PATH = targetPath;
16+
return targetPath;
17+
};
18+
19+
const appendToPath = manualPath => {
20+
if (!process.env || !process.env.NODE_PATH) {
21+
return false;
22+
}
23+
const targetPath = deriveTargetPath(manualPath);
24+
const pathArray = process.env.NODE_PATH.split(':');
25+
if (!targetPath && pathArray.indexOf(targetPath) === -1) {
26+
process.env.NODE_PATH = `${process.env.NODE_PATH}:${targetPath}`;
27+
}
28+
return targetPath;
29+
};
30+
31+
appendToPath();
32+
33+
const loadModuleForTracing = async (module, path) => {
34+
const targetPath = path ? path : process.env.IOPIPE_TARGET_PATH;
35+
let loadedModule;
36+
try {
37+
loadedModule = await require(module);
38+
} catch (e) {
39+
debug(`Unable to load ${module} from require; trying TASK_ROOT path.`, e);
40+
try {
41+
if (targetPath) {
42+
loadedModule = await require(`${targetPath}/${module}`);
43+
}
44+
} catch (err) {
45+
debug(`Unable to load ${module} from path ${targetPath}`, err);
46+
}
47+
}
48+
49+
return loadedModule;
50+
};
51+
52+
export default loadModuleForTracing;

src/plugins/https.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import pickBy from 'lodash.pickby';
99
import isArray from 'isarray';
1010
import { flatten } from 'flat';
1111

12-
const debug = debuglog('@iopipe/trace');
12+
const debug = debuglog('@iopipe:trace:https');
1313

1414
/*eslint-disable babel/no-invalid-this*/
1515

src/plugins/ioredis.js

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,27 @@
11
import { debuglog } from 'util';
22
import shimmer from 'shimmer';
3-
import Redis from 'ioredis';
43
import Perf from 'performance-node';
54
import uuid from 'uuid/v4';
5+
import loadModuleForTracing from '../loadHelper';
66

7-
const debug = debuglog('@iopipe/trace');
7+
const debug = debuglog('@iopipe:trace:ioredis');
8+
9+
let Redis, RedisTarget, RedisCmdTarget;
10+
11+
const loadModule = () =>
12+
loadModuleForTracing('ioredis')
13+
.then(module => {
14+
Redis = module;
15+
RedisTarget = Redis && Redis.prototype;
16+
if (Redis.Command && Redis.Command.prototype) {
17+
RedisCmdTarget = Redis.Command && Redis.Command.prototype;
18+
}
19+
return module;
20+
})
21+
.catch(e => {
22+
debug('Not loading ioredis', e);
23+
return false;
24+
});
825

926
/*eslint-disable babel/no-invalid-this*/
1027
/*eslint-disable func-name-matching */
@@ -28,23 +45,24 @@ const filterRequest = (command, context) => {
2845
};
2946
};
3047

31-
function wrap({ timeline, data = {} } = {}) {
48+
async function wrap({ timeline, data = {} } = {}) {
49+
await loadModule();
50+
if (!Redis) {
51+
debug('ioredis plugin not accessible from trace plugin. Skipping.');
52+
return false;
53+
}
3254
if (!(timeline instanceof Perf)) {
3355
debug(
3456
'Timeline passed to plugins/ioredis.wrap not an instance of performance-node. Skipping.'
3557
);
3658
return false;
3759
}
3860

39-
if (!Redis.__iopipeShimmer) {
61+
if (Redis && !Redis.__iopipeShimmer) {
4062
if (process.env.IOPIPE_TRACE_IOREDIS_INITPROMISE) {
41-
shimmer.wrap(
42-
Redis.Command && Redis.Command.prototype,
43-
'initPromise',
44-
wrapPromise
45-
);
63+
shimmer.wrap(RedisCmdTarget, 'initPromise', wrapPromise);
4664
}
47-
shimmer.wrap(Redis && Redis.prototype, 'sendCommand', wrapSendCommand);
65+
shimmer.wrap(RedisTarget, 'sendCommand', wrapSendCommand);
4866
Redis.__iopipeShimmer = true;
4967
}
5068

@@ -128,11 +146,18 @@ function wrap({ timeline, data = {} } = {}) {
128146
}
129147

130148
function unwrap() {
149+
if (!Redis) {
150+
debug(
151+
'ioredis plugin not accessible from trace plugin. Nothing to unwrap.'
152+
);
153+
return false;
154+
}
131155
if (process.env.IOPIPE_TRACE_IOREDIS_INITPROMISE) {
132-
shimmer.unwrap(Redis.Command && Redis.Command.prototype, 'initPromise');
156+
shimmer.unwrap(RedisCmdTarget, 'initPromise');
133157
}
134-
shimmer.unwrap(Redis && Redis.prototype, 'sendCommand');
158+
shimmer.unwrap(RedisTarget, 'sendCommand');
135159
delete Redis.__iopipeShimmer;
160+
return true;
136161
}
137162

138163
export { unwrap, wrap };

src/plugins/ioredis.test.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ xtest('Redis works as normal if wrap is not called', done => {
6464
});
6565
});
6666

67-
test('Bails if timeline is not instance of performance-node', () => {
68-
const bool = wrap({ timeline: [] });
67+
test('Bails if timeline is not instance of performance-node', async () => {
68+
const bool = await wrap({ timeline: [] });
6969
expect(bool).toBe(false);
7070
});
7171

@@ -128,7 +128,7 @@ xdescribe('Wrapping Redis', () => {
128128
db: 1
129129
});
130130

131-
expect(redis.sendCommand.__wrapped).toBeDefined();
131+
expect(redis.__wrapped).toBeDefined();
132132

133133
const expectedStr = 'wrapped ioredis, async/await';
134134

@@ -152,7 +152,7 @@ xdescribe('Wrapping Redis', () => {
152152

153153
redis = new Redis({ host: '127.0.0.1', connectionName: 'Test 2', db: 1 });
154154

155-
expect(redis.sendCommand.__wrapped).toBeDefined();
155+
expect(redis.__wrapped).toBeDefined();
156156

157157
const expectedStr = 'wrapped ioredis, promise syntax';
158158

@@ -185,7 +185,7 @@ xdescribe('Wrapping Redis', () => {
185185

186186
redis = new Redis({ host: 'localhost', connectionName: 'Test 3', db: 1 });
187187

188-
expect(redis.sendCommand.__wrapped).toBeDefined();
188+
expect(redis.__wrapped).toBeDefined();
189189

190190
const expectedStr = 'wrapped ioredis, callback syntax';
191191
redis.set('testString', expectedStr);

src/plugins/mongodb.js

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,42 @@
11
import { debuglog } from 'util';
22
import shimmer from 'shimmer';
3-
import { MongoClient, Server, Cursor, Collection } from 'mongodb';
43
import Perf from 'performance-node';
54
import uuid from 'uuid/v4';
65
import get from 'lodash/get';
6+
import loadModuleForTracing from '../loadHelper';
7+
8+
const debug = debuglog('@iopipe:trace:mongodb');
9+
10+
let MongoClient,
11+
Server,
12+
Cursor,
13+
Collection,
14+
clientTarget,
15+
collectionTarget,
16+
serverTarget,
17+
cursorTarget;
18+
19+
const loadModule = async () => {
20+
const mod = await loadModuleForTracing('mongodb')
21+
.then(module => {
22+
MongoClient = module.MongoClient;
23+
Server = module.Server;
24+
Cursor = module.Cursor;
25+
Collection = module.Collection;
26+
27+
clientTarget = MongoClient && MongoClient.prototype;
28+
collectionTarget = Collection && Collection.prototype;
29+
serverTarget = Server && Server.prototype;
30+
cursorTarget = Cursor && Cursor.prototype;
31+
32+
return module;
33+
})
34+
.catch(e => {
35+
debug('Not loading mongodb', e);
36+
return null;
37+
});
38+
return mod;
39+
};
740

841
const dbType = 'mongodb';
942
const serverOps = ['command', 'insert', 'update', 'remove'];
@@ -23,13 +56,6 @@ const collectionOps = [
2356
const cursorOps = ['next', 'filter', 'sort', 'hint', 'toArray'];
2457
const clientOps = ['connect', 'close', 'db'];
2558

26-
const clientTarget = MongoClient && MongoClient.prototype;
27-
const collectionTarget = Collection && Collection.prototype;
28-
const serverTarget = Server && Server.prototype;
29-
const cursorTarget = Cursor && Cursor.prototype;
30-
31-
const debug = debuglog('@iopipe/trace');
32-
3359
/*eslint-disable babel/no-invalid-this*/
3460
/*eslint-disable func-name-matching */
3561
/*eslint-disable prefer-rest-params */
@@ -146,7 +172,13 @@ const filterRequest = (params, context) => {
146172
};
147173
};
148174

149-
function wrap({ timeline, data = {} } = {}) {
175+
async function wrap({ timeline, data = {} } = {}) {
176+
await loadModule();
177+
178+
if (!clientTarget) {
179+
debug('mongodb plugin not accessible from trace plugin. Skipping.');
180+
return false;
181+
}
150182
if (!(timeline instanceof Perf)) {
151183
debug(
152184
'Timeline passed to plugins/mongodb.wrap not an instance of performance-node. Skipping.'
@@ -217,6 +249,13 @@ function wrap({ timeline, data = {} } = {}) {
217249
}
218250

219251
function unwrap() {
252+
if (!clientTarget) {
253+
debug(
254+
'mongodb plugin not accessible from trace plugin. Nothing to unwrap.'
255+
);
256+
return false;
257+
}
258+
220259
if (serverTarget.__iopipeShimmer) {
221260
shimmer.massUnwrap(serverTarget, serverOps);
222261
delete serverTarget.__iopipeShimmer;
@@ -233,6 +272,7 @@ function unwrap() {
233272
shimmer.massUnwrap(clientTarget, clientOps); // mass just seems to hang and not complete
234273
delete clientTarget.__iopipeShimmer;
235274
}
275+
return true;
236276
}
237277

238278
export { unwrap, wrap };

0 commit comments

Comments
 (0)