-
Notifications
You must be signed in to change notification settings - Fork 810
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs: Describe support for ESM (#4876)
Co-authored-by: Trent Mick <[email protected]>
- Loading branch information
1 parent
2ca2459
commit 966ac17
Showing
5 changed files
with
170 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
# ECMAScript Modules vs. CommonJS | ||
|
||
Node.js uses a different module loader for ECMAScript Modules (ESM) vs. CommonJS (CJS). | ||
To verify whether your application is ESM or CJS, refer to [Node.js docs for Determining Module System](https://nodejs.org/api/packages.html#determining-module-system). | ||
|
||
An `.mjs` extension or `type:module` in the built app's `package.json` indicates the app is ESM. | ||
|
||
**Much of OpenTelemetry JS documentation is written assuming the compiled application is run as CJS.** | ||
ESM support is ongoing; a few adjustments are needed for configuration and startup commands. | ||
|
||
For more explanation about CJS and ESM, see the [Node.js docs](https://nodejs.org/api/modules.html#enabling). | ||
|
||
## TypeScript | ||
|
||
Many TypeScript projects today are written using ESM syntax, regardless of how they are compiled. | ||
In the `tsconfig.json`, there is an option to compile to ESM or CJS. | ||
If the compiled code is ESM, those import statements will remain the same (e.g. `import { foo } from 'bar';`). | ||
If the compiled code is CJS, those import statements will become `require()` statements (e.g. `const { foo } = require('bar');`) | ||
|
||
## Initializing the SDK | ||
|
||
Instrumentation setup and configuration must be run before your application code. | ||
If the SDK is initialized in a separate file (recommended), ensure it is imported first in application startup, or use the `--require` or `--import` flag during startup to preload the module. | ||
|
||
For CJS, the `NODE_OPTIONS` for the startup command should include `--require ./telemetry.js`. | ||
|
||
For ESM, minimum Node.js version of `18.19.0` is required. | ||
The `NODE_OPTIONS` for the startup command should include `--import ./telemetry.js`. | ||
|
||
## Instrumentation Hook Required for ESM | ||
|
||
If your application is written in JavaScript as ESM, or compiled to ESM from TypeScript, then a loader hook is required to properly patch instrumentation. | ||
The custom hook for ESM instrumentation is `--experimental-loader=@opentelemetry/instrumentation/hook.mjs`. | ||
This flag must be passed to the `node` binary, which is often done as a startup command and/or in the `NODE_OPTIONS` environment variable. | ||
|
||
### Additional Notes on Experimental Loaders | ||
|
||
Though the OpenTelemetry loader currently relies on `import-in-the-middle`, direct usage of `import-in-the-middle/hook.mjs` may cease to work in the future. | ||
The only currently supported loader hook is `@opentelemetry/instrumentation/hook.mjs`. | ||
|
||
**Note:** Eventually the recommendation for how to setup OpenTelemetry for usage with ESM will change to no longer require `--experimental-loader=@opentelemetry/instrumentation/hook.mjs`. | ||
Instead the bootstrap code (in `./telemetry.js`) will use Node.js's newer `module.register(...)`. | ||
Refer to this [issue](https://github.com/open-telemetry/opentelemetry-js/issues/4933) for details. | ||
|
||
Because of ongoing issues with loaders running TypeScript code as ESM in development environments, results may vary. | ||
|
||
To use `ts-node` to run the uncompiled TypeScript code, the module must be CJS. | ||
To use `tsx` to run the uncompiled TypeScript code as ESM, the `--import` flag must be used. | ||
|
||
## Using the Zero Code Option with `auto-instrumentations-node` | ||
|
||
The `auto-instrumentations-node` package contains a `register` entry-point that can be used with `--require` or `--import` to setup and start the SDK easily, without application code changes. | ||
For ESM, the package also requires the usage of the loader hook. | ||
|
||
Startup command for CJS: | ||
|
||
```sh | ||
node --require @opentelemetry/auto-instrumentations-node/register app.js | ||
``` | ||
|
||
Startup command for ESM: | ||
|
||
```sh | ||
node --experimental-loader=@opentelemetry/instrumentation/hook.mjs --import @opentelemetry/auto-instrumentations-node/register app.js | ||
``` | ||
|
||
## Examples | ||
|
||
### Example Written in JavaScript as CJS | ||
|
||
```javascript | ||
/*telemetry.cjs*/ | ||
const { NodeSDK } = require('@opentelemetry/sdk-node'); | ||
const { ConsoleSpanExporter } = require('@opentelemetry/sdk-trace-node'); | ||
const { | ||
getNodeAutoInstrumentations, | ||
} = require('@opentelemetry/auto-instrumentations-node'); | ||
|
||
const sdk = new NodeSDK({ | ||
traceExporter: new ConsoleSpanExporter(), | ||
instrumentations: [getNodeAutoInstrumentations()], | ||
}); | ||
|
||
sdk.start(); | ||
``` | ||
|
||
Startup command: | ||
|
||
```sh | ||
node --require ./telemetry.cjs app.js | ||
``` | ||
|
||
### Example Written in JavaScript as ESM or TypeScript | ||
|
||
```typescript | ||
/*telemetry.ts | telemetry.mjs*/ | ||
import { NodeSDK } from '@opentelemetry/sdk-node'; | ||
import { ConsoleSpanExporter } from '@opentelemetry/sdk-trace-node'; | ||
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; | ||
|
||
const sdk = new NodeSDK({ | ||
traceExporter: new ConsoleSpanExporter(), | ||
instrumentations: [getNodeAutoInstrumentations()], | ||
}); | ||
|
||
sdk.start(); | ||
``` | ||
|
||
Startup command for compiled CJS: | ||
|
||
```sh | ||
node --require ./telemetry.js app.js | ||
``` | ||
|
||
Startup command for compiled ESM: | ||
|
||
```sh | ||
node --experimental-loader=@opentelemetry/instrumentation/hook.mjs --import ./telemetry.js app.js | ||
``` | ||
|
||
### ESM Options for Different Versions of Node.js | ||
|
||
The entire startup command should include the following `NODE_OPTIONS`: | ||
|
||
| Node.js Version | NODE_OPTIONS | | ||
| ----------------- | ----------------------------------------------------------------------------------------- | | ||
| 16.x | `--require ./telemetry.cjs --experimental-loader=@opentelemetry/instrumentation/hook.mjs` | | ||
| >=18.1.0 <18.19.0 | `--require ./telemetry.cjs --experimental-loader=@opentelemetry/instrumentation/hook.mjs` | | ||
| ^18.19.0 | `--import ./telemetry.mjs --experimental-loader=@opentelemetry/instrumentation/hook.mjs` | | ||
| 20.x | `--import ./telemetry.mjs --experimental-loader=@opentelemetry/instrumentation/hook.mjs` | | ||
| 22.x | `--import ./telemetry.mjs --experimental-loader=@opentelemetry/instrumentation/hook.mjs` | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters