Skip to content
This repository was archived by the owner on Jan 21, 2026. It is now read-only.

Commit deb2a44

Browse files
authored
fix: support tracing awaited mongoose queries (#1007)
1 parent a60b623 commit deb2a44

5 files changed

Lines changed: 133 additions & 2 deletions

File tree

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"@types/knex": "^0.15.1",
6161
"@types/methods": "^1.1.0",
6262
"@types/mocha": "^5.2.5",
63+
"@types/mongoose": "^5.3.26",
6364
"@types/ncp": "^2.0.1",
6465
"@types/nock": "^10.0.0",
6566
"@types/node": "~10.7.2",
@@ -81,6 +82,7 @@
8182
"intelli-espower-loader": "^1.0.1",
8283
"js-green-licenses": "^0.5.0",
8384
"jshint": "^2.9.1",
85+
"linkinator": "^1.1.2",
8486
"mocha": "^6.0.0",
8587
"ncp": "^2.0.0",
8688
"nock": "^10.0.0",
@@ -95,8 +97,7 @@
9597
"timekeeper": "^2.0.0",
9698
"tmp": "0.1.0",
9799
"ts-node": "^8.0.0",
98-
"typescript": "~3.4.0",
99-
"linkinator": "^1.1.2"
100+
"typescript": "~3.4.0"
100101
},
101102
"dependencies": {
102103
"@google-cloud/common": "^0.32.1",

src/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ export const defaultConfig = {
265265
'http2': path.join(pluginDirectory, 'plugin-http2.js'),
266266
'koa': path.join(pluginDirectory, 'plugin-koa.js'),
267267
'mongodb-core': path.join(pluginDirectory, 'plugin-mongodb-core.js'),
268+
'mongoose': path.join(pluginDirectory, 'plugin-mongoose.js'),
268269
'mysql': path.join(pluginDirectory, 'plugin-mysql.js'),
269270
'mysql2': path.join(pluginDirectory, 'plugin-mysql2.js'),
270271
'pg': path.join(pluginDirectory, 'plugin-pg.js'),

src/plugins/plugin-mongoose.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Copyright 2019 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import * as mongooseTypes from 'mongoose';
18+
import { PluginTypes } from '..';
19+
20+
const plugin: PluginTypes.Plugin = [
21+
{
22+
versions: '4 - 5',
23+
file: 'lib/query.js',
24+
intercept: (Query: typeof mongooseTypes.Query, api) => {
25+
// Assume that the context desired at Query execution time should be the
26+
// context where the Query object was constructed. In most (if not all)
27+
// Mongoose read APIs, both of these appear to happen as part of the same
28+
// API call.
29+
return new Proxy(Query, {
30+
apply(target, thisArg, args) {
31+
// result is expected to be undefined.
32+
const result = target.apply(thisArg, args);
33+
thisArg.exec = api.wrap(thisArg.exec);
34+
return result;
35+
}
36+
});
37+
}
38+
}
39+
];
40+
41+
export = plugin;
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* Copyright 2019 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import * as assert from 'assert';
18+
import * as mongooseTypes from 'mongoose';
19+
20+
import * as traceTestModule from '../trace';
21+
import {describeInterop} from '../utils';
22+
23+
describeInterop<typeof mongooseTypes>('mongoose', (fixture) => {
24+
let mongoose: typeof mongooseTypes;
25+
// Simple will be treated as a class constructor.
26+
// tslint:disable-next-line:variable-name
27+
let Simple: mongooseTypes.Model<mongooseTypes.Document>;
28+
29+
/**
30+
* Common logic used in multiple tests -- inserts an object into the database.
31+
* @param doc
32+
*/
33+
async function insertTestData(doc: {f1: string, f2: boolean, f3: number}) {
34+
const data = new Simple(doc);
35+
const tracer = traceTestModule.get();
36+
await tracer.runInRootSpan({name: 'insert-test-data'}, async (span) => {
37+
assert.ok(tracer.isRealSpan(span));
38+
await data.save();
39+
span.endSpan();
40+
});
41+
}
42+
43+
before(async () => {
44+
traceTestModule.setCLSForTest();
45+
traceTestModule.setPluginLoaderForTest();
46+
traceTestModule.start();
47+
mongoose = fixture.require();
48+
await mongoose.connect('mongodb://localhost:27017/testdb');
49+
50+
const {Schema} = mongoose;
51+
const simpleSchema = new Schema({f1: String, f2: Boolean, f3: Number});
52+
Simple = mongoose.model('Simple', simpleSchema);
53+
});
54+
55+
after(async () => {
56+
traceTestModule.setCLSForTest(traceTestModule.TestCLS);
57+
traceTestModule.setPluginLoaderForTest(traceTestModule.TestPluginLoader);
58+
await mongoose.connection.db.dropDatabase();
59+
await mongoose.disconnect();
60+
});
61+
62+
afterEach(() => {
63+
traceTestModule.clearTraceData();
64+
});
65+
66+
it('Traces creates with async/await', async () => {
67+
await insertTestData({f1: 'val', f2: false, f3: 1729});
68+
const trace = traceTestModule.getOneTrace(
69+
trace => trace.spans.some(span => span.name === 'insert-test-data'));
70+
assert.strictEqual(trace.spans.length, 2);
71+
assert.strictEqual(trace.spans[1].name, 'mongo-insert');
72+
});
73+
74+
it('Traces queries with async/await', async () => {
75+
await insertTestData({f1: 'sim', f2: false, f3: 1729});
76+
const tracer = traceTestModule.get();
77+
await tracer.runInRootSpan({name: 'query-test-data'}, async (span) => {
78+
assert.ok(tracer.isRealSpan(span));
79+
await Simple.findOne({f1: 'sim'});
80+
span.endSpan();
81+
});
82+
const trace = traceTestModule.getOneTrace(
83+
trace => trace.spans.some(span => span.name === 'query-test-data'));
84+
assert.strictEqual(trace.spans.length, 2);
85+
assert.strictEqual(trace.spans[1].name, 'mongo-cursor');
86+
});
87+
});

tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"test/plugins/test-trace-http.ts",
2626
"test/plugins/test-trace-http2.ts",
2727
"test/plugins/test-trace-knex.ts",
28+
"test/plugins/test-trace-mongoose-async-await.ts",
2829
"test/logger.ts",
2930
"test/nocks.ts",
3031
"test/test-cls.ts",

0 commit comments

Comments
 (0)