Skip to content

Commit 64d358e

Browse files
JCMaispichlermarc
andauthored
fix: instrumentation of ESM-imported mongoose (#2793)
Co-authored-by: Marc Pichler <[email protected]>
1 parent 32f41ee commit 64d358e

File tree

3 files changed

+100
-8
lines changed

3 files changed

+100
-8
lines changed

plugins/node/instrumentation-mongoose/src/mongoose.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,12 @@ export class MongooseInstrumentation extends InstrumentationBase<MongooseInstrum
106106
return module;
107107
}
108108

109-
private patch(
110-
moduleExports: typeof mongoose,
111-
moduleVersion: string | undefined
112-
) {
109+
private patch(module: any, moduleVersion: string | undefined) {
110+
const moduleExports: typeof mongoose =
111+
module[Symbol.toStringTag] === 'Module'
112+
? module.default // ESM
113+
: module; // CommonJS
114+
113115
this._wrap(
114116
moduleExports.Model.prototype,
115117
'save',
@@ -154,10 +156,12 @@ export class MongooseInstrumentation extends InstrumentationBase<MongooseInstrum
154156
return moduleExports;
155157
}
156158

157-
private unpatch(
158-
moduleExports: typeof mongoose,
159-
moduleVersion: string | undefined
160-
): void {
159+
private unpatch(module: any, moduleVersion: string | undefined): void {
160+
const moduleExports: typeof mongoose =
161+
module[Symbol.toStringTag] === 'Module'
162+
? module.default // ESM
163+
: module; // CommonJS
164+
161165
const contextCaptureFunctions = getContextCaptureFunctions(moduleVersion);
162166

163167
this._unwrap(moduleExports.Model.prototype, 'save');
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
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+
* https://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+
// Use mongoose from an ES module:
18+
// node --experimental-loader=@opentelemetry/instrumentation/hook.mjs use-mongoose.mjs [MONGO_URL] [DB_NAME
19+
20+
import { trace } from '@opentelemetry/api';
21+
import { createTestNodeSdk } from '@opentelemetry/contrib-test-utils';
22+
23+
import { MongooseInstrumentation } from '../../build/src/index.js';
24+
25+
const sdk = createTestNodeSdk({
26+
serviceName: 'use-mongoose',
27+
instrumentations: [new MongooseInstrumentation()],
28+
});
29+
sdk.start();
30+
31+
import assert from 'assert';
32+
import mongoose from 'mongoose';
33+
34+
const MONGO_URL = process.argv[2] || '';
35+
const DB_NAME = process.argv[3] || '';
36+
37+
const randomId = ((Math.random() * 2 ** 32) >>> 0).toString(16);
38+
39+
await mongoose.connect(MONGO_URL, {
40+
dbName: DB_NAME,
41+
});
42+
43+
const TestModel = mongoose.model(
44+
'Test',
45+
new mongoose.Schema({
46+
name: String,
47+
})
48+
);
49+
50+
const tracer = trace.getTracer('mongoose-instrumentation');
51+
await tracer.startActiveSpan('manual', async span => {
52+
const name = `test-${randomId}`;
53+
const [createdDoc] = await TestModel.create([{ name }]);
54+
55+
const doc = await TestModel.findOne({ name }).exec();
56+
57+
assert(doc && createdDoc?._id.toString() === doc?._id.toString());
58+
59+
span.end();
60+
});
61+
62+
await mongoose.disconnect();
63+
await sdk.shutdown();

plugins/node/instrumentation-mongoose/test/mongoose-common.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
* limitations under the License.
1515
*/
1616
import 'mocha';
17+
import * as assert from 'assert';
1718
import { expect } from 'expect';
1819
import { context, ROOT_CONTEXT } from '@opentelemetry/api';
20+
import * as testUtils from '@opentelemetry/contrib-test-utils';
1921
import {
2022
SEMATTRS_DB_OPERATION,
2123
SEMATTRS_DB_STATEMENT,
@@ -548,4 +550,27 @@ describe('mongoose instrumentation [common]', () => {
548550
expect(spans.length).toBe(0);
549551
});
550552
});
553+
554+
it('should work with ESM usage', async () => {
555+
await testUtils.runTestFixture({
556+
cwd: __dirname,
557+
argv: ['fixtures/use-mongoose.mjs', MONGO_URI, DB_NAME],
558+
env: {
559+
NODE_OPTIONS:
560+
'--experimental-loader=@opentelemetry/instrumentation/hook.mjs',
561+
NODE_NO_WARNINGS: '1',
562+
},
563+
checkResult: (err, stdout, stderr) => {
564+
assert.ifError(err);
565+
},
566+
checkCollector: (collector: testUtils.TestCollector) => {
567+
const spans = collector.sortedSpans;
568+
assert.strictEqual(spans[0].name, 'manual');
569+
assert.strictEqual(spans[1].name, 'mongoose.Test.save');
570+
assert.strictEqual(spans[1].parentSpanId, spans[0].spanId);
571+
assert.strictEqual(spans[2].name, 'mongoose.Test.findOne');
572+
assert.strictEqual(spans[2].parentSpanId, spans[0].spanId);
573+
},
574+
});
575+
});
551576
});

0 commit comments

Comments
 (0)