diff --git a/CHANGELOG.md b/CHANGELOG.md index cb3b53e4d6..ad98094bdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/ ## Unreleased * feat: collect host id for non-cloud environments [#3575](https://github.com/open-telemetry/opentelemetry-js/pull/3575) @mwear +* feat: added env variable to require only specific libraries [#3666](https://github.com/open-telemetry/opentelemetry-js/pull/3666) @kirrg001 ### :boom: Breaking Change diff --git a/experimental/packages/opentelemetry-instrumentation/src/platform/node/RequireInTheMiddleSingleton.ts b/experimental/packages/opentelemetry-instrumentation/src/platform/node/RequireInTheMiddleSingleton.ts index 0bcea53ea8..487dc083e4 100644 --- a/experimental/packages/opentelemetry-instrumentation/src/platform/node/RequireInTheMiddleSingleton.ts +++ b/experimental/packages/opentelemetry-instrumentation/src/platform/node/RequireInTheMiddleSingleton.ts @@ -51,6 +51,7 @@ const isMocha = [ * on the performance of instrumentation hooks being applied. */ export class RequireInTheMiddleSingleton { + private hook?: RequireInTheMiddle.Hooked; private _moduleNameTrie: ModuleNameTrie = new ModuleNameTrie(); private static _instance?: RequireInTheMiddleSingleton; @@ -59,9 +60,15 @@ export class RequireInTheMiddleSingleton { } private _initialize() { - RequireInTheMiddle( - // Intercept all `require` calls; we will filter the matching ones below - null, + // A list of strings separated by comma e.g. fastify,fs + const modules = process.env.OTEL_REQUIRE_ONLY + ? process.env.OTEL_REQUIRE_ONLY.split(',').map(i => i.trim()) + : null; + + this.hook = RequireInTheMiddle( + // Intercept all `require` calls; we will filter the matching ones below. + // Alternatively you can set an env variable to only listen for specific libs. + modules, { internals: true }, (exports, name, basedir) => { // For internal files on Windows, `name` will use backslash as the path separator @@ -84,6 +91,15 @@ export class RequireInTheMiddleSingleton { ); } + /** + * Deregister require-in-the-middle hook. + * @returns + */ + unhook() { + if (!this.hook) return; + this.hook.unhook(); + } + /** * Register a hook with `require-in-the-middle` * diff --git a/experimental/packages/opentelemetry-instrumentation/test/node/RequireInTheMiddleSingleton.test.ts b/experimental/packages/opentelemetry-instrumentation/test/node/RequireInTheMiddleSingleton.test.ts index df7059987c..2c171297f3 100644 --- a/experimental/packages/opentelemetry-instrumentation/test/node/RequireInTheMiddleSingleton.test.ts +++ b/experimental/packages/opentelemetry-instrumentation/test/node/RequireInTheMiddleSingleton.test.ts @@ -20,8 +20,6 @@ import * as path from 'path'; import * as RequireInTheMiddle from 'require-in-the-middle'; import { RequireInTheMiddleSingleton } from '../../src/platform/node/RequireInTheMiddleSingleton'; -const requireInTheMiddleSingleton = RequireInTheMiddleSingleton.getInstance(); - type AugmentedExports = { __ritmOnRequires?: string[]; }; @@ -34,15 +32,25 @@ const makeOnRequiresStub = (label: string): sinon.SinonStub => }) as RequireInTheMiddle.OnRequireFn); describe('RequireInTheMiddleSingleton', () => { - describe('register', () => { - const onRequireFsStub = makeOnRequiresStub('fs'); - const onRequireFsPromisesStub = makeOnRequiresStub('fs-promises'); - const onRequireCodecovStub = makeOnRequiresStub('codecov'); - const onRequireCodecovLibStub = makeOnRequiresStub('codecov-lib'); - const onRequireCpxStub = makeOnRequiresStub('cpx'); - const onRequireCpxLibStub = makeOnRequiresStub('cpx-lib'); + describe('default', () => { + let requireInTheMiddleSingleton: RequireInTheMiddleSingleton; + let onRequireFsStub: sinon.SinonStub; + let onRequireFsPromisesStub: sinon.SinonStub; + let onRequireCodecovStub: sinon.SinonStub; + let onRequireCodecovLibStub: sinon.SinonStub; + let onRequireCpxStub: sinon.SinonStub; + let onRequireCpxLibStub: sinon.SinonStub; before(() => { + onRequireFsStub = makeOnRequiresStub('fs'); + onRequireFsPromisesStub = makeOnRequiresStub('fs-promises'); + onRequireCodecovStub = makeOnRequiresStub('codecov'); + onRequireCodecovLibStub = makeOnRequiresStub('codecov-lib'); + onRequireCpxStub = makeOnRequiresStub('cpx'); + onRequireCpxLibStub = makeOnRequiresStub('cpx-lib'); + + requireInTheMiddleSingleton = RequireInTheMiddleSingleton.getInstance(); + requireInTheMiddleSingleton.register('fs', onRequireFsStub); requireInTheMiddleSingleton.register( 'fs/promises', @@ -69,6 +77,10 @@ describe('RequireInTheMiddleSingleton', () => { onRequireCpxLibStub.resetHistory(); }); + after(() => { + requireInTheMiddleSingleton.unhook(); + }); + it('should return a hooked object', () => { const moduleName = 'm'; const onRequire = makeOnRequiresStub('m'); @@ -179,4 +191,71 @@ describe('RequireInTheMiddleSingleton', () => { }); }); }); + + describe('with OTEL_REQUIRE_ONLY set', () => { + let requireInTheMiddleSingleton: RequireInTheMiddleSingleton; + let onRequireFsStub: sinon.SinonStub; + let onRequireCodecovStub: sinon.SinonStub; + + describe('single module', function () { + before(() => { + onRequireFsStub = makeOnRequiresStub('fs'); + onRequireCodecovStub = makeOnRequiresStub('codecov'); + + process.env.OTEL_REQUIRE_ONLY = 'fs'; + requireInTheMiddleSingleton = RequireInTheMiddleSingleton.getInstance(); + + requireInTheMiddleSingleton.register('fs', onRequireFsStub); + requireInTheMiddleSingleton.register('codecov', onRequireCodecovStub); + }); + + beforeEach(() => { + onRequireFsStub.resetHistory(); + onRequireCodecovStub.resetHistory(); + }); + + after(() => { + delete process.env.OTEL_REQUIRE_ONLY; + requireInTheMiddleSingleton.unhook(); + }); + + it('should call `onRequire` for fs', () => { + require('fs'); + require('codecov'); + + assert.equal(onRequireFsStub.called, true); + assert.equal(onRequireCodecovStub.called, false); + }); + }); + + describe('multiple modules', function () { + before(() => { + onRequireFsStub = makeOnRequiresStub('fs'); + onRequireCodecovStub = makeOnRequiresStub('codecov'); + + process.env.OTEL_REQUIRE_ONLY = 'fs,codecov'; + requireInTheMiddleSingleton = RequireInTheMiddleSingleton.getInstance(); + + requireInTheMiddleSingleton.register('fs', onRequireFsStub); + requireInTheMiddleSingleton.register('codecov', onRequireCodecovStub); + }); + + beforeEach(() => { + onRequireFsStub.resetHistory(); + onRequireCodecovStub.resetHistory(); + }); + + after(() => { + delete process.env.OTEL_REQUIRE_ONLY; + }); + + it('should call `onRequire` for codecov & fs', () => { + require('fs'); + require('codecov'); + + assert.equal(onRequireFsStub.called, true); + assert.equal(onRequireCodecovStub.called, true); + }); + }); + }); });