Skip to content

Commit 16979f6

Browse files
fix(instrumentation-dataloader): support ESM imports of dataloader module (#2973)
1 parent ef59c8d commit 16979f6

File tree

5 files changed

+94
-3
lines changed

5 files changed

+94
-3
lines changed

package-lock.json

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/instrumentation-dataloader/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"devDependencies": {
4949
"@opentelemetry/api": "^1.3.0",
5050
"@opentelemetry/context-async-hooks": "^2.0.0",
51+
"@opentelemetry/contrib-test-utils": "^0.49.0",
5152
"@opentelemetry/sdk-trace-base": "^2.0.0",
5253
"@opentelemetry/sdk-trace-node": "^2.0.0",
5354
"@types/mocha": "10.0.10",

packages/instrumentation-dataloader/src/instrumentation.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ type PrimeFn = (typeof Dataloader.prototype)['prime'];
4444
type ClearFn = (typeof Dataloader.prototype)['clear'];
4545
type ClearAllFn = (typeof Dataloader.prototype)['clearAll'];
4646

47+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
48+
function extractModuleExports(module: any) {
49+
return module[Symbol.toStringTag] === 'Module'
50+
? module.default // ESM
51+
: module; // CommonJS
52+
}
53+
4754
export class DataloaderInstrumentation extends InstrumentationBase<DataloaderInstrumentationConfig> {
4855
constructor(config: DataloaderInstrumentationConfig = {}) {
4956
super(PACKAGE_NAME, PACKAGE_VERSION, config);
@@ -54,7 +61,8 @@ export class DataloaderInstrumentation extends InstrumentationBase<DataloaderIns
5461
new InstrumentationNodeModuleDefinition(
5562
MODULE_NAME,
5663
['>=2.0.0 <3'],
57-
dataloader => {
64+
module => {
65+
const dataloader = extractModuleExports(module);
5866
this._patchLoad(dataloader.prototype);
5967
this._patchLoadMany(dataloader.prototype);
6068
this._patchPrime(dataloader.prototype);
@@ -63,14 +71,15 @@ export class DataloaderInstrumentation extends InstrumentationBase<DataloaderIns
6371

6472
return this._getPatchedConstructor(dataloader);
6573
},
66-
dataloader => {
74+
module => {
75+
const dataloader = extractModuleExports(module);
6776
['load', 'loadMany', 'prime', 'clear', 'clearAll'].forEach(method => {
6877
if (isWrapped(dataloader.prototype[method])) {
6978
this._unwrap(dataloader.prototype, method);
7079
}
7180
});
7281
}
73-
) as InstrumentationNodeModuleDefinition,
82+
),
7483
];
7584
}
7685

packages/instrumentation-dataloader/test/dataloader.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ import {
2121
import { context, SpanKind, SpanStatusCode, trace } from '@opentelemetry/api';
2222
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
2323
import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks';
24+
import {
25+
runTestFixture,
26+
TestCollector,
27+
} from '@opentelemetry/contrib-test-utils';
2428

2529
import { DataloaderInstrumentation } from '../src';
2630
const instrumentation = new DataloaderInstrumentation();
@@ -641,3 +645,25 @@ describe('DataloaderInstrumentation', () => {
641645
assert.strictEqual(batchSpan.name, 'dataloader.batch');
642646
});
643647
});
648+
649+
describe('ESM usage', () => {
650+
it('should work with ESM default import', async function () {
651+
await runTestFixture({
652+
cwd: __dirname,
653+
argv: ['fixtures/use-dataloader-default-import.mjs'],
654+
env: {
655+
NODE_OPTIONS:
656+
'--experimental-loader=@opentelemetry/instrumentation/hook.mjs',
657+
NODE_NO_WARNINGS: '1',
658+
},
659+
checkCollector: (collector: TestCollector) => {
660+
const spans = collector.sortedSpans;
661+
assert.strictEqual(spans[0].name, 'manual');
662+
assert.strictEqual(spans[1].name, 'dataloader.load');
663+
assert.strictEqual(spans[1].parentSpanId, spans[0].spanId);
664+
assert.strictEqual(spans[2].name, 'dataloader.batch');
665+
assert.strictEqual(spans[2].parentSpanId, spans[1].spanId);
666+
},
667+
});
668+
});
669+
});
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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 dataloader from an ES module:
18+
// node --experimental-loader=@opentelemetry/instrumentation/hook.mjs use-dataloader-default-import.mjs
19+
20+
import { trace } from '@opentelemetry/api';
21+
import { createTestNodeSdk } from '@opentelemetry/contrib-test-utils';
22+
import * as crypto from 'crypto';
23+
24+
import { DataloaderInstrumentation } from '../../build/src/index.js';
25+
26+
const sdk = createTestNodeSdk({
27+
serviceName: 'use-dataloader',
28+
instrumentations: [
29+
new DataloaderInstrumentation()
30+
]
31+
})
32+
sdk.start();
33+
34+
import Dataloader from 'dataloader';
35+
36+
function getMd5HashFromIdx(idx ) {
37+
return crypto.createHash('md5').update(String(idx)).digest('hex');
38+
}
39+
const dataloader = new Dataloader(
40+
async keys =>
41+
keys.map((_, idx) => {
42+
return getMd5HashFromIdx(idx);
43+
}),
44+
{ cache: true }
45+
);
46+
47+
const tracer = trace.getTracer();
48+
await tracer.startActiveSpan('manual', async (span) => {
49+
await dataloader.load(1);
50+
span.end();
51+
});
52+
53+
await sdk.shutdown();

0 commit comments

Comments
 (0)