Use instrumentation loader to load plugins and instrumentations (#1855)

* chore: removing plugin loader from node, updating usage and docs

* chore: testing and updating examples

* chore: lint

* chore: adding comment about usage

* chore: adding upgrading guidelines
This commit is contained in:
Bartlomiej Obecny 2021-02-05 19:02:29 +01:00 committed by GitHub
parent 998176399e
commit 9cfa92c4a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 381 additions and 1250 deletions

View File

@ -5,9 +5,9 @@ labels: instrumentation
---
<!--
**NB:** Before opening a plugin support request against this repo, consider whether the plugin should reside in the [contrib repository](https://github.com/open-telemetry/opentelemetry-js-contrib).
**NB:** Before opening an instrumentation support request against this repo, consider whether the instrumentation should reside in the [contrib repository](https://github.com/open-telemetry/opentelemetry-js-contrib).
You are welcome to try out the [plugin api](https://github.com/open-telemetry/opentelemetry-js/blob/main/doc/plugin-guide.md) to build your own plugin. If you do try out the plugin api, please let us know if you have any questions/feedback.
You are welcome to try out the [instrumentation api](https://github.com/open-telemetry/opentelemetry-js/blob/main/doc/instrumentation-guide.md) to build your own instrumentation. If you do try out the instrumentation api, please let us know if you have any questions/feedback.
-->
### Is it applicable for Node or Browser or both

View File

@ -135,7 +135,7 @@ Approvers ([@open-telemetry/js-approvers](https://github.com/orgs/open-telemetry
- [Naseem K. Ullah](https://github.com/naseemkullah), Transit
- [Olivier Albertini](https://github.com/OlivierAlbertini), Ville de Montréal
*Find more about the approver role in [community repository](https://github.com/open-telemetry/community/blob/master/community-membership.md#approver).*
*Find more about the approver role in [community repository](https://github.com/open-telemetry/community/blob/main/community-membership.md#approver).*
Maintainers ([@open-telemetry/js-maintainers](https://github.com/orgs/open-telemetry/teams/javascript-maintainers)):
@ -143,7 +143,7 @@ Maintainers ([@open-telemetry/js-maintainers](https://github.com/orgs/open-telem
- [Daniel Dyla](https://github.com/dyladan), Dynatrace
- [Valentin Marchaud](https://github.com/vmarchaud), Open Source Contributor
*Find more about the maintainer role in [community repository](https://github.com/open-telemetry/community/blob/master/community-membership.md#maintainer).*
*Find more about the maintainer role in [community repository](https://github.com/open-telemetry/community/blob/main/community-membership.md#maintainer).*
### Thanks to all the people who already contributed
@ -182,18 +182,17 @@ OpenTelemetry is vendor-agnostic and can upload data to any backend with various
See the [OpenTelemetry registry](https://opentelemetry.io/registry/?s=node.js) for a list of exporters available.
### Plugins
### Instrumentations & Plugins
OpenTelemetry can collect tracing data automatically using plugins. Vendors/Users can also create and use their own. Currently, OpenTelemetry supports automatic tracing for:
OpenTelemetry can collect tracing data automatically using instrumentations. Vendors/Users can also create and use their own. Currently, OpenTelemetry supports automatic tracing for:
#### Node Plugins
#### Node Instrumentations & Plugins
##### Core
- [@opentelemetry/plugin-grpc][otel-plugin-grpc]
- [@opentelemetry/instrumentation-grpc][otel-instrumentation-grpc] previous [@opentelemetry/plugin-grpc][otel-plugin-grpc]
- [@opentelemetry/plugin-grpc-js][otel-plugin-grpc-js]
- [@opentelemetry/plugin-http][otel-plugin-http]
- [@opentelemetry/plugin-https][otel-plugin-https]
- [@opentelemetry/instrumentation-http][otel-plugin-http] previous [@opentelemetry/plugin-http][otel-plugin-http] and [@opentelemetry/plugin-https][otel-plugin-https]
##### Contrib
@ -215,7 +214,7 @@ These plugins are hosted at <https://github.com/open-telemetry/opentelemetry-js-
##### Core
- [@opentelemetry/instrumentation-xml-http-request][otel-instrumentation-xml-http-request]
- [@opentelemetry/plugin-fetch][otel-plugin-fetch]
- [@opentelemetry/instrumentation-fetch][otel-instrumentation-fetch]
##### Contrib
@ -224,7 +223,7 @@ These plugins are hosted at <https://github.com/open-telemetry/opentelemetry-js-
- [@opentelemetry/plugin-document-load][otel-contrib-plugin-document-load]
- [@opentelemetry/plugin-user-interaction][otel-contrib-plugin-user-interaction]
To request automatic tracing support for a module not on this list, please [file an issue](https://github.com/open-telemetry/opentelemetry-js/issues). Alternatively, you can [write a plugin yourself](https://github.com/open-telemetry/opentelemetry-js/blob/main/doc/plugin-guide.md).
To request automatic tracing support for a module not on this list, please [file an issue](https://github.com/open-telemetry/opentelemetry-js/issues). Alternatively, you can [write an instrumentation yourself](https://github.com/open-telemetry/opentelemetry-js/blob/main/doc/instrumentation-guide.md).
### Shims
@ -234,6 +233,65 @@ To request automatic tracing support for a module not on this list, please [file
## Upgrade guidelines
### 0.16.0 to 0.17.0
[PR-1855](https://github.com/open-telemetry/opentelemetry-js/pull/1855) Use instrumentation loader to load plugins and instrumentations
- Providers do no load the plugins anymore. Also PluginLoader has been removed from providers, use `registerInstrumentations` instead
```javascript
//Previously in node
const provider = new NodeTracerProvider({
plugins: {
'@grpc/grpc-js': {
enabled: true,
path: '@opentelemetry/plugin-grpc-js',
},
}
});
// Now
const provider = new NodeTracerProvider();
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
registerInstrumentations({
instrumentations: [
{
plugins: {
'@grpc/grpc-js': {
enabled: true,
path: '@opentelemetry/plugin-grpc-js',
},
}
}
],
tracerProvider: provider,
});
// or if you want to load only default instrumentations / plugins
registerInstrumentations({
tracerProvider: provider,
});
//Previously in browser
const provider = new WebTracerProvider({
plugins: [
new DocumentLoad()
]
});
// Now
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
const provider = new WebTracerProvider();
registerInstrumentations({
instrumentations: [
new DocumentLoad(),
],
});
```
- `registerInstrumentations` supports loading old plugins and instrumentations together. It also supports setting tracer provider and meter provider on instrumentations
## Upgrade guidelines
### 0.15.0 to 0.16.0
[PR-1874](https://github.com/open-telemetry/opentelemetry-js/pull/1874) More specific API type names
@ -290,11 +348,15 @@ Apache 2.0 - See [LICENSE][license-url] for more information.
[otel-metrics]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-metrics
[otel-node]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-node
[otel-plugin-fetch]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-plugin-fetch
[otel-plugin-grpc]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-plugin-grpc
[otel-plugin-grpc-js]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-plugin-grpc-js
[otel-plugin-http]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-plugin-http
[otel-plugin-https]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-plugin-https
[otel-instrumentation-fetch]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-instrumentation-fetch
[otel-instrumentation-grpc]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-instrumentation-grpc
[otel-instrumentation-http]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-instrumentation-http
[otel-instrumentation-https]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-instrumentation-https
[otel-instrumentation-xml-http-request]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-instrumentation-xml-http-request
[otel-shim-opentracing]: https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-shim-opentracing

View File

@ -1,6 +1,6 @@
# Plugin Developer Guide
# Instrumentation Developer Guide
The `NodeTracerProvider` or `Node-SDK` is driven by a set of plugins that describe how to patch a module to generate trace spans when that module is used. We provide out-of-the-box instrumentation for many popular frameworks and libraries by using a plugin system (see [builtin plugins][builtin-plugins]), and provide a means for developers to create their own.
We provide out-of-the-box instrumentations for many popular frameworks and libraries by using an instrumentation system (see [builtin instrumentations][builtin-instrumentations]), and provide a means for developers to create their own.
We strongly recommended to create a dedicated package for newly added plugin, example: `@opentelemetry/plugin-xxx`.
@ -45,7 +45,7 @@ We recommend using [`shimmer`][shimmer] to modify function properties on objects
Please refer to the [HTTP instrumentation][http-plugin] or [gRPC instrumentation][grpc-plugin] for more comprehensive examples.
[shimmer]: https://github.com/othiym23/shimmer
[builtin-plugins]: https://github.com/open-telemetry/opentelemetry-js#plugins
[builtin-instrumentations]: https://github.com/open-telemetry/opentelemetry-js#instrumentations&plugins
[base-plugin]: https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-core/src/platform/node/BasePlugin.ts
[http-plugin]: https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-plugin-http/src/http.ts#L44
[grpc-plugin]: https://github.com/open-telemetry/opentelemetry-js/blob/main/packages/opentelemetry-plugin-grpc/src/grpc.ts#L52

View File

@ -1,7 +1,7 @@
'use strict';
const api = require('@opentelemetry/api');
const tracer = require('./tracer')('example-grpc-client');
const tracer = require('./tracer')('example-grpc-js-client');
// eslint-disable-next-line import/order
const grpc = require('@grpc/grpc-js');
const messages = require('./helloworld_pb');

View File

@ -32,6 +32,7 @@
"@opentelemetry/api": "^0.16.0",
"@opentelemetry/exporter-jaeger": "^0.16.0",
"@opentelemetry/exporter-zipkin": "^0.16.0",
"@opentelemetry/instrumentation": "^0.16.0",
"@opentelemetry/node": "^0.16.0",
"@opentelemetry/plugin-grpc-js": "^0.16.0",
"@opentelemetry/tracing": "^0.16.0",

View File

@ -1,7 +1,7 @@
'use strict';
const api = require('@opentelemetry/api');
const tracer = require('./tracer')(('example-grpc-server'));
const tracer = require('./tracer')(('example-grpc-js-server'));
// eslint-disable-next-line import/order
const grpc = require('@grpc/grpc-js');

View File

@ -1,6 +1,7 @@
'use strict';
const opentelemetry = require('@opentelemetry/api');
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
const { NodeTracerProvider } = require('@opentelemetry/node');
const { SimpleSpanProcessor } = require('@opentelemetry/tracing');
const { JaegerExporter } = require('@opentelemetry/exporter-jaeger');
@ -9,13 +10,26 @@ const { ZipkinExporter } = require('@opentelemetry/exporter-zipkin');
const EXPORTER = process.env.EXPORTER || '';
module.exports = (serviceName) => {
const provider = new NodeTracerProvider({
plugins: {
'@grpc/grpc-js': {
enabled: true,
path: '@opentelemetry/plugin-grpc-js',
const provider = new NodeTracerProvider();
registerInstrumentations({
instrumentations: [
{
plugins: {
'@grpc/grpc-js': {
enabled: true,
path: '@opentelemetry/plugin-grpc-js',
// // when boostraping with lerna for testing purposes
// path: `${__dirname}/../../packages/opentelemetry-plugin-grpc-js/build/src`
},
// // when boostraping with lerna for testing purposes
// 'http': {
// enabled: true,
// path: `${__dirname}/../../packages/opentelemetry-plugin-http/build/src`
// },
},
},
},
],
tracerProvider: provider,
});
let exporter;

View File

@ -31,6 +31,7 @@
"@opentelemetry/api": "^0.16.0",
"@opentelemetry/exporter-jaeger": "^0.16.0",
"@opentelemetry/exporter-zipkin": "^0.16.0",
"@opentelemetry/instrumentation": "^0.16.0",
"@opentelemetry/node": "^0.16.0",
"@opentelemetry/plugin-grpc": "^0.16.0",
"@opentelemetry/tracing": "^0.16.0",

View File

@ -1,6 +1,7 @@
'use strict';
const opentelemetry = require('@opentelemetry/api');
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
const { NodeTracerProvider } = require('@opentelemetry/node');
const { SimpleSpanProcessor } = require('@opentelemetry/tracing');
const { JaegerExporter } = require('@opentelemetry/exporter-jaeger');
@ -10,6 +11,20 @@ const EXPORTER = process.env.EXPORTER || '';
module.exports = (serviceName) => {
const provider = new NodeTracerProvider();
registerInstrumentations({
tracerProvider: provider,
// // when boostraping with lerna for testing purposes
// instrumentations: [
// {
// plugins: {
// grpc: {
// enabled: true,
// path: `${__dirname}/../../packages/opentelemetry-plugin-grpc/build/src`
// }
// }
// }
// ],
});
let exporter;
if (EXPORTER.toLowerCase().startsWith('z')) {

View File

@ -31,6 +31,7 @@
"@opentelemetry/api": "^0.16.0",
"@opentelemetry/exporter-jaeger": "^0.16.0",
"@opentelemetry/exporter-zipkin": "^0.16.0",
"@opentelemetry/instrumentation": "^0.16.0",
"@opentelemetry/node": "^0.16.0",
"@opentelemetry/plugin-http": "^0.16.0",
"@opentelemetry/tracing": "^0.16.0"

View File

@ -1,6 +1,7 @@
'use strict';
const opentelemetry = require('@opentelemetry/api');
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
const { NodeTracerProvider } = require('@opentelemetry/node');
const { SimpleSpanProcessor } = require('@opentelemetry/tracing');
const { JaegerExporter } = require('@opentelemetry/exporter-jaeger');
@ -10,6 +11,20 @@ const EXPORTER = process.env.EXPORTER || '';
module.exports = (serviceName) => {
const provider = new NodeTracerProvider();
registerInstrumentations({
tracerProvider: provider,
// // when boostraping with lerna for testing purposes
// instrumentations: [
// {
// plugins: {
// http: {
// enabled: true,
// path: `${__dirname}/../../packages/opentelemetry-plugin-http/build/src`
// }
// }
// }
// ],
});
let exporter;
if (EXPORTER.toLowerCase().startsWith('z')) {

View File

@ -32,6 +32,7 @@
"@opentelemetry/api": "^0.16.0",
"@opentelemetry/exporter-jaeger": "^0.16.0",
"@opentelemetry/exporter-zipkin": "^0.16.0",
"@opentelemetry/instrumentation": "^0.16.0",
"@opentelemetry/node": "^0.16.0",
"@opentelemetry/plugin-https": "^0.16.0",
"@opentelemetry/tracing": "^0.16.0"

View File

@ -1,6 +1,7 @@
'use strict';
const opentelemetry = require('@opentelemetry/api');
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
const { NodeTracerProvider } = require('@opentelemetry/node');
const { SimpleSpanProcessor } = require('@opentelemetry/tracing');
const { JaegerExporter } = require('@opentelemetry/exporter-jaeger');
@ -12,6 +13,20 @@ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
module.exports = (serviceName) => {
let exporter;
const provider = new NodeTracerProvider();
registerInstrumentations({
tracerProvider: provider,
// when boostraping with lerna for testing purposes
// instrumentations: [
// {
// plugins: {
// https: {
// enabled: true,
// path: `${__dirname}/../../packages/opentelemetry-plugin-https/build/src`
// }
// }
// }
// ],
});
if (EXPORTER.toLowerCase().startsWith('z')) {
exporter = new ZipkinExporter({

View File

@ -2,7 +2,7 @@
const http = require('http');
const opentracing = require('opentracing');
const shim = require('./shim').shim('http_client_service');
const shim = require('./shim').shim('http_client_opentracing');
opentracing.initGlobalTracer(shim);
const tracer = opentracing.globalTracer();

View File

@ -31,6 +31,7 @@
"dependencies": {
"@opentelemetry/exporter-jaeger": "^0.16.0",
"@opentelemetry/exporter-zipkin": "^0.16.0",
"@opentelemetry/instrumentation": "^0.16.0",
"@opentelemetry/node": "^0.16.0",
"@opentelemetry/shim-opentracing": "^0.16.0",
"@opentelemetry/tracing": "^0.16.0",

View File

@ -3,7 +3,7 @@
const http = require('http');
const opentracing = require('opentracing');
const utils = require('./utils');
const shim = require('./shim').shim('http_server_service');
const shim = require('./shim').shim('http_server_opentracing');
opentracing.initGlobalTracer(shim);
const tracer = opentracing.globalTracer();

View File

@ -1,5 +1,6 @@
'use strict';
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
const { NodeTracerProvider } = require('@opentelemetry/node');
const { SimpleSpanProcessor } = require('@opentelemetry/tracing');
const { JaegerExporter } = require('@opentelemetry/exporter-jaeger');
@ -8,6 +9,20 @@ const { TracerShim } = require('@opentelemetry/shim-opentracing');
function shim(serviceName) {
const provider = new NodeTracerProvider();
registerInstrumentations({
tracerProvider: provider,
// // when boostraping with lerna for testing purposes
// instrumentations: [
// {
// plugins: {
// 'opentracing': {
// enabled: true,
// path: `${__dirname}/../../packages/opentelemetry-shim-opentracing/build/src`
// }
// }
// }
// ],
});
provider.addSpanProcessor(new SimpleSpanProcessor(getExporter(serviceName)));
// Initialize the OpenTelemetry APIs to use the NodeTracerProvider bindings

View File

@ -4,12 +4,15 @@ import { WebTracerProvider } from '@opentelemetry/web';
import { DocumentLoad } from '@opentelemetry/plugin-document-load';
import { ZoneContextManager } from '@opentelemetry/context-zone';
import { CollectorTraceExporter } from '@opentelemetry/exporter-collector';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
const provider = new WebTracerProvider({
plugins: [
new DocumentLoad(),
],
const provider = new WebTracerProvider();
registerInstrumentations({
instrumentations: [new DocumentLoad()],
tracerProvider: provider,
});
provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));
provider.addSpanProcessor(new SimpleSpanProcessor(new CollectorTraceExporter()));

View File

@ -8,9 +8,12 @@ import { WebTracerProvider } from '@opentelemetry/web';
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';
import { ZoneContextManager } from '@opentelemetry/context-zone';
import { B3Propagator } from '@opentelemetry/propagator-b3';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
const provider = new WebTracerProvider({
plugins: [
const provider = new WebTracerProvider();
registerInstrumentations({
instrumentations: [
new FetchInstrumentation({
ignoreUrls: [/localhost:8090\/sockjs-node/],
propagateTraceHeaderCorsUrls: [
@ -20,6 +23,7 @@ const provider = new WebTracerProvider({
clearTimingResources: true
}),
],
tracerProvider: provider,
});
provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));

View File

@ -5,9 +5,12 @@ import { ZoneContextManager } from '@opentelemetry/context-zone';
import { CollectorTraceExporter } from '@opentelemetry/exporter-collector';
import { B3Propagator } from '@opentelemetry/propagator-b3';
import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
const providerWithZone = new WebTracerProvider({
plugins: [
const providerWithZone = new WebTracerProvider();
registerInstrumentations({
instrumentations: [
new UserInteractionPlugin(),
new XMLHttpRequestInstrumentation({
ignoreUrls: [/localhost/],
@ -16,6 +19,7 @@ const providerWithZone = new WebTracerProvider({
],
}),
],
tracerProvider: providerWithZone,
});
providerWithZone.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));

View File

@ -5,9 +5,12 @@ import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xm
import { ZoneContextManager } from '@opentelemetry/context-zone';
import { CollectorTraceExporter } from '@opentelemetry/exporter-collector';
import { B3Propagator } from '@opentelemetry/propagator-b3';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
const providerWithZone = new WebTracerProvider({
plugins: [
const providerWithZone = new WebTracerProvider();
registerInstrumentations({
instrumentations: [
new XMLHttpRequestInstrumentation({
ignoreUrls: [/localhost:8090\/sockjs-node/],
propagateTraceHeaderCorsUrls: [
@ -15,6 +18,7 @@ const providerWithZone = new WebTracerProvider({
],
}),
],
tracerProvider: providerWithZone,
});
providerWithZone.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));

View File

@ -34,10 +34,12 @@
"webpack-merge": "^4.2.2"
},
"dependencies": {
"@opentelemetry/api": "^0.16.0",
"@opentelemetry/context-zone": "^0.16.0",
"@opentelemetry/core": "^0.16.0",
"@opentelemetry/exporter-collector": "^0.16.0",
"@opentelemetry/exporter-zipkin": "^0.16.0",
"@opentelemetry/instrumentation": "^0.16.0",
"@opentelemetry/instrumentation-fetch": "^0.16.0",
"@opentelemetry/instrumentation-xml-http-request": "^0.16.0",
"@opentelemetry/metrics": "^0.16.0",

View File

@ -78,12 +78,18 @@ Create a file named `tracing.js` and add the following code:
const { LogLevel } = require("@opentelemetry/core");
const { NodeTracerProvider } = require("@opentelemetry/node");
const { registerInstrumentations } = require("@opentelemetry/instrumentation");
const provider = new NodeTracerProvider({
logLevel: LogLevel.ERROR
});
provider.register();
registerInstrumentations({
tracerProvider: provider,
});
```
Now, if you run your application with `node -r ./tracing.js app.js`, your application will create and propagate traces over HTTP. If an already instrumented service that supports [Trace Context](https://www.w3.org/TR/trace-context/) headers calls your application using HTTP, and you call another application using HTTP, the Trace Context headers will be correctly propagated.
@ -116,11 +122,16 @@ const { LogLevel } = require("@opentelemetry/core");
const { NodeTracerProvider } = require("@opentelemetry/node");
const { SimpleSpanProcessor } = require("@opentelemetry/tracing");
const { ZipkinExporter } = require("@opentelemetry/exporter-zipkin");
const { registerInstrumentations } = require("@opentelemetry/instrumentation");
const provider = new NodeTracerProvider({
logLevel: LogLevel.ERROR
});
registerInstrumentations({
tracerProvider: provider,
});
provider.register();
provider.addSpanProcessor(

View File

@ -12,6 +12,7 @@
"@opentelemetry/core": "^0.14.0",
"@opentelemetry/exporter-zipkin": "^0.14.0",
"@opentelemetry/node": "^0.14.0",
"@opentelemetry/instrumentation": "^0.16.0",
"@opentelemetry/plugin-express": "^0.12.1",
"@opentelemetry/plugin-http": "^0.14.0",
"@opentelemetry/plugin-https": "^0.14.0",

View File

@ -4,6 +4,7 @@ const { LogLevel } = require("@opentelemetry/core");
const { NodeTracerProvider } = require("@opentelemetry/node");
const { SimpleSpanProcessor } = require("@opentelemetry/tracing");
const { ZipkinExporter } = require("@opentelemetry/exporter-zipkin");
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
const provider = new NodeTracerProvider({ logLevel: LogLevel.ERROR });
@ -19,4 +20,10 @@ provider.addSpanProcessor(
);
provider.register();
// load old default plugins
registerInstrumentations({
tracerProvider: provider,
});
console.log("tracing initialized");

View File

@ -15,7 +15,10 @@
*/
import * as api from '@opentelemetry/api';
import * as core from '@opentelemetry/core';
import { isWrapped } from '@opentelemetry/instrumentation';
import {
isWrapped,
registerInstrumentations,
} from '@opentelemetry/instrumentation';
import {
B3Propagator,
@ -215,7 +218,10 @@ describe('fetch', () => {
fetchInstrumentation = new FetchInstrumentation(config);
webTracerProviderWithZone = new WebTracerProvider({
logLevel: core.LogLevel.ERROR,
plugins: [fetchInstrumentation],
});
registerInstrumentations({
tracerProvider: webTracerProviderWithZone,
instrumentations: [fetchInstrumentation],
});
webTracerWithZone = webTracerProviderWithZone.getTracer('fetch-test');
dummySpanExporter = new DummySpanExporter();

View File

@ -14,6 +14,7 @@
* limitations under the License.
*/
import { Span } from '@opentelemetry/api';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
import { HttpAttribute } from '@opentelemetry/semantic-conventions';
import { ReadableSpan, SpanProcessor } from '@opentelemetry/tracing';
import { WebTracerProvider } from '@opentelemetry/web';
@ -40,8 +41,10 @@ describe('unmocked xhr', () => {
let testSpans: TestSpanProcessor;
let provider: WebTracerProvider;
beforeEach(() => {
provider = new WebTracerProvider({
plugins: [new XMLHttpRequestInstrumentation()],
provider = new WebTracerProvider();
registerInstrumentations({
instrumentations: [new XMLHttpRequestInstrumentation()],
tracerProvider: provider,
});
testSpans = new TestSpanProcessor();
provider.addSpanProcessor(testSpans);

View File

@ -19,6 +19,7 @@ import {
otperformance as performance,
isWrapped,
} from '@opentelemetry/core';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
import {
B3Propagator,
B3InjectEncoding,
@ -203,7 +204,10 @@ describe('xhr', () => {
);
webTracerProviderWithZone = new WebTracerProvider({
logLevel: LogLevel.ERROR,
plugins: [xmlHttpRequestInstrumentation],
});
registerInstrumentations({
instrumentations: [xmlHttpRequestInstrumentation],
tracerProvider: webTracerProviderWithZone,
});
webTracerWithZone = webTracerProviderWithZone.getTracer('xhr-test');
dummySpanExporter = new DummySpanExporter();
@ -730,8 +734,13 @@ describe('xhr', () => {
webTracerWithZoneProvider = new WebTracerProvider({
logLevel: LogLevel.ERROR,
plugins: [new XMLHttpRequestInstrumentation()],
});
registerInstrumentations({
instrumentations: [new XMLHttpRequestInstrumentation()],
tracerProvider: webTracerWithZoneProvider,
});
dummySpanExporter = new DummySpanExporter();
exportSpy = sinon.stub(dummySpanExporter, 'export');
webTracerWithZoneProvider.addSpanProcessor(

View File

@ -17,4 +17,5 @@
export * from './autoLoader';
export * from './platform/index';
export * from './types';
export * from './types_internal';
export * from './utils';

View File

@ -13,9 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './old/autoLoader';
export * from './instrumentation';
export * from './instrumentationNodeModuleDefinition';
export * from './instrumentationNodeModuleFile';
export * from './old/autoLoader';
export * from './types';

View File

@ -15,10 +15,9 @@
*/
import * as assert from 'assert';
import { Instrumentation } from '../../src';
import { InstrumentationAbstract } from '../../src/instrumentation';
import { Instrumentation, InstrumentationBase } from '../../src';
class TestInstrumentation extends InstrumentationAbstract {
class TestInstrumentation extends InstrumentationBase {
constructor() {
super('test', '1.0.0');
}
@ -34,7 +33,7 @@ describe('BaseInstrumentation', () => {
});
it('should create an instance', () => {
assert.ok(instrumentation instanceof InstrumentationAbstract);
assert.ok(instrumentation instanceof InstrumentationBase);
});
it('should have a name', () => {
@ -44,4 +43,17 @@ describe('BaseInstrumentation', () => {
it('should have a version', () => {
assert.deepStrictEqual(instrumentation.instrumentationVersion, '1.0.0');
});
describe('constructor', () => {
it('should enable instrumentation by default', () => {
let called = false;
class TestInstrumentation2 extends TestInstrumentation {
enable() {
called = true;
}
}
instrumentation = new TestInstrumentation2();
assert.strictEqual(called, true);
});
});
});

View File

@ -12,17 +12,13 @@ For manual instrumentation see the
## How auto instrumentation works
This package exposes a `NodeTracerProvider` that will automatically hook into the module loader of Node.js.
This package exposes a `NodeTracerProvider`.
For loading plugins / instrumentations please use `registerInstrumentations` function from [opentelemetry-instrumentation](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-instrumentation)
For this to work, please make sure that `NodeTracerProvider` is initialized before any other module of your application, (like `http` or `express`) is loaded.
OpenTelemetry comes with a growing number of instrumentation plugins for well know modules (see [supported modules](https://github.com/open-telemetry/opentelemetry-js#plugins)) and an API to create custom plugins (see [the plugin developer guide](https://github.com/open-telemetry/opentelemetry-js/blob/main/doc/plugin-guide.md)).
Whenever a module is loaded `NodeTracerProvider` will check if a matching instrumentation plugin has been installed.
OpenTelemetry comes with a growing number of instrumentation plugins for well know modules (see [supported modules](https://github.com/open-telemetry/opentelemetry-js#plugins)) and an API to create custom instrumentation (see [the instrumentation developer guide](https://github.com/open-telemetry/opentelemetry-js/blob/main/doc/instrumentation-guide.md)).
> **Please note:** This module does *not* bundle any plugins. They need to be installed separately.
If the respective plugin was found, it will be used to patch the original module to add instrumentation code.
This is done by wrapping all tracing-relevant functions.
This instrumentation code will automatically
@ -32,8 +28,6 @@ This instrumentation code will automatically
- add this trace-context identifier to outbound requests to allow continuing the distributed trace on the next hop (if applicable)
- create and end spans
In short, this means that this module will use provided plugins to automatically instrument your application to produce spans and provide end-to-end tracing by just adding a few lines of code.
## Creating custom spans on top of auto-instrumentation
Additionally to automated instrumentation, `NodeTracerProvider` exposes the same API as [@opentelemetry/tracing](https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-tracing), allowing creating custom spans if needed.
@ -45,8 +39,9 @@ npm install --save @opentelemetry/api
npm install --save @opentelemetry/node
# Install instrumentation plugins
npm install --save @opentelemetry/plugin-http
npm install --save @opentelemetry/plugin-https
npm install --save @opentelemetry/instrumentation-http
# and for example one additional
npm install --save instrumentation-graphql
```
## Usage
@ -56,21 +51,28 @@ The following code will configure the `NodeTracerProvider` to instrument `http`
modules](https://github.com/open-telemetry/opentelemetry-js#plugins))
using `@opentelemetry/plugin-http`.
```js
```javascript
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
const { NodeTracerProvider } = require('@opentelemetry/node');
// Create and configure NodeTracerProvider
const provider = new NodeTracerProvider();
// Initialize the provider
provider.register()
provider.register();
// register and load instrumentation and old plugins - old plugins will be loaded automatically as previously
// but instrumentations needs to be added
registerInstrumentations({
tracerProvider: provider,
});
// Your application code - http will automatically be instrumented if
// @opentelemetry/plugin-http is present
const http = require('http');
```
## Plugin configuration
## Instrumentation / Plugin configuration
User supplied plugin configuration is merged with the default plugin
configuration. Furthermore, custom plugins that are configured are implicitly
@ -83,22 +85,37 @@ In the following example:
- the customPlugin is loaded from the user supplied path
- all default plugins are still loaded if installed.
```js
const provider = new NodeTracerProvider({
plugins: {
express: {
enabled: false,
},
http: {
requestHook: (span, request) => {
span.setAttribute("custom request hook attribute", "request");
```javascript
const { GraphQLInstrumentation } = require('@opentelemetry/instrumentation-graphql');
const provider = new NodeTracerProvider();
// register and load instrumentation and old plugins - old plugins will be loaded automatically as previously
// but instrumentations needs to be added
registerInstrumentations({
tracerProvider: provider,
instrumentations: [
new GraphQLInstrumentation(),
// for older plugins you can just copy paste the old configuration
{
plugins: {
express: {
enabled: false,
},
http: {
requestHook: (span, request) => {
span.setAttribute("custom request hook attribute", "request");
},
},
customPlugin: {
path: "/path/to/custom/module",
},
},
},
customPlugin: {
path: "/path/to/custom/module",
},
},
}
],
});
```
### Disable Plugins with Environment Variables

View File

@ -47,12 +47,14 @@
"@types/node": "14.14.20",
"@types/semver": "7.3.4",
"@types/shimmer": "1.0.1",
"@types/sinon": "9.0.10",
"codecov": "3.8.1",
"gts": "3.1.0",
"mocha": "7.2.0",
"nyc": "15.1.0",
"rimraf": "3.0.2",
"shimmer": "1.2.1",
"sinon": "9.2.3",
"ts-mocha": "8.0.0",
"ts-node": "9.1.1",
"typescript": "4.1.3"
@ -62,7 +64,6 @@
"@opentelemetry/context-async-hooks": "^0.16.0",
"@opentelemetry/core": "^0.16.0",
"@opentelemetry/tracing": "^0.16.0",
"require-in-the-middle": "^5.0.0",
"semver": "^7.1.3"
}
}

View File

@ -22,9 +22,8 @@ import {
BasicTracerProvider,
SDKRegistrationConfig,
} from '@opentelemetry/tracing';
import { DEFAULT_INSTRUMENTATION_PLUGINS, NodeTracerConfig } from './config';
import { PluginLoader, Plugins } from './instrumentation/PluginLoader';
import * as semver from 'semver';
import { NodeTracerConfig } from './config';
/**
* Register this TracerProvider for use with the OpenTelemetry API.
@ -34,25 +33,14 @@ import * as semver from 'semver';
* @param config Configuration object for SDK registration
*/
export class NodeTracerProvider extends BasicTracerProvider {
private readonly _pluginLoader: PluginLoader;
/**
* Constructs a new Tracer instance.
*/
constructor(config: NodeTracerConfig = {}) {
super(config);
this._pluginLoader = new PluginLoader(this, this.logger);
config.plugins
? this._pluginLoader.load(
this._mergePlugins(DEFAULT_INSTRUMENTATION_PLUGINS, config.plugins)
)
: this._pluginLoader.load(DEFAULT_INSTRUMENTATION_PLUGINS);
}
stop() {
this._pluginLoader.unload();
if (config.plugins) {
console.warn(
'plugins options was removed, please use' +
' "registerInstrumentations" to load plugins'
);
}
}
register(config: SDKRegistrationConfig = {}) {
@ -66,33 +54,4 @@ export class NodeTracerProvider extends BasicTracerProvider {
super.register(config);
}
/**
* Two layer merge.
* First, for user supplied config of plugin(s) that are loaded by default,
* merge the user supplied and default configs of said plugin(s).
* Then merge the results with the default plugins.
* @returns 2-layer deep merge of default and user supplied plugins.
*/
private _mergePlugins(
defaultPlugins: Plugins,
userSuppliedPlugins: Plugins
): Plugins {
const mergedUserSuppliedPlugins: Plugins = {};
for (const pluginName in userSuppliedPlugins) {
mergedUserSuppliedPlugins[pluginName] = {
// Any user-supplied non-default plugin should be enabled by default
...(DEFAULT_INSTRUMENTATION_PLUGINS[pluginName] || { enabled: true }),
...userSuppliedPlugins[pluginName],
};
}
const mergedPlugins: Plugins = {
...defaultPlugins,
...mergedUserSuppliedPlugins,
};
return mergedPlugins;
}
}

View File

@ -14,31 +14,12 @@
* limitations under the License.
*/
import { Plugins } from './instrumentation/PluginLoader';
import { TracerConfig } from '@opentelemetry/tracing';
/**
* NodeTracerConfig provides an interface for configuring a Node Tracer.
*/
export interface NodeTracerConfig extends TracerConfig {
/** Plugins options. */
plugins?: Plugins;
/** Plugins options deprecated */
plugins?: unknown[];
}
/** List of all default supported plugins */
export const DEFAULT_INSTRUMENTATION_PLUGINS: Plugins = {
mongodb: { enabled: true, path: '@opentelemetry/plugin-mongodb' },
grpc: { enabled: true, path: '@opentelemetry/plugin-grpc' },
'@grpc/grpc-js': { enabled: true, path: '@opentelemetry/plugin-grpc-js' },
http: { enabled: true, path: '@opentelemetry/plugin-http' },
https: { enabled: true, path: '@opentelemetry/plugin-https' },
mysql: { enabled: true, path: '@opentelemetry/plugin-mysql' },
pg: { enabled: true, path: '@opentelemetry/plugin-pg' },
redis: { enabled: true, path: '@opentelemetry/plugin-redis' },
ioredis: { enabled: true, path: '@opentelemetry/plugin-ioredis' },
'pg-pool': { enabled: true, path: '@opentelemetry/plugin-pg-pool' },
express: { enabled: true, path: '@opentelemetry/plugin-express' },
'@hapi/hapi': { enabled: true, path: '@opentelemetry/hapi-instrumentation' },
koa: { enabled: true, path: '@opentelemetry/koa-instrumentation' },
dns: { enabled: true, path: '@opentelemetry/plugin-dns' },
};

View File

@ -15,5 +15,4 @@
*/
export { NodeTracerConfig } from './config';
export { Plugins } from './instrumentation/PluginLoader';
export * from './NodeTracerProvider';

View File

@ -1,207 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Logger, TracerProvider } from '@opentelemetry/api';
import { Plugin, PluginConfig } from '@opentelemetry/core';
import { getEnv } from '@opentelemetry/core';
import * as hook from 'require-in-the-middle';
import * as utils from './utils';
// States for the Plugin Loader
export enum HookState {
UNINITIALIZED,
ENABLED,
DISABLED,
}
/**
* Wildcard symbol. If ignore list is set to this, disable all plugins
*/
const DISABLE_ALL_PLUGINS = '*';
export interface Plugins {
[pluginName: string]: PluginConfig;
}
/**
* Returns the Plugins object that meet the below conditions.
* Valid criteria: 1. It should be enabled. 2. Should have non-empty path.
*/
function filterPlugins(plugins: Plugins): Plugins {
const keys = Object.keys(plugins);
return keys.reduce((acc: Plugins, key: string) => {
if (plugins[key].enabled && plugins[key].path) acc[key] = plugins[key];
return acc;
}, {});
}
/**
* The PluginLoader class can load instrumentation plugins that use a patch
* mechanism to enable automatic tracing for specific target modules.
*/
export class PluginLoader {
/** A list of loaded plugins. */
private _plugins: Plugin[] = [];
/**
* A field that tracks whether the require-in-the-middle hook has been loaded
* for the first time, as well as whether the hook body is activated or not.
*/
private _hookState = HookState.UNINITIALIZED;
/** Constructs a new PluginLoader instance. */
constructor(readonly provider: TracerProvider, readonly logger: Logger) {}
/**
* Loads a list of plugins. Each plugin module should implement the core
* {@link Plugin} interface and export an instance named as 'plugin'. This
* function will attach a hook to be called the first time the module is
* loaded.
* @param Plugins an object whose keys are plugin names and whose
* {@link PluginConfig} values indicate several configuration options.
*/
load(plugins: Plugins): PluginLoader {
if (this._hookState === HookState.UNINITIALIZED) {
const pluginsToLoad = filterPlugins(plugins);
const modulesToHook = Object.keys(pluginsToLoad);
const modulesToIgnore = getEnv().OTEL_NO_PATCH_MODULES;
// Do not hook require when no module is provided. In this case it is
// not necessary. With skipping this step we lower our footprint in
// customer applications and require-in-the-middle won't show up in CPU
// frames.
if (modulesToHook.length === 0) {
this._hookState = HookState.DISABLED;
return this;
}
const requiredModulesToHook = modulesToHook.filter((name: string) => {
try {
const moduleResolvedFilename = require.resolve(name);
return moduleResolvedFilename in require.cache;
} catch {
return false;
}
});
if (requiredModulesToHook.length > 0) {
this.logger.warn(
`Some modules (${requiredModulesToHook.join(
', '
)}) were already required when their respective plugin was loaded, some plugins might not work. Make sure the SDK is setup before you require in other modules.`
);
}
// Enable the require hook.
hook(modulesToHook, (exports, name, baseDir) => {
if (this._hookState !== HookState.ENABLED) return exports;
const config = pluginsToLoad[name];
const modulePath = config.path!;
let version = null;
if (!baseDir) {
// basedir is the directory where the module is located,
// or undefined for core modules.
// lets plugins restrict what they support for core modules (see plugin.supportedVersions)
version = process.versions.node;
} else {
// Get the module version.
version = utils.getPackageVersion(this.logger, baseDir);
}
// Skip loading of all modules if '*' is provided
if (modulesToIgnore[0] === DISABLE_ALL_PLUGINS) {
this.logger.info(
`PluginLoader#load: skipped patching module ${name} because all plugins are disabled (${modulesToIgnore.join(
','
)})`
);
return exports;
}
if (modulesToIgnore.includes(name)) {
this.logger.info(
`PluginLoader#load: skipped patching module ${name} because it was on the ignore list (${modulesToIgnore.join(
','
)})`
);
return exports;
}
this.logger.info(
`PluginLoader#load: trying to load ${name}@${version}`
);
if (!version) return exports;
this.logger.debug(
`PluginLoader#load: applying patch to ${name}@${version} using ${modulePath} module`
);
// Expecting a plugin from module;
try {
const plugin: Plugin = require(modulePath).plugin;
if (!utils.isSupportedVersion(version, plugin.supportedVersions)) {
this.logger.warn(
`PluginLoader#load: Plugin ${name} only supports module ${plugin.moduleName} with the versions: ${plugin.supportedVersions}`
);
return exports;
}
if (plugin.moduleName !== name) {
this.logger.error(
`PluginLoader#load: Entry ${name} use a plugin that instruments ${plugin.moduleName}`
);
return exports;
}
this._plugins.push(plugin);
// Enable each supported plugin.
return plugin.enable(exports, this.provider, this.logger, config);
} catch (e) {
this.logger.error(
`PluginLoader#load: could not load plugin ${modulePath} of module ${name}. Error: ${e.message}`
);
return exports;
}
});
this._hookState = HookState.ENABLED;
} else if (this._hookState === HookState.DISABLED) {
this.logger.error(
'PluginLoader#load: Currently cannot re-enable plugin loader.'
);
} else {
this.logger.error('PluginLoader#load: Plugin loader already enabled.');
}
return this;
}
/** Unloads plugins. */
unload(): PluginLoader {
if (this._hookState === HookState.ENABLED) {
for (const plugin of this._plugins) {
plugin.disable();
}
this._plugins = [];
this._hookState = HookState.DISABLED;
}
return this;
}
}
/**
* Adds a search path for plugin modules. Intended for testing purposes only.
* @param searchPath The path to add.
*/
export function searchPathForTest(searchPath: string) {
module.paths.push(searchPath);
}

View File

@ -1,32 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare module 'require-in-the-middle' {
namespace hook {
type Options = {
internals?: boolean;
};
type OnRequireFn = <T>(exports: T, name: string, basedir?: string) => T;
}
function hook(
modules: string[] | null,
options: hook.Options | null,
onRequire: hook.OnRequireFn
): void;
function hook(modules: string[] | null, onRequire: hook.OnRequireFn): void;
function hook(onRequire: hook.OnRequireFn): void;
export = hook;
}

View File

@ -1,74 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Logger } from '@opentelemetry/api';
import * as path from 'path';
import * as semver from 'semver';
/**
* Gets the package version.
* @param logger The logger to use.
* @param basedir The base directory.
*/
export function getPackageVersion(
logger: Logger,
basedir: string
): string | null {
const pjsonPath = path.join(basedir, 'package.json');
try {
const version = require(pjsonPath).version;
// Attempt to parse a string as a semantic version, returning either a
// SemVer object or null.
if (!semver.parse(version)) {
logger.error(
`getPackageVersion: [${pjsonPath}|${version}] Version string could not be parsed.`
);
return null;
}
return version;
} catch (e) {
logger.error(
`getPackageVersion: [${pjsonPath}] An error occurred while retrieving version string. ${e.message}`
);
return null;
}
}
/**
* Determines if a version is supported
* @param moduleVersion a version in [semver](https://semver.org/spec/v2.0.0.html) format.
* @param [supportedVersions] a list of supported versions ([semver](https://semver.org/spec/v2.0.0.html) format).
*/
export function isSupportedVersion(
moduleVersion: string,
supportedVersions?: string[]
) {
if (!Array.isArray(supportedVersions) || supportedVersions.length === 0) {
return true;
}
return supportedVersions.some(supportedVersion =>
semver.satisfies(moduleVersion, supportedVersion)
);
}
/**
* Adds a search path for plugin modules. Intended for testing purposes only.
* @param searchPath The path to add.
*/
export function searchPathForTest(searchPath: string) {
module.paths.push(searchPath);
}

View File

@ -31,6 +31,7 @@ import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks';
import { Span } from '@opentelemetry/tracing';
import { Resource, TELEMETRY_SDK_RESOURCE } from '@opentelemetry/resources';
import * as assert from 'assert';
import * as sinon from 'sinon';
import * as path from 'path';
import { ContextManager, ROOT_CONTEXT } from '@opentelemetry/context-base';
import { NodeTracerProvider } from '../src/NodeTracerProvider';
@ -61,7 +62,6 @@ describe('NodeTracerProvider', () => {
afterEach(() => {
// clear require cache
Object.keys(require.cache).forEach(key => delete require.cache[key]);
provider.stop();
contextManager.disable();
context.disable();
});
@ -86,38 +86,18 @@ describe('NodeTracerProvider', () => {
assert.ok(provider instanceof NodeTracerProvider);
});
it('should load a merge of user configured and default plugins and implictly enable non-default plugins', () => {
provider = new NodeTracerProvider({
logger: new NoopLogger(),
plugins: {
'simple-module': {
path: '@opentelemetry/plugin-simple-module',
},
'supported-module': {
path: '@opentelemetry/plugin-supported-module',
enhancedDatabaseReporting: false,
ignoreMethods: [],
ignoreUrls: [],
},
'random-module': {
enabled: false,
path: '@opentelemetry/random-module',
},
http: {
path: '@opentelemetry/plugin-http-module',
},
},
});
const plugins = provider['_pluginLoader']['_plugins'];
assert.strictEqual(plugins.length, 0);
require('simple-module');
assert.strictEqual(plugins.length, 1);
require('supported-module');
assert.strictEqual(plugins.length, 2);
require('random-module');
assert.strictEqual(plugins.length, 2);
require('http');
assert.strictEqual(plugins.length, 3);
it('should show warning when plugins are defined', () => {
const dummyPlugin1 = {};
const spyWarn = sinon.spy(console, 'warn');
const plugins = [dummyPlugin1];
const options = { plugins };
provider = new NodeTracerProvider(options);
assert.strictEqual(
spyWarn.args[0][0],
'plugins options was removed, please use "registerInstrumentations" to load plugins'
);
});
});
@ -264,52 +244,3 @@ describe('NodeTracerProvider', () => {
});
});
});
describe('mergePlugins', () => {
const defaultPlugins = {
module1: {
enabled: true,
path: 'testpath',
},
module2: {
enabled: true,
path: 'testpath2',
},
module3: {
enabled: true,
path: 'testpath3',
},
};
const userPlugins = {
module2: {
path: 'userpath',
},
module3: {
enabled: false,
},
nonDefaultModule: {
path: 'userpath2',
},
};
const provider = new NodeTracerProvider();
const mergedPlugins = provider['_mergePlugins'](defaultPlugins, userPlugins);
it('should merge user and default configs', () => {
assert.equal(mergedPlugins.module1.enabled, true);
assert.equal(mergedPlugins.module1.path, 'testpath');
assert.equal(mergedPlugins.module2.enabled, true);
assert.equal(mergedPlugins.module2.path, 'userpath');
assert.equal(mergedPlugins.module3.enabled, false);
assert.equal(mergedPlugins.nonDefaultModule.enabled, true);
assert.equal(mergedPlugins.nonDefaultModule.path, 'userpath2');
});
it('should should not mangle default config', () => {
assert.equal(defaultPlugins.module2.path, 'testpath2');
assert.equal(defaultPlugins.module3.enabled, true);
assert.equal(defaultPlugins.module3.path, 'testpath3');
});
});

View File

@ -1,363 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NoopTracerProvider, NoopLogger } from '@opentelemetry/api';
import * as assert from 'assert';
import * as path from 'path';
import {
HookState,
PluginLoader,
Plugins,
searchPathForTest,
} from '../../src/instrumentation/PluginLoader';
const INSTALLED_PLUGINS_PATH = path.join(__dirname, 'node_modules');
/* eslint-disable node/no-extraneous-require */
const simplePlugins: Plugins = {
'simple-module': {
enabled: true,
path: '@opentelemetry/plugin-simple-module',
ignoreMethods: [],
ignoreUrls: [],
},
};
const httpPlugins: Plugins = {
http: {
enabled: true,
path: '@opentelemetry/plugin-http-module',
ignoreMethods: [],
ignoreUrls: [],
},
};
const disablePlugins: Plugins = {
'simple-module': {
enabled: false,
path: '@opentelemetry/plugin-simple-module',
},
nonexistent: {
enabled: false,
path: '@opentelemetry/plugin-nonexistent-module',
},
};
const nonexistentPlugins: Plugins = {
nonexistent: {
enabled: true,
path: '@opentelemetry/plugin-nonexistent-module',
},
};
const missingPathPlugins: Plugins = {
'simple-module': {
enabled: true,
},
nonexistent: {
enabled: true,
},
};
const supportedVersionPlugins: Plugins = {
'supported-module': {
enabled: true,
path: '@opentelemetry/plugin-supported-module',
},
};
const notSupportedVersionPlugins: Plugins = {
'notsupported-module': {
enabled: true,
path: 'notsupported-module',
},
};
const alreadyRequiredPlugins: Plugins = {
'already-require-module': {
enabled: true,
path: '@opentelemetry/plugin-supported-module',
},
};
const differentNamePlugins: Plugins = {
'random-module': {
enabled: true,
path: '@opentelemetry/plugin-http-module',
},
};
describe('PluginLoader', () => {
const provider = new NoopTracerProvider();
const logger = new NoopLogger();
before(() => {
module.paths.push(INSTALLED_PLUGINS_PATH);
searchPathForTest(INSTALLED_PLUGINS_PATH);
});
afterEach(() => {
// clear require cache
Object.keys(require.cache).forEach(key => delete require.cache[key]);
});
describe('.state()', () => {
it('returns UNINITIALIZED when first called', () => {
const pluginLoader = new PluginLoader(provider, logger);
assert.strictEqual(pluginLoader['_hookState'], HookState.UNINITIALIZED);
});
it('transitions from UNINITIALIZED to ENABLED', () => {
const pluginLoader = new PluginLoader(provider, logger);
pluginLoader.load(simplePlugins);
assert.strictEqual(pluginLoader['_hookState'], HookState.ENABLED);
pluginLoader.unload();
});
it('transitions from ENABLED to DISABLED', () => {
const pluginLoader = new PluginLoader(provider, logger);
pluginLoader.load(simplePlugins).unload();
assert.strictEqual(pluginLoader['_hookState'], HookState.DISABLED);
});
});
describe('.load()', () => {
afterEach(() => {
delete process.env['OTEL_NO_PATCH_MODULES'];
});
it('sanity check', () => {
// Ensure that module fixtures contain values that we expect.
const simpleModule = require('simple-module');
const simpleModule001 = require('supported-module');
const simpleModule100 = require('notsupported-module');
assert.strictEqual(simpleModule.name(), 'simple-module');
assert.strictEqual(simpleModule001.name(), 'supported-module');
assert.strictEqual(simpleModule100.name(), 'notsupported-module');
assert.strictEqual(simpleModule.value(), 0);
assert.strictEqual(simpleModule001.value(), 0);
assert.strictEqual(simpleModule100.value(), 0);
assert.throws(() => require('nonexistent-module'));
});
it('should not load a plugin on the ignore list environment variable', () => {
// Set ignore list env var
process.env['OTEL_NO_PATCH_MODULES'] = 'simple-module';
const pluginLoader = new PluginLoader(provider, logger);
pluginLoader.load({ ...simplePlugins, ...supportedVersionPlugins });
assert.strictEqual(pluginLoader['_plugins'].length, 0);
const simpleModule = require('simple-module');
assert.strictEqual(pluginLoader['_plugins'].length, 0);
assert.strictEqual(simpleModule.value(), 0);
assert.strictEqual(simpleModule.name(), 'simple-module');
const supportedModule = require('supported-module');
assert.strictEqual(pluginLoader['_plugins'].length, 1);
assert.strictEqual(supportedModule.value(), 1);
assert.strictEqual(supportedModule.name(), 'patched-supported-module');
pluginLoader.unload();
});
it('should not load plugins on the ignore list environment variable', () => {
// Set ignore list env var
process.env['OTEL_NO_PATCH_MODULES'] = 'simple-module,http';
const pluginLoader = new PluginLoader(provider, logger);
pluginLoader.load({
...simplePlugins,
...supportedVersionPlugins,
...httpPlugins,
});
assert.strictEqual(pluginLoader['_plugins'].length, 0);
const simpleModule = require('simple-module');
assert.strictEqual(pluginLoader['_plugins'].length, 0);
assert.strictEqual(simpleModule.value(), 0);
assert.strictEqual(simpleModule.name(), 'simple-module');
const httpModule = require('http');
assert.ok(httpModule);
assert.strictEqual(pluginLoader['_plugins'].length, 0);
const supportedModule = require('supported-module');
assert.strictEqual(pluginLoader['_plugins'].length, 1);
assert.strictEqual(supportedModule.value(), 1);
assert.strictEqual(supportedModule.name(), 'patched-supported-module');
pluginLoader.unload();
});
it('should not load any plugins if ignore list environment variable is set to "*"', () => {
// Set ignore list env var
process.env['OTEL_NO_PATCH_MODULES'] = '*';
const pluginLoader = new PluginLoader(provider, logger);
pluginLoader.load({
...simplePlugins,
...supportedVersionPlugins,
...httpPlugins,
});
assert.strictEqual(pluginLoader['_plugins'].length, 0);
const simpleModule = require('simple-module');
const httpModule = require('http');
const supportedModule = require('supported-module');
assert.strictEqual(
pluginLoader['_plugins'].length,
0,
'No plugins were loaded'
);
assert.strictEqual(simpleModule.value(), 0);
assert.strictEqual(simpleModule.name(), 'simple-module');
assert.ok(httpModule);
assert.strictEqual(supportedModule.value(), 0);
assert.strictEqual(supportedModule.name(), 'supported-module');
pluginLoader.unload();
});
it('should load a plugin and patch the target modules', () => {
const pluginLoader = new PluginLoader(provider, logger);
assert.strictEqual(pluginLoader['_plugins'].length, 0);
pluginLoader.load(simplePlugins);
// The hook is only called the first time the module is loaded.
const simpleModule = require('simple-module');
assert.strictEqual(pluginLoader['_plugins'].length, 1);
assert.strictEqual(simpleModule.value(), 1);
assert.strictEqual(simpleModule.name(), 'patched-simple-module');
pluginLoader.unload();
});
it('should load a plugin and patch the core module', () => {
const pluginLoader = new PluginLoader(provider, logger);
assert.strictEqual(pluginLoader['_plugins'].length, 0);
pluginLoader.load(httpPlugins);
// The hook is only called the first time the module is loaded.
const httpModule = require('http');
assert.strictEqual(pluginLoader['_plugins'].length, 1);
assert.strictEqual(httpModule.get(), 'patched');
pluginLoader.unload();
});
// @TODO: simplify this test once we can load module with custom path
it('should not load the plugin when supported versions does not match', () => {
const pluginLoader = new PluginLoader(provider, logger);
assert.strictEqual(pluginLoader['_plugins'].length, 0);
pluginLoader.load(notSupportedVersionPlugins);
// The hook is only called the first time the module is loaded.
require('notsupported-module');
assert.strictEqual(pluginLoader['_plugins'].length, 0);
pluginLoader.unload();
});
// @TODO: simplify this test once we can load module with custom path
it('should load a plugin and patch the target modules when supported versions match', () => {
const pluginLoader = new PluginLoader(provider, logger);
assert.strictEqual(pluginLoader['_plugins'].length, 0);
pluginLoader.load(supportedVersionPlugins);
// The hook is only called the first time the module is loaded.
const simpleModule = require('supported-module');
assert.strictEqual(pluginLoader['_plugins'].length, 1);
assert.strictEqual(simpleModule.value(), 1);
assert.strictEqual(simpleModule.name(), 'patched-supported-module');
pluginLoader.unload();
});
it('should not load a plugin when value is false', () => {
const pluginLoader = new PluginLoader(provider, logger);
assert.strictEqual(pluginLoader['_plugins'].length, 0);
pluginLoader.load(disablePlugins);
const simpleModule = require('simple-module');
assert.strictEqual(pluginLoader['_plugins'].length, 0);
assert.strictEqual(simpleModule.value(), 0);
assert.strictEqual(simpleModule.name(), 'simple-module');
pluginLoader.unload();
});
it('should not load a plugin when value is true but path is missing', () => {
const pluginLoader = new PluginLoader(provider, logger);
assert.strictEqual(pluginLoader['_plugins'].length, 0);
pluginLoader.load(missingPathPlugins);
const simpleModule = require('simple-module');
assert.strictEqual(pluginLoader['_plugins'].length, 0);
assert.strictEqual(simpleModule.value(), 0);
assert.strictEqual(simpleModule.name(), 'simple-module');
pluginLoader.unload();
});
it('should not load a non existing plugin', () => {
const pluginLoader = new PluginLoader(provider, logger);
assert.strictEqual(pluginLoader['_plugins'].length, 0);
pluginLoader.load(nonexistentPlugins);
assert.strictEqual(pluginLoader['_plugins'].length, 0);
pluginLoader.unload();
});
it("doesn't patch modules for which plugins aren't specified", () => {
const pluginLoader = new PluginLoader(provider, logger);
pluginLoader.load({});
assert.strictEqual(require('simple-module').value(), 0);
pluginLoader.unload();
});
it('should warn when module was already loaded', callback => {
const verifyWarnLogger = {
error: logger.error,
info: logger.info,
debug: logger.debug,
warn: (message: string, ...args: unknown[]) => {
assert(message.match(/were already required when/));
assert(message.match(/(already-require-module)/));
return callback();
},
};
require('already-require-module');
const pluginLoader = new PluginLoader(provider, verifyWarnLogger);
pluginLoader.load(alreadyRequiredPlugins);
pluginLoader.unload();
});
it('should not load a plugin that patches a different module that the one configured', () => {
const pluginLoader = new PluginLoader(provider, logger);
assert.strictEqual(pluginLoader['_plugins'].length, 0);
pluginLoader.load(differentNamePlugins);
require('random-module');
assert.strictEqual(pluginLoader['_plugins'].length, 0);
pluginLoader.unload();
});
});
describe('.unload()', () => {
it('should unload the plugins and unpatch the target module when unloads', () => {
const pluginLoader = new PluginLoader(provider, logger);
assert.strictEqual(pluginLoader['_plugins'].length, 0);
pluginLoader.load(simplePlugins);
// The hook is only called the first time the module is loaded.
const simpleModule = require('simple-module');
assert.strictEqual(pluginLoader['_plugins'].length, 1);
assert.strictEqual(simpleModule.value(), 1);
assert.strictEqual(simpleModule.name(), 'patched-simple-module');
pluginLoader.unload();
assert.strictEqual(pluginLoader['_plugins'].length, 0);
assert.strictEqual(simpleModule.name(), 'simple-module');
assert.strictEqual(simpleModule.value(), 0);
});
});
});

View File

@ -1,22 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@opentelemetry/core");
const shimmer = require("shimmer");
class HttpModulePlugin extends core_1.BasePlugin {
constructor() {
super();
this.moduleName = 'http';
}
patch() {
shimmer.wrap(this._moduleExports, 'get', orig => () => 'patched');
return this._moduleExports;
}
unpatch() {
shimmer.unwrap(this._moduleExports, 'get');
}
}
exports.HttpModulePlugin = HttpModulePlugin;
const plugin = new HttpModulePlugin();
exports.plugin = plugin;

View File

@ -1,5 +0,0 @@
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
__export(require("./http-module"));

View File

@ -1,4 +0,0 @@
{
"name": "@opentelemetry/plugin-http-module",
"version": "0.0.1"
}

View File

@ -1,5 +0,0 @@
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
__export(require("./simple-module"));

View File

@ -1,4 +0,0 @@
{
"name": "@opentelemetry/plugin-notsupported-module",
"version": "1.0.0"
}

View File

@ -1,25 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@opentelemetry/core");
const shimmer = require("shimmer");
class SimpleModulePlugin extends core_1.BasePlugin {
constructor() {
super();
this.moduleName = 'notsupported-module';
}
patch() {
shimmer.wrap(this._moduleExports, 'name', orig => () => 'patched-' + orig.apply());
shimmer.wrap(this._moduleExports, 'value', orig => () => orig.apply() + 1);
return this._moduleExports;
}
unpatch() {
shimmer.unwrap(this._moduleExports, 'name');
shimmer.unwrap(this._moduleExports, 'value');
}
}
exports.SimpleModulePlugin = SimpleModulePlugin;
const plugin = new SimpleModulePlugin();
plugin.supportedVersions = ['1.0.0'];
exports.plugin = plugin;

View File

@ -1,5 +0,0 @@
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
__export(require("./simple-module"));

View File

@ -1,4 +0,0 @@
{
"name": "@opentelemetry/plugin-simple-module",
"version": "0.0.1"
}

View File

@ -1,24 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@opentelemetry/core");
const shimmer = require("shimmer");
class SimpleModulePlugin extends core_1.BasePlugin {
constructor() {
super();
this.moduleName = 'simple-module';
}
patch() {
shimmer.wrap(this._moduleExports, 'name', orig => () => 'patched-' + orig.apply());
shimmer.wrap(this._moduleExports, 'value', orig => () => orig.apply() + 1);
return this._moduleExports;
}
unpatch() {
shimmer.unwrap(this._moduleExports, 'name');
shimmer.unwrap(this._moduleExports, 'value');
}
}
exports.SimpleModulePlugin = SimpleModulePlugin;
const plugin = new SimpleModulePlugin();
exports.plugin = plugin;

View File

@ -1,5 +0,0 @@
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
__export(require("./simple-module"));

View File

@ -1,4 +0,0 @@
{
"name": "@opentelemetry/plugin-supported-module",
"version": "0.0.1"
}

View File

@ -1,25 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@opentelemetry/core");
const shimmer = require("shimmer");
class SimpleModulePlugin extends core_1.BasePlugin {
constructor() {
super();
this.moduleName = 'supported-module';
}
patch() {
shimmer.wrap(this._moduleExports, 'name', orig => () => 'patched-' + orig.apply());
shimmer.wrap(this._moduleExports, 'value', orig => () => orig.apply() + 1);
return this._moduleExports;
}
unpatch() {
shimmer.unwrap(this._moduleExports, 'name');
shimmer.unwrap(this._moduleExports, 'value');
}
}
exports.SimpleModulePlugin = SimpleModulePlugin;
const plugin = new SimpleModulePlugin();
plugin.supportedVersions = ['^0.0.1'];
exports.plugin = plugin;

View File

@ -1,4 +0,0 @@
module.exports = {
name: () => 'already-module',
value: () => 0,
};

View File

@ -1,4 +0,0 @@
{
"name": "already-module",
"version": "0.1.0"
}

View File

@ -1,4 +0,0 @@
module.exports = {
name: () => 'notsupported-module',
value: () => 0,
};

View File

@ -1,4 +0,0 @@
{
"name": "notsupported-module",
"version": "0.0.1"
}

View File

@ -1,4 +0,0 @@
module.exports = {
name: () => 'random-module',
value: () => 0,
};

View File

@ -1,4 +0,0 @@
{
"name": "random-module",
"version": "0.1.0"
}

View File

@ -1,4 +0,0 @@
module.exports = {
name: () => 'simple-module',
value: () => 0,
};

View File

@ -1,4 +0,0 @@
{
"name": "simple-module",
"version": "0.1.0"
}

View File

@ -1,4 +0,0 @@
module.exports = {
name: () => 'supported-module',
value: () => 0,
};

View File

@ -1,4 +0,0 @@
{
"name": "supported-module",
"version": "0.0.1"
}

View File

@ -1,95 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NoopLogger } from '@opentelemetry/api';
import * as assert from 'assert';
import * as path from 'path';
import * as utils from '../../src/instrumentation/utils';
const INSTALLED_PLUGINS_PATH = path.join(__dirname, 'node_modules');
const TEST_MODULES: Array<{ name: string; version: string | null }> = [
{
name: 'simple-module',
version: '0.1.0',
},
{
name: 'nonexistent-module',
version: null,
},
{
name: 'http',
version: null,
},
];
describe('Instrumentation#utils', () => {
const logger = new NoopLogger();
before(() => {
utils.searchPathForTest(INSTALLED_PLUGINS_PATH);
});
describe('getPackageVersion', () => {
TEST_MODULES.forEach(testCase => {
it(`should return ${testCase.version} for ${testCase.name}`, () => {
assert.strictEqual(
utils.getPackageVersion(logger, testCase.name),
testCase.version
);
});
});
});
describe('isSupportedVersion', () => {
const version = '1.0.1';
it('should return true when supportedVersions is not defined', () => {
assert.strictEqual(utils.isSupportedVersion('1.0.0', undefined), true);
});
[
['1.X'],
[version],
['1.X.X', '3.X.X'],
['^1.0.0'],
['~1.0.0', '^0.1.0'],
['*'],
['>1.0.0'],
[],
].forEach(supportedVersion => {
it(`should return true when version is equal to ${version} and supportedVersions is equal to ${supportedVersion}`, () => {
assert.strictEqual(
utils.isSupportedVersion(version, supportedVersion),
true
);
});
});
[['0.X'], ['0.1.0'], ['0.X.X'], ['^0.1.0'], ['1.0.0'], ['<1.0.0']].forEach(
supportedVersion => {
it(`should return false when version is equal to ${version} and supportedVersions is equal to ${supportedVersion}`, () => {
assert.strictEqual(
utils.isSupportedVersion(version, supportedVersion),
false
);
});
}
);
it("should return false when version is equal to null and supportedVersions is equal to '*'", () => {
assert.strictEqual(utils.isSupportedVersion(null as any, ['*']), false);
});
});
});

View File

@ -44,6 +44,7 @@
"@opentelemetry/api-metrics": "^0.16.0",
"@opentelemetry/context-base": "^0.16.0",
"@opentelemetry/core": "^0.16.0",
"@opentelemetry/instrumentation": "^0.16.0",
"@opentelemetry/metrics": "^0.16.0",
"@opentelemetry/node": "^0.16.0",
"@opentelemetry/resource-detector-aws": "^0.16.0",

View File

@ -18,6 +18,10 @@ import { TextMapPropagator } from '@opentelemetry/api';
import { metrics } from '@opentelemetry/api-metrics';
import { ContextManager } from '@opentelemetry/context-base';
import { MeterConfig, MeterProvider } from '@opentelemetry/metrics';
import {
InstrumentationOption,
registerInstrumentations,
} from '@opentelemetry/instrumentation';
import { NodeTracerConfig, NodeTracerProvider } from '@opentelemetry/node';
import { awsEc2Detector } from '@opentelemetry/resource-detector-aws';
import { gcpDetector } from '@opentelemetry/resource-detector-gcp';
@ -39,6 +43,7 @@ export class NodeSDK {
contextManager?: ContextManager;
textMapPropagator?: TextMapPropagator;
};
private _instrumentations: InstrumentationOption[];
private _meterProviderConfig?: MeterConfig;
private _resource: Resource;
@ -65,9 +70,6 @@ export class NodeSDK {
if (configuration.logger) {
tracerProviderConfig.logger = configuration.logger;
}
if (configuration.plugins) {
tracerProviderConfig.plugins = configuration.plugins;
}
if (configuration.sampler) {
tracerProviderConfig.sampler = configuration.sampler;
}
@ -108,8 +110,16 @@ export class NodeSDK {
this.configureMeterProvider(meterConfig);
}
}
let instrumentations: InstrumentationOption[] = [];
if (configuration.instrumentations) {
instrumentations = configuration.instrumentations;
} else if (configuration.plugins) {
console.error('plugins option is deprecated');
instrumentations = configuration.plugins;
}
this._instrumentations = instrumentations;
}
/** Set configurations required to register a NodeTracerProvider */
public configureTracerProvider(
tracerConfig: NodeTracerConfig,
@ -178,6 +188,12 @@ export class NodeSDK {
metrics.setGlobalMeterProvider(meterProvider);
}
registerInstrumentations({
instrumentations: this._instrumentations,
tracerProvider: this._tracerProvider,
meterProvider: this._meterProvider,
});
}
public shutdown(): Promise<void> {

View File

@ -15,7 +15,8 @@
*/
import type { ContextManager } from '@opentelemetry/context-base';
import type { api, core, metrics, node, resources, tracing } from '.';
import type { api, core, metrics, resources, tracing } from '.';
import { InstrumentationOption } from '@opentelemetry/instrumentation';
export interface NodeSDKConfiguration {
autoDetectResources: boolean;
@ -27,7 +28,9 @@ export interface NodeSDKConfiguration {
metricProcessor: metrics.Processor;
metricExporter: metrics.MetricExporter;
metricInterval: number;
plugins: node.Plugins;
/* Deprecated */
plugins: InstrumentationOption[];
instrumentations: InstrumentationOption[];
resource: resources.Resource;
sampler: api.Sampler;
spanProcessor: tracing.SpanProcessor;

View File

@ -31,7 +31,7 @@ import { NoopContextManager } from '@opentelemetry/context-base';
import { CompositePropagator } from '@opentelemetry/core';
import { ConsoleMetricExporter, MeterProvider } from '@opentelemetry/metrics';
import { NodeTracerProvider } from '@opentelemetry/node';
import * as NodeConfig from '@opentelemetry/node/build/src/config';
import * as NodeConfig from '@opentelemetry/instrumentation/build/src/platform/node/old/autoLoader';
import { awsEc2Detector } from '@opentelemetry/resource-detector-aws';
import { resetIsAvailableCache } from '@opentelemetry/resource-detector-gcp';
import { Resource } from '@opentelemetry/resources';

View File

@ -24,6 +24,9 @@
{
"path": "../opentelemetry-core"
},
{
"path": "../opentelemetry-instrumentation"
},
{
"path": "../opentelemetry-metrics"
},

View File

@ -16,7 +16,7 @@ This package exposes a class `WebTracerProvider` that will be able to automatica
See the example how to use it.
OpenTelemetry comes with a growing number of instrumentation plugins for well know modules (see [supported modules](https://github.com/open-telemetry/opentelemetry-js#plugins)) and an API to create custom plugins (see [the plugin developer guide](https://github.com/open-telemetry/opentelemetry-js/blob/main/doc/plugin-guide.md)).
OpenTelemetry comes with a growing number of instrumentations for well know modules (see [supported modules](https://github.com/open-telemetry/opentelemetry-js#plugins)) and an API to create custom instrumentations (see [the instrumentation developer guide](https://github.com/open-telemetry/opentelemetry-js/blob/main/doc/instrumentation-guide.md)).
Web Tracer currently supports one plugin for document load.
Unlike Node Tracer (`NodeTracerProvider`), the plugins needs to be initialised and passed in configuration.
@ -37,29 +37,24 @@ import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/tracing
import { WebTracerProvider } from '@opentelemetry/web';
import { DocumentLoad } from '@opentelemetry/plugin-document-load';
import { ZoneContextManager } from '@opentelemetry/context-zone';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
// Minimum required setup - supports only synchronous operations
const provider = new WebTracerProvider({
plugins: [
new DocumentLoad()
]
});
const provider = new WebTracerProvider();
provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));
provider.register();
const providerWithZone = new WebTracerProvider({
plugins: [
new DocumentLoad()
]
});
providerWithZone.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));
// Changing default contextManager to use ZoneContextManager - supports asynchronous operations
providerWithZone.register({
provider.register({
// Changing default contextManager to use ZoneContextManager - supports asynchronous operations - optional
contextManager: new ZoneContextManager(),
});
// Registering instrumentations / plugins
registerInstrumentations({
instrumentations: [
new DocumentLoad(),
],
tracerProvider: provider,
});
```
## Useful links

View File

@ -77,7 +77,6 @@
"@opentelemetry/api": "^0.16.0",
"@opentelemetry/context-base": "^0.16.0",
"@opentelemetry/core": "^0.16.0",
"@opentelemetry/instrumentation": "^0.16.0",
"@opentelemetry/semantic-conventions": "^0.16.0",
"@opentelemetry/tracing": "^0.16.0"
}

View File

@ -14,8 +14,6 @@
* limitations under the License.
*/
import { BasePlugin } from '@opentelemetry/core';
import { InstrumentationBase } from '@opentelemetry/instrumentation';
import {
BasicTracerProvider,
SDKRegistrationConfig,
@ -30,7 +28,7 @@ export interface WebTracerConfig extends TracerConfig {
/**
* plugins to be used with tracer, they will be enabled automatically
*/
plugins?: (BasePlugin<unknown> | InstrumentationBase)[];
plugins?: unknown[];
}
/**
@ -42,21 +40,14 @@ export class WebTracerProvider extends BasicTracerProvider {
* @param config Web Tracer config
*/
constructor(config: WebTracerConfig = {}) {
if (typeof config.plugins === 'undefined') {
config.plugins = [];
if (typeof config.plugins !== 'undefined') {
console.warn(
'plugins option was removed, please use' +
' "registerInstrumentations" to load plugins'
);
}
super(config);
for (const plugin of config.plugins) {
const instrumentation = (plugin as unknown) as InstrumentationBase;
if (typeof instrumentation.setTracerProvider === 'function') {
instrumentation.setTracerProvider(this);
instrumentation.enable();
} else {
plugin.enable([], this, this.logger);
}
}
if ((config as SDKRegistrationConfig).contextManager) {
throw (
'contextManager should be defined in register method not in' +

View File

@ -17,8 +17,6 @@
import { context, NoopLogger, getSpan, setSpan } from '@opentelemetry/api';
import { ContextManager } from '@opentelemetry/context-base';
import { ZoneContextManager } from '@opentelemetry/context-zone';
import { BasePlugin } from '@opentelemetry/core';
import { InstrumentationBase } from '@opentelemetry/instrumentation';
import { B3Propagator } from '@opentelemetry/propagator-b3';
import { Resource, TELEMETRY_SDK_RESOURCE } from '@opentelemetry/resources';
import { Span, Tracer } from '@opentelemetry/tracing';
@ -27,25 +25,6 @@ import * as sinon from 'sinon';
import { WebTracerConfig } from '../src';
import { WebTracerProvider } from '../src/WebTracerProvider';
class DummyPlugin extends BasePlugin<unknown> {
constructor() {
super('dummy');
}
moduleName = 'dummy';
patch() {}
unpatch() {}
}
class DummyInstrumentation extends InstrumentationBase<unknown> {
constructor() {
super('dummy', '1');
}
enable() {}
disable() {}
init() {}
}
describe('WebTracerProvider', () => {
describe('constructor', () => {
let defaultOptions: WebTracerConfig;
@ -69,24 +48,19 @@ describe('WebTracerProvider', () => {
assert.ok(tracer instanceof Tracer);
});
it('should enable all plugins', () => {
const dummyPlugin1 = new DummyPlugin();
const dummyPlugin2 = new DummyPlugin();
const dummyPlugin3 = new DummyInstrumentation();
const spyEnable1 = sinon.spy(dummyPlugin1, 'enable');
const spyEnable2 = sinon.spy(dummyPlugin2, 'enable');
const spyEnable3 = sinon.spy(dummyPlugin3, 'enable');
const spySetTracerProvider = sinon.spy(dummyPlugin3, 'setTracerProvider');
it('should show warning when plugins are defined', () => {
const dummyPlugin1 = {};
const spyWarn = sinon.spy(window.console, 'warn');
const plugins = [dummyPlugin1, dummyPlugin2, dummyPlugin3];
const plugins = [dummyPlugin1];
const options = { plugins };
new WebTracerProvider(options);
assert.ok(spyEnable1.calledOnce === true);
assert.ok(spyEnable2.calledOnce === true);
assert.ok(spyEnable3.calledOnce === true);
assert.ok(spySetTracerProvider.calledOnce === true);
assert.strictEqual(
spyWarn.args[0][0],
'plugins option was removed, please use "registerInstrumentations" to load plugins'
);
});
it('should work without default context manager', () => {

View File

@ -22,9 +22,6 @@
{
"path": "../opentelemetry-core"
},
{
"path": "../opentelemetry-instrumentation"
},
{
"path": "../opentelemetry-propagator-b3"
},