* chore: track package-lock.json (#4238) * chore: track package-lock.json * Pin to old versions for node 14 * Use version range * Remove unused cached directories * Temporarily disable other tests * Temporarily enable only api test * Enable only some packages * Test only api packages * Test trace exporters * Fix line ordering * Test all packages except otlp exporters * Add trace http exporter * Add trace proto exporter * Test all but grpc exporters * chore: use npm workspaces and degrade lerna to v6 * chore: get rid of lerna bootstrap * chore: use npx * chore: allow install scripts to setup buf * chore: fix w3c-integration-test cache key * chore: fix cache key * chore: disable resource compat test * chore: fix node_modules assumptions * chore: fix hoisted karma issue * chore: fix markdown linter complaints * chore: lock @grpc/grpc-js to v1.8.21 * Break caches * chore: remove cache * chore: fixup inline commands --------- Co-authored-by: Daniel Dyla <dyladan@users.noreply.github.com> * docs: fixed link to benchmark results (#4233) Co-authored-by: Chengzhong Wu <legendecas@gmail.com> * chore(deps): update all patch versions (#4215) * fix: otlp json encoding (#4220) Co-authored-by: Marc Pichler <marc.pichler@dynatrace.com> * fix: remove duplicate export star from version.ts (#4225) Co-authored-by: Marc Pichler <marc.pichler@dynatrace.com> * docs: fix sdk-node config instructions (#4249) Co-authored-by: Marc Pichler <marc.pichler@dynatrace.com> * feat(api): publish api esnext target (#4231) * chore: release API 1.7.0/Core 1.18.0/Experimental 0.45.0 (#4254) * fix(sdk-metrics): hand-roll MetricAdvice type as older API versions do not include it (#4260) * chore: prepare release 1.18.1/0.45.1 (#4261) * chore: no need for 'packages' in "lerna.json" (#4264) * Benchmark tests for trace OTLP transform and BatchSpanProcessor (#4218) Co-authored-by: Marc Pichler <marc.pichler@dynatrace.com> * chore: type reference on zone.js (#4257) Co-authored-by: Marc Pichler <marc.pichler@dynatrace.com> * docs: add docker-compose to run prometheus for the experimental example (#4268) Co-authored-by: Marc Pichler <marc.pichler@dynatrace.com> * fix(sdk-logs): avoid map attribute set when count limit exceeded (#4195) Co-authored-by: Marc Pichler <marc.pichler@dynatrace.com> * chore(deps): update dependency chromedriver to v119 [security] (#4280) * chore(deps): update actions/setup-node action to v4 (#4236) * fix(sdk-trace-base): processor onStart called with a span having empty attributes (#4277) Co-authored-by: artahmetaj <artahmetaj@yahoo.com> * Update fetch instrumentation to be runtime agnostic (#4063) Co-authored-by: Marc Pichler <marc.pichler@dynatrace.com> --------- Co-authored-by: Chengzhong Wu <legendecas@gmail.com> Co-authored-by: Martin Kuba <martin@martinkuba.com> Co-authored-by: Mend Renovate <bot@renovateapp.com> Co-authored-by: Siim Kallas <siimkallas@gmail.com> Co-authored-by: Marc Pichler <marc.pichler@dynatrace.com> Co-authored-by: David Luna <david.luna@elastic.co> Co-authored-by: Dinko Osrecki <dinko.osrecki@emarsys.com> Co-authored-by: Trent Mick <trentm@gmail.com> Co-authored-by: François <32224751+Lp-Francois@users.noreply.github.com> Co-authored-by: Hyun Oh <hyunnoh01@gmail.com> Co-authored-by: André Cruz <andremiguelcruz@msn.com> Co-authored-by: artahmetaj <artahmetaj@yahoo.com> Co-authored-by: drewcorlin1 <82601620+drewcorlin1@users.noreply.github.com> |
||
|---|---|---|
| .. | ||
| src | ||
| test | ||
| .eslintignore | ||
| .eslintrc.js | ||
| .gitignore | ||
| .npmignore | ||
| LICENSE | ||
| README.md | ||
| hook.mjs | ||
| karma.conf.js | ||
| package.json | ||
| tsconfig.esm.json | ||
| tsconfig.esnext.json | ||
| tsconfig.json | ||
README.md
OpenTelemetry Instrumentation for web and node
Note: This is an experimental package under active development. New releases may include breaking changes.
Installation
npm install --save @opentelemetry/instrumentation
Usage in Node
import {
InstrumentationBase,
InstrumentationConfig,
InstrumentationNodeModuleDefinition,
InstrumentationNodeModuleFile,
} from '@opentelemetry/instrumentation';
import type * as module_name_to_be_patched from 'module_name_to_be_patched';
export class MyInstrumentation extends InstrumentationBase {
constructor(config: InstrumentationConfig = {}) {
super('MyInstrumentation', VERSION, config);
}
/**
* Init method will be called when the plugin is constructed.
* It returns an `InstrumentationNodeModuleDefinition` which describes
* the node module to be instrumented and patched.
* It may also return a list of `InstrumentationNodeModuleDefinition`s if
* the plugin should patch multiple modules or versions.
*/
protected init() {
const module = new InstrumentationNodeModuleDefinition<typeof module_name_to_be_patched>(
'module_name_to_be_patched',
['1.*'],
this._onPatchMain,
this._onUnPatchMain,
);
// in case you need to patch additional files - this is optional
module.files.push(this._addPatchingMethod());
return module;
// you can also define more modules then just return an array of modules
// return [module1, module2, ....]
}
private _onPatchMain(moduleExports: typeof module_name_to_be_patched) {
this._wrap(
moduleExports,
'mainMethodName',
this._patchMainMethodName()
);
return moduleExports;
}
private _onUnPatchMain(moduleExports: typeof module_name_to_be_patched) {
this._unwrap(moduleExports, 'mainMethodName');
}
private _addPatchingMethod(): InstrumentationNodeModuleFile<typeof module_name_to_be_patched> {
const file = new InstrumentationNodeModuleFile<typeof module_name_to_be_patched>(
'module_name_to_be_patched/src/some_file.js',
this._onPatchMethodName,
this._onUnPatchMethodName,
);
return file;
}
private _onPatchMethodName(moduleExports: typeof module_name_to_be_patched) {
this._wrap(
moduleExports,
'methodName',
this._patchMethodName()
);
return moduleExports;
}
private _onUnPatchMethodName(moduleExports: typeof module_name_to_be_patched) {
this._unwrap(moduleExports, 'methodName');
}
private _patchMethodName(): (original) => any {
const plugin = this;
return function methodName(original) {
return function patchMethodName(this: any): PromiseOrValue<module_name_to_be_patched.methodName> {
console.log('methodName', arguments);
return original.apply(this, arguments);
};
};
}
private _patchMainMethodName(): (original) => any {
const plugin = this;
return function mainMethodName(original) {
return function patchMainMethodName(this: any): PromiseOrValue<module_name_to_be_patched.mainMethodName> {
console.log('mainMethodName', arguments);
return original.apply(this, arguments);
};
};
}
}
// Later, but before the module to instrument is required
const myInstrumentationn = new MyInstrumentation();
myInstrumentation.setTracerProvider(provider); // this is optional, only if global TracerProvider shouldn't be used
myInstrumentation.setMeterProvider(meterProvider); // this is optional
myInstrumentation.enable();
// or use Auto Loader
Usage in Web
import {
InstrumentationBase,
InstrumentationConfig,
} from '@opentelemetry/instrumentation';
import { Instrumentation } from '@opentelemetry/instrumentation';
export class MyInstrumentation extends InstrumentationBase {
constructor(config: InstrumentationConfig = {}) {
super('MyInstrumentation', VERSION, config);
}
private _patchOpen() {
return (original: OpenFunction): OpenFunction => {
const plugin = this;
return function patchOpen(this: XMLHttpRequest, ...args): void {
console.log('open', arguments);
return original.apply(this, args);
};
};
}
public enable() {
this._wrap(XMLHttpRequest.prototype, 'open', this._patchOpen());
}
public disable() {
this._unwrap(XMLHttpRequest.prototype, 'open');
}
}
// Later
const myInstrumentation = new MyInstrumentation();
myInstrumentation.setTracerProvider(provider); // this is optional, only if global TracerProvider shouldn't be used
myInstrumentation.setMeterProvider(meterProvider); // this is optional, only if global MeterProvider shouldn't be used
myInstrumentation.enable();
// or use Auto Loader
AutoLoader
NODE - Auto Loader
const { B3Propagator } = require('@opentelemetry/propagator-b3');
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');
const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');
const tracerProvider = new NodeTracerProvider();
tracerProvider.register({
propagator: new B3Propagator(),
});
registerInstrumentations({
instrumentations: [
new HttpInstrumentation(),
],
//tracerProvider: tracerProvider, // optional, only if global TracerProvider shouldn't be used
//meterProvider: meterProvider, // optional, only if global MeterProvider shouldn't be used
});
WEB - Auto Loader
const { B3Propagator } = require('@opentelemetry/propagator-b3');
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
const { XMLHttpRequestInstrumentation } = require('@opentelemetry/instrumentation-xml-http-request');
const { WebTracerProvider } = require('@opentelemetry/sdk-trace-web');
const tracerProvider = new WebTracerProvider();
tracerProvider.register({
propagator: new B3Propagator(),
});
registerInstrumentations({
instrumentations: [
new XMLHttpRequestInstrumentation({
ignoreUrls: [/localhost/],
propagateTraceHeaderCorsUrls: [
'http://localhost:8090',
],
}),
],
//tracerProvider: tracerProvider, // optional, only if global TracerProvider shouldn't be used
//meterProvider: meterProvider, // optional, only if global MeterProvider shouldn't be used
});
Selection of the used TracerProvider/MeterProvider
The registerInstrumentations() API allows to specify which TracerProvider and/or MeterProvider to use by the given options object.
If nothing is specified the global registered provider is used. Usually this is what most users want therefore it's recommended to keep this default.
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 ES Modules 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=@opentelemetry/instrumentation/hook.mjs flag to the node binary. Alternatively you can set the NODE_OPTIONS environment variable to NODE_OPTIONS="--experimental-loader=@opentelemetry/instrumentation/hook.mjs".
As the ESM module loader from NodeJS is experimental, so is our support for it. Feel free to provide feedback or report issues about it.
Note: ESM Instrumentation is not yet supported for Node 20.
Limitations
Instrumentations for external modules (e.g. express, mongodb,...) hooks the require call or import statement. Therefore following conditions need to be met that this mechanism can work:
- Instrumentations are registered before the module to instrument is
requireed (CJS only) - modules are not included in a bundle. Tools like
esbuild,webpack, ... usually have some mechanism to exclude specific modules from bundling
License
Apache 2.0 - See LICENSE for more information.
Useful links
- For more information on OpenTelemetry, visit: https://opentelemetry.io/
- For help or feedback on this project, join us in GitHub Discussions