-
Notifications
You must be signed in to change notification settings - Fork 821
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(instrumentation): add support for esm module #2846
Conversation
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## main #2846 +/- ##
==========================================
+ Coverage 93.26% 93.67% +0.41%
==========================================
Files 247 242 -5
Lines 7346 7181 -165
Branches 1512 1477 -35
==========================================
- Hits 6851 6727 -124
+ Misses 495 454 -41 |
ee0d04d
to
48fce71
Compare
Why is it needed to mark |
Apparently for tests only but i didn't test it yet |
3caafdf
to
0801865
Compare
Added a simple test that patches a esm module and verify that we get the patched version, however making it work with our typescript tooling was pretty much a nightmare. Would like input of everyone to discuss if we ship it like this or we want to do more ? PS: Wil fix tests for older node version / changelog after settling above comment cc @open-telemetry/javascript-approvers |
experimental/packages/opentelemetry-instrumentation/src/platform/node/instrumentation.ts
Outdated
Show resolved
Hide resolved
0801865
to
629ef1d
Compare
I've fixed the tests (sorry for the bash hack, i didn't see another way to not run those tests for node < 12) and added a comment in the readme to explain that user need to use choose the iitm loader so we can hook into modules |
629ef1d
to
aac74b6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good job! I didn't expect this to require so few changes.
sorry for the bash hack, i didn't see another way to not run those tests for node < 12
I love a good bash hack(sarcasm), but if you don't, an less-bash-hacky alternative could be something like:
node -e 'process.exit(process.versions.node.split(".")[0] >= 12)' || echo "running it"
Q: Have you tried running any contrib package against this?
...tal/packages/opentelemetry-instrumentation/test/node/node_modules/my-esm-module/package.json
Outdated
Show resolved
Hide resolved
experimental/packages/opentelemetry-instrumentation/src/platform/node/instrumentation.ts
Outdated
Show resolved
Hide resolved
Well i believe most of our instrumentation target cjs module so i didn't try, do you have one in mind that have a esm bundle ?
I prefer your suggestion, i will update my PR |
b2654a5
to
d93cdb3
Compare
d93cdb3
to
c649189
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems fine to me. Overall less changes than I expected. Thanks for doing the legwork on this.
experimental/packages/opentelemetry-instrumentation/package.json
Outdated
Show resolved
Hide resolved
@@ -261,6 +261,10 @@ If nothing is specified the global registered provider is used. Usually this is | |||
There might be usecase where someone has the need for more providers within an application. Please note that special care must be takes in such setups | |||
to avoid leaking information from one provider to the other because there are a lot places where e.g. the global `ContextManager` or `Propagator` is used. | |||
|
|||
## ESM within Node.JS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should mark this explicitly an experimental feature and solicit feedback? Might be nice to know if this is actually working for people or if it is being used once its released.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dismissed my approval because we need to make sure a basic |
I assumed the new paths will also kick in if my application is esm, but not the library. Isn't that the case? |
c649189
to
4256872
Compare
All that activity over #1946 motivated me to take some time from my holiday to get back to this. Good news actually some of the issues i've had has been resolved by other PRs:
From my understanding, the new path for esm only kicks in when the ESM module loader is used so for ESM modules, you can find more information there: https://nodejs.org/dist/latest-v18.x/docs/api/packages.html#determining-module-system |
4437550
to
2c79d08
Compare
Thank you for taking time of your holiday working on this. Let me know, how I can help. I think that agitated is a popular ESM package that could be a test target? |
@@ -263,6 +263,11 @@ If nothing is specified the global registered provider is used. Usually this is | |||
There might be usecase where someone has the need for more providers within an application. Please note that special care must be takes in such setups | |||
to avoid leaking information from one provider to the other because there are a lot places where e.g. the global `ContextManager` or `Propagator` is used. | |||
|
|||
## Instrumentation for ESM Module In NodeJS (experimental) | |||
|
|||
As the module loading mechanism for ESM is different than CJS, you need to select a custom loader so instrumentation can load hook on the esm module it want to patch. To do so, you must provide the `--experimental-loader=import-in-the-middle/hook.mjs` flag to the `node` binary, alternatively you can set the `NODE_OPTIONS` environment variable to `--experimental-loader=import-in-the-middle/hook.mjs`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would recommend creating a wrapper for the hook so you can have a loader hook name that's clearly in the otel namespace rather than the unfamiliar import-in-the-middle naming. That's what we do at Datadog. We tell users to use dd-trace/loader-hook.js which just loads the IITM hook: https://github.com/DataDog/dd-trace-js/blob/master/loader-hook.mjs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion, i would like other opinion on this since this would result in using --experimental-loader=@opentelemetry-instrumentation/hook.mjs
instead. Also since the instrumentation is built for multiple platforms i'm not sure this would not create more problems as we support more (deno comes to my mind)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The hook could be a separate package. My suggestion is just to make the connection to otel clearer in some way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@open-telemetry/javascript-maintainers I would like in put on this, i'm not sure what's the best way to do this:
- have a dedicated package that re-export ittm
- exporting it from the instrumentation package but it need to be excluded from the browser build, however i'm not sure where to place the file in this case ? inside
/plaform/node
? at the root ? Since user would need to require it directly, that means we need it to be at the root ofbuild
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Between those options i'd rather have a dedicated package. Honestly though I think it's fine to just use RITM directly. It's just one more thing for us to build, maintain, and publish
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can guess that @Qard being at the other end by maintaining ITTM doesn't want to get questions about ESM instrumentation within otel but maybe i'm mistaken ? I agree that its more complicated to have one empty package on our end just for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
--experimental-loader=@opentelemetry-instrumentation/hook.mjs
looks good to me. I didn't find a new dedicated package necessary here. The hook entry files to be published will be rather small. Reducing the number of OTEL packages can alleviate the burden of setting up an OTEL application.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
--experimental-loader=@opentelemetry-instrumentation/hook.mjs
looks good to me. I didn't find a new dedicated package necessary here. The hook entry files to be published will be rather small. Reducing the number of OTEL packages can alleviate the burden of setting up an OTEL application.
Do you mean --experimental-loader=@opentelemetry/instrumentation/hook.mjs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My concern is more just users being confused about needing --experimental-loader=import-in-the-middle/hook.mjs
which is loading some other module than opentelemetry which they are likely unfamiliar with. Having an opentelemetry-specific proxy to it makes it clearer that this is a thing opentelemetry wants from the user. It also doesn't leave you tied to some specific external library you don't control. If at some point in the future IITM no longer does what you need or maintenance dies or whatever else, you can simply change the contents of your hook file without needing to ask all your users to update their CLI args. Changing CLI args sounds like it should be a trivial thing, but stuff like that often gets encoded deep in infra configs and places like that which creates unnecessary friction if changes need to be made in the future. It's just a better idea to have an entrypoint for that which you control.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also choose the way of having own wrapper for loading a non-core external library. If we were speaking about browser, it would probably make sense to keep the external dep as is to reduce the generated client's bundle size in case this lib is reused outside of opentelemetry as well. For node it's irrelevant, there are no cons of wrapping it
My simple unit test shows that you can mutate exports with the PR so yeah i think once merged it would be nice to have a esm-only instrumentation created |
I think the |
2c79d08
to
f6384aa
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still LGTM.
@@ -263,6 +263,11 @@ If nothing is specified the global registered provider is used. Usually this is | |||
There might be usecase where someone has the need for more providers within an application. Please note that special care must be takes in such setups | |||
to avoid leaking information from one provider to the other because there are a lot places where e.g. the global `ContextManager` or `Propagator` is used. | |||
|
|||
## Instrumentation for ESM Module In NodeJS (experimental) | |||
|
|||
As the module loading mechanism for ESM is different than CJS, you need to select a custom loader so instrumentation can load hook on the esm module it want to patch. To do so, you must provide the `--experimental-loader=import-in-the-middle/hook.mjs` flag to the `node` binary, alternatively you can set the `NODE_OPTIONS` environment variable to `--experimental-loader=import-in-the-middle/hook.mjs`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
--experimental-loader=@opentelemetry-instrumentation/hook.mjs
looks good to me. I didn't find a new dedicated package necessary here. The hook entry files to be published will be rather small. Reducing the number of OTEL packages can alleviate the burden of setting up an OTEL application.
I gave this a PR try on node v18.12.1. I know it's totally work in progress, but just in case it's helpful, I thought I'd post my results here. Short version: I'm not having much luck :/
I'm using esbuild to bundle my TypeScript app's source code, but am excluding all Despite the debug error logs above, I am not importing anything before loading the instrumentations as can be seen from the source of my bundled app:
I am seeing auto-instrumented traces in the debug logs of my app and in Jaeger for mysql and http, but I'm not seeing debug logs or traces for any of the other instrumentations. I recently switched the app from CommonJS to ESM due to several of my dependencies dropping support for CommonJS. I previously saw traces in Jaeger, and then (unsurprisingly) stopped seeing traces after switching my app to ESM. Anyway, thank you so much for working on this feature, and hopefully my experience is just user error and will work out of the box once this PR lands. |
I believe this show that my PR doesn't work when we try to patch CJS module from ESM (since most of instrumentation we are patching are still CJS). My test only does ESM loading ESM modules. I'm gonna try to reproduce when i've time but in the mean time:
Thanks for your help |
I branched and created an example app to try some things out using HTTP and Express instrumentation. I don't know if it was all necessary, but in a few places I updated the There are instructions in a README for running this app as CommonJS and as ESModules - it defaults to CommonJS. When I run as CommonJS, I see 5 total spans: 1 http, 3 express, 1 manual. When I run as ESM, I only see the manual span. I notice these in the console output for CommonJS and not ESM: @opentelemetry/instrumentation-http Applying patch for http@16.15.0
Applying patch for express@4.18.2 |
As discussed outside of this PR and in SIG meetings, here is the new PR for this work: #3698 |
Re-opening following #2640 + #2763
I'm marking this as draft since we have a semi problem with this: its really not easy to test this. Granted there wasn't a lot of test in place before that for require in the middle but i think it make sense to do it anyway.
I've looked around and if we want to run in esm mode and verify that we can correctly hook esm module, we need to mark the package itself as being ESM: mochajs/mocha-examples#47 (comment)
I'm pretty sure doing this will cause downstream problem for users so i don't think this is the way to go, do someone have an idea ?