fix(instrumentation-http): Ensure instrumentation of http.get and https.get work when used in ESM code#4866
Conversation
…https.get` work when used in ESM code The issue was that the `_wrap`ing of `http.get` was getting the just-wrapped `http.request` by accessing `moduleExports.request`. However, when wrapping an ES module the `moduleExports` object from IITM is a Proxy object that allows setting a property, but *not* re-getting that set property. The fix is to use the wrapped `http.request` from the `this._wrap` call. That required fixing a bug in the IITM code-path of `InstrumentationBase.prototype._wrap` to return the wrapped property. (The previous code was doing `return Object.defineProperty(...)`, which returns the moduleExports, not the defined property.) Fixes: open-telemetry#4857
experimental/packages/opentelemetry-instrumentation-http/test/integrations/esm.test.mjs
Fixed
Show fixed
Hide fixed
experimental/packages/opentelemetry-instrumentation-http/test/integrations/esm.test.mjs
Fixed
Show fixed
Hide fixed
| "test": "nyc mocha test/**/*.test.ts", | ||
| "test:cjs": "nyc mocha test/**/*.test.ts", | ||
| "test:esm": "nyc node --experimental-loader=@opentelemetry/instrumentation/hook.mjs ../../../node_modules/.bin/mocha 'test/**/*.test.mjs'", | ||
| "test": "npm run test:cjs && npm run test:esm", |
There was a problem hiding this comment.
Reviewer note: This test:esm script follows the same pattern used for ESM tests in opentelemetry-instrumentation.
| "tdd:browser": "karma start", | ||
| "test:cjs": "nyc mocha 'test/**/*.test.ts' --exclude 'test/browser/**/*.ts'", | ||
| "test:esm": "nyc node --experimental-loader=./hook.mjs ../../../node_modules/mocha/bin/mocha 'test/node/*.test.mjs' test/node/*.test.mjs", | ||
| "test:esm": "nyc node --experimental-loader=./hook.mjs ../../../node_modules/mocha/bin/mocha 'test/node/*.test.mjs'", |
There was a problem hiding this comment.
Reviewer note: I think this was a typo from when this was originally added. The additional test/node/*.test.mjs arg is redundant.
| Object.defineProperty(moduleExports, name, { | ||
| value: wrapped, | ||
| }); | ||
| return wrapped; |
There was a problem hiding this comment.
Reviewer note: From a quick check, I didn't see any core or contrib-repo instrumentations using the return value of this._wrap. Arguably this is a "fix" for @opentelemetry/instrumentation, but I didn't mention it in the CHANGELOG.
experimental/packages/opentelemetry-instrumentation/test/node/EsmInstrumentation.test.mjs
Show resolved
Hide resolved
maryliag
left a comment
There was a problem hiding this comment.
just a nit, otherwise LGTM
|
@trentm sorry I'm terrible with acronyms, what IITM means? |
My bad: It is import-in-the-middle (https://github.com/nodejs/import-in-the-middle). (And often RITM for require-in-the-middle.) |
JamieDanielson
left a comment
There was a problem hiding this comment.
This is great, thanks for working on this! Appreciate the test updates as well. I left a few nits around the tests but the functionality itself is good. I'm now wondering about other instrumentations and what can be done there... but for now 🚀
experimental/packages/opentelemetry-instrumentation-http/test/integrations/esm.test.mjs
Outdated
Show resolved
Hide resolved
experimental/packages/opentelemetry-instrumentation-http/test/integrations/esm.test.mjs
Outdated
Show resolved
Hide resolved
experimental/packages/opentelemetry-instrumentation-http/test/integrations/esm.test.mjs
Outdated
Show resolved
Hide resolved
experimental/packages/opentelemetry-instrumentation-http/test/integrations/esm.test.mjs
Outdated
Show resolved
Hide resolved
| moduleExports => { | ||
| this._wrap(moduleExports, 'testFunction', () => { | ||
| return () => 'patched'; | ||
| const wrapRetval = this._wrap(moduleExports, 'testFunction', () => { |
There was a problem hiding this comment.
| const wrapRetval = this._wrap(moduleExports, 'testFunction', () => { | |
| const wrappedReturnValue = this._wrap(moduleExports, 'testFunction', () => { |
This is a total nit, but I wonder if we could use a full name for this - primarily because this tends to be a confusing functionality and I'm a sucker for detailed variable names. Maybe something like patchedModule, similar to the patchedRequest you have in the http code? Nonblocking, especially since it's a test.
experimental/packages/opentelemetry-instrumentation-http/test/integrations/esm.test.mjs
Outdated
Show resolved
Hide resolved
|
Also I think this updates open-telemetry/opentelemetry-js-contrib#1942 now for http instrumentation? |
Co-authored-by: Jamie Danielson <jamieedanielson@gmail.com>
Co-authored-by: Jamie Danielson <jamieedanielson@gmail.com>
Co-authored-by: Jamie Danielson <jamieedanielson@gmail.com>
Co-authored-by: Jamie Danielson <jamieedanielson@gmail.com>
| const clientReq = https.request( | ||
| `https://127.0.0.1:${port}/https.request`, | ||
| { | ||
| rejectUnauthorized: false, |
Check failure
Code scanning / CodeQL
Disabling certificate validation
| https.get( | ||
| `https://127.0.0.1:${port}/https.get`, | ||
| { | ||
| rejectUnauthorized: false, |
Check failure
Code scanning / CodeQL
Disabling certificate validation
…https.get` work when used in ESM code (open-telemetry#4866) * fix(instrumentation-http): Ensure instrumentation of `http.get` and `https.get` work when used in ESM code The issue was that the `_wrap`ing of `http.get` was getting the just-wrapped `http.request` by accessing `moduleExports.request`. However, when wrapping an ES module the `moduleExports` object from IITM is a Proxy object that allows setting a property, but *not* re-getting that set property. The fix is to use the wrapped `http.request` from the `this._wrap` call. That required fixing a bug in the IITM code-path of `InstrumentationBase.prototype._wrap` to return the wrapped property. (The previous code was doing `return Object.defineProperty(...)`, which returns the moduleExports, not the defined property.) Fixes: open-telemetry#4857 * correct typo in the changelog message * does this fix the test:esm script running on windows? * remove other console.logs (presumably dev leftovers) from tests in this file * test name suggestion Co-authored-by: Jamie Danielson <jamieedanielson@gmail.com> * test name suggestion Co-authored-by: Jamie Danielson <jamieedanielson@gmail.com> * test name suggestion Co-authored-by: Jamie Danielson <jamieedanielson@gmail.com> * test name suggestion Co-authored-by: Jamie Danielson <jamieedanielson@gmail.com> * var naming suggestion: expand cres and creq, the abbrev isn't obvious --------- Co-authored-by: Jamie Danielson <jamieedanielson@gmail.com>
The issue was that the
_wraping ofhttp.getwas getting thejust-wrapped
http.requestby accessingmoduleExports.request.However, when wrapping an ES module the
moduleExportsobject from IITMis a Proxy object that allows setting a property, but not re-getting
that set property.
The fix is to use the wrapped
http.requestfrom thethis._wrapcall.That required fixing a bug in the IITM code-path of
InstrumentationBase.prototype._wrapto return the wrapped property.(The previous code was doing
return Object.defineProperty(...), whichreturns the moduleExports, not the defined property.)
Fixes: #4857