Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/instrumentation-dataloader/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"devDependencies": {
"@opentelemetry/api": "^1.3.0",
"@opentelemetry/context-async-hooks": "^2.0.0",
"@opentelemetry/contrib-test-utils": "^0.49.0",
"@opentelemetry/sdk-trace-base": "^2.0.0",
"@opentelemetry/sdk-trace-node": "^2.0.0",
"@types/mocha": "10.0.10",
Expand Down
15 changes: 12 additions & 3 deletions packages/instrumentation-dataloader/src/instrumentation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ type PrimeFn = (typeof Dataloader.prototype)['prime'];
type ClearFn = (typeof Dataloader.prototype)['clear'];
type ClearAllFn = (typeof Dataloader.prototype)['clearAll'];

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function extractModuleExports(module: any) {
return module[Symbol.toStringTag] === 'Module'
? module.default // ESM
: module; // CommonJS
}

export class DataloaderInstrumentation extends InstrumentationBase<DataloaderInstrumentationConfig> {
constructor(config: DataloaderInstrumentationConfig = {}) {
super(PACKAGE_NAME, PACKAGE_VERSION, config);
Expand All @@ -54,7 +61,8 @@ export class DataloaderInstrumentation extends InstrumentationBase<DataloaderIns
new InstrumentationNodeModuleDefinition(
MODULE_NAME,
['>=2.0.0 <3'],
dataloader => {
dataloaderPkg => {
const dataloader = extractModuleExports(dataloaderPkg);
this._patchLoad(dataloader.prototype);
this._patchLoadMany(dataloader.prototype);
this._patchPrime(dataloader.prototype);
Expand All @@ -63,14 +71,15 @@ export class DataloaderInstrumentation extends InstrumentationBase<DataloaderIns

return this._getPatchedConstructor(dataloader);
},
dataloader => {
dataloaderPkg => {
const dataloader = extractModuleExports(dataloaderPkg);
['load', 'loadMany', 'prime', 'clear', 'clearAll'].forEach(method => {
if (isWrapped(dataloader.prototype[method])) {
this._unwrap(dataloader.prototype, method);
}
});
}
) as InstrumentationNodeModuleDefinition,
),
];
}

Expand Down
26 changes: 26 additions & 0 deletions packages/instrumentation-dataloader/test/dataloader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import {
import { context, SpanKind, SpanStatusCode, trace } from '@opentelemetry/api';
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks';
import {
runTestFixture,
TestCollector,
} from '@opentelemetry/contrib-test-utils';

import { DataloaderInstrumentation } from '../src';
const instrumentation = new DataloaderInstrumentation();
Expand Down Expand Up @@ -641,3 +645,25 @@ describe('DataloaderInstrumentation', () => {
assert.strictEqual(batchSpan.name, 'dataloader.batch');
});
});

describe('ESM usage', () => {
it('should work with ESM default import', async function () {
await runTestFixture({
cwd: __dirname,
argv: ['fixtures/use-dataloader-default-import.mjs'],
env: {
NODE_OPTIONS:
'--experimental-loader=@opentelemetry/instrumentation/hook.mjs',
NODE_NO_WARNINGS: '1',
},
checkCollector: (collector: TestCollector) => {
const spans = collector.sortedSpans;
assert.strictEqual(spans[0].name, 'manual');
assert.strictEqual(spans[1].name, 'dataloader.load');
assert.strictEqual(spans[1].parentSpanId, spans[0].spanId);
assert.strictEqual(spans[2].name, 'dataloader.batch');
assert.strictEqual(spans[2].parentSpanId, spans[1].spanId);
},
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// Use dataloader from an ES module:
// node --experimental-loader=@opentelemetry/instrumentation/hook.mjs use-dataloader-default-import.mjs

import { trace } from '@opentelemetry/api';
import { createTestNodeSdk } from '@opentelemetry/contrib-test-utils';
import * as crypto from 'crypto';

import { DataloaderInstrumentation } from '../../build/src/index.js';

const sdk = createTestNodeSdk({
serviceName: 'use-dataloader',
instrumentations: [
new DataloaderInstrumentation()
]
})
sdk.start();

import Dataloader from 'dataloader';

function getMd5HashFromIdx(idx ) {
return crypto.createHash('md5').update(String(idx)).digest('hex');
}
const dataloader = new Dataloader(
async keys =>
keys.map((_, idx) => {
return getMd5HashFromIdx(idx);
}),
{ cache: true }
);

const tracer = trace.getTracer();
await tracer.startActiveSpan('manual', async (span) => {
await dataloader.load(1);
span.end();
});

await sdk.shutdown();
Loading