opentelemetry-js: Auto-instrumentation doesn't work when using Yarn 2 with PnP
What version of OpenTelemetry are you using?
"@opentelemetry/api": "^0.10.2",
"@opentelemetry/exporter-collector": "^0.10.2",
"@opentelemetry/exporter-jaeger": "^0.10.2",
"@opentelemetry/node": "^0.10.2",
"@opentelemetry/plugin-grpc": "^0.10.2",
"@opentelemetry/plugin-http": "^0.10.2",
"@opentelemetry/plugin-https": "^0.10.2",
"@opentelemetry/tracing": "^0.10.2",
What version of Node are you using?
v14.7.0
What did you do?
When using Yarn 2 with PnP enabled (which is on by default), the plugin-based auto-instrumentation doesn’t seem to work.
What did you expect to see?
It should work as when not using PnP (i.e. node_modules
being present).
What did you see instead?
PluginLoader#load: trying to load http@14.7.0
PluginLoader#load: could not load plugin @opentelemetry/plugin-http of module http. Error: @opentelemetry/node tried to access @opentelemetry/plugin-http, but it isn't declared in its dependencies; this makes the require call ambiguous and unsound.
Required package: @opentelemetry/plugin-http (via "@opentelemetry/plugin-http")
Required by: @opentelemetry/node@npm:0.10.2 (via /home/schickling/code/my-project/.yarn/cache/@opentelemetry-node-npm-0.10.2-4554cb7ee1-d2e66e9307.zip/node_modules/@opentelemetry/node/build/src/instrumentation/)
Require stack:
- /home/schickling/code/my-project/.yarn/cache/@opentelemetry-node-npm-0.10.2-4554cb7ee1-d2e66e9307.zip/node_modules/@opentelemetry/node/build/src/instrumentation/PluginLoader.js
- /home/schickling/code/my-project/.yarn/cache/@opentelemetry-node-npm-0.10.2-4554cb7ee1-d2e66e9307.zip/node_modules/@opentelemetry/node/build/src/NodeTracerProvider.js
- /home/schickling/code/my-project/.yarn/cache/@opentelemetry-node-npm-0.10.2-4554cb7ee1-d2e66e9307.zip/node_modules/@opentelemetry/node/build/src/index.js
- /home/schickling/code/my-project/packages/my-project-service/src/index.ts
PluginLoader#load: trying to load https@14.7.0
PluginLoader#load: could not load plugin @opentelemetry/plugin-https of module https. Error: @opentelemetry/node tried to access @opentelemetry/plugin-https, but it isn't declared in its dependencies; this makes the require call ambiguous and unsound.
Required package: @opentelemetry/plugin-https (via "@opentelemetry/plugin-https")
Required by: @opentelemetry/node@npm:0.10.2 (via /home/schickling/code/my-project/.yarn/cache/@opentelemetry-node-npm-0.10.2-4554cb7ee1-d2e66e9307.zip/node_modules/@opentelemetry/node/build/src/instrumentation/)
Require stack:
- /home/schickling/code/my-project/.yarn/cache/@opentelemetry-node-npm-0.10.2-4554cb7ee1-d2e66e9307.zip/node_modules/@opentelemetry/node/build/src/instrumentation/PluginLoader.js
- /home/schickling/code/my-project/.yarn/cache/@opentelemetry-node-npm-0.10.2-4554cb7ee1-d2e66e9307.zip/node_modules/@opentelemetry/node/build/src/NodeTracerProvider.js
- /home/schickling/code/my-project/.yarn/cache/@opentelemetry-node-npm-0.10.2-4554cb7ee1-d2e66e9307.zip/node_modules/@opentelemetry/node/build/src/index.js
- /home/schickling/code/my-project/packages/my-project-service/src/index.ts
About this issue
- Original URL
- State: open
- Created 4 years ago
- Reactions: 2
- Comments: 18 (10 by maintainers)
Just fyi, this isn’t a PnP issue. It’s a Node issue that PnP highlights more than node_modules can do. The problem is that because you’re calling
require
on something that isn’t listed as a dependency, you’re subject to the hoisting whims. For example, take the following case. There’s nothing special, just two incompatible telemetry versions used in two different deps:Package managers will tend to hoist them to the top. Now, there’s no rule around hoisting, except that the dependency contract must be respected (ie a package doing
require
on one of its listed dependency must obtain a version compatible with the range it requested). Because you didn’t list explicit relationships, the following is possible:When opentelemetry@2 will make the require call to retrieve its http plugin, due to the hoisting, it will get the v1 from this plugin, not the v2. The hoisting is invalid, but because of the lack of explicit relationship, package managers don’t have the information needed to prevent it.
In non-plugin scenarios, you’d list those dependencies as (optional?) peer dependencies. This would be enough to fix the problem, as the additional constraint would prevent package managers from generating hoisting like the above. In your case however, you likely don’t know what plugins will be used - after all, maybe your users will develop their own!
In this case, I’d suggest to do the same thing as Webpack, Babel, ESLint, …: load your plugins on behalf of someone else. For example, if your require call is like this:
Instead require it using this:
Or, another option:
In both cases, the resolution is done on behalf of the package that actually lists the plugin as dependencies. How you know its path is up to you, but it can come from anything: the configuration file path (Babel, ESLint), a basedir parameter in the initializer (Webpack), … it’s the most flexible option.
Hi there, did any one succeed yet with Instrumentation using yarn 2 (with PnP) and webpack? I could only see modules like dns, tcp or http working out of the box. Modules like express, graphql or mongodb are not working.
Do I need to externalize some of the deps?
It seems like dependencies should be declared in the packages that have the dependencies. If consumers of a package are left to figure out which packages (and versions) to puzzle together, you’re kind of missing the target.
By the way you can workaround this by adding overrides to
.yarnrc.yml
We don’t have any overrides for opentelemetry in our
.yarnrc.yaml
any more, I think they added the necessarypeerDependencies
at some point.Not sure about that error, it looks like a tricky one to debug. I guess you could look and see if that file exists in the package zip file, or configure yarn to unzip that module and see if it makes a difference. Some debugging to do there, more than what I can help with from here.
Hi @dyladan, any update on this one?
We’re having the exact same problem (with webpack), auto-instrumentation doesn’t work.
I prefer to build the lower layers as explicitly as possible, then build the “magic” (like auto-detecting/auto-installing plugins) into wrappers around it. That way you can always drop down to the explicit version in the case that you have some unexpected failure.
If we’re going to make this change, it needs to be done before GA in my opinion as changing the plugin interface to allow manual plugin installation would be quite the breaking change.
Thanks for this additional context. Makes sense.
I’ve run into similar problems when using bundlers (e.g.
esbuild
orwebpack
). I think this is a common enough scenario/pattern in modern applications that it’s worth considering the change you’ve outlined.