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:
parent
998176399e
commit
9cfa92c4a5
|
@ -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
|
||||
|
|
84
README.md
84
README.md
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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');
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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')) {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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')) {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()));
|
||||
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -17,4 +17,5 @@
|
|||
export * from './autoLoader';
|
||||
export * from './platform/index';
|
||||
export * from './types';
|
||||
export * from './types_internal';
|
||||
export * from './utils';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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' },
|
||||
};
|
||||
|
|
|
@ -15,5 +15,4 @@
|
|||
*/
|
||||
|
||||
export { NodeTracerConfig } from './config';
|
||||
export { Plugins } from './instrumentation/PluginLoader';
|
||||
export * from './NodeTracerProvider';
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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;
|
|
@ -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"));
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"name": "@opentelemetry/plugin-http-module",
|
||||
"version": "0.0.1"
|
||||
}
|
|
@ -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"));
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"name": "@opentelemetry/plugin-notsupported-module",
|
||||
"version": "1.0.0"
|
||||
}
|
|
@ -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;
|
|
@ -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"));
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"name": "@opentelemetry/plugin-simple-module",
|
||||
"version": "0.0.1"
|
||||
}
|
|
@ -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;
|
|
@ -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"));
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"name": "@opentelemetry/plugin-supported-module",
|
||||
"version": "0.0.1"
|
||||
}
|
|
@ -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;
|
|
@ -1,4 +0,0 @@
|
|||
module.exports = {
|
||||
name: () => 'already-module',
|
||||
value: () => 0,
|
||||
};
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"name": "already-module",
|
||||
"version": "0.1.0"
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
module.exports = {
|
||||
name: () => 'notsupported-module',
|
||||
value: () => 0,
|
||||
};
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"name": "notsupported-module",
|
||||
"version": "0.0.1"
|
||||
}
|
4
packages/opentelemetry-node/test/instrumentation/node_modules/random-module/index.js
generated
vendored
4
packages/opentelemetry-node/test/instrumentation/node_modules/random-module/index.js
generated
vendored
|
@ -1,4 +0,0 @@
|
|||
module.exports = {
|
||||
name: () => 'random-module',
|
||||
value: () => 0,
|
||||
};
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"name": "random-module",
|
||||
"version": "0.1.0"
|
||||
}
|
4
packages/opentelemetry-node/test/instrumentation/node_modules/simple-module/index.js
generated
vendored
4
packages/opentelemetry-node/test/instrumentation/node_modules/simple-module/index.js
generated
vendored
|
@ -1,4 +0,0 @@
|
|||
module.exports = {
|
||||
name: () => 'simple-module',
|
||||
value: () => 0,
|
||||
};
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"name": "simple-module",
|
||||
"version": "0.1.0"
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
module.exports = {
|
||||
name: () => 'supported-module',
|
||||
value: () => 0,
|
||||
};
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"name": "supported-module",
|
||||
"version": "0.0.1"
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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",
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
{
|
||||
"path": "../opentelemetry-core"
|
||||
},
|
||||
{
|
||||
"path": "../opentelemetry-instrumentation"
|
||||
},
|
||||
{
|
||||
"path": "../opentelemetry-metrics"
|
||||
},
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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' +
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -22,9 +22,6 @@
|
|||
{
|
||||
"path": "../opentelemetry-core"
|
||||
},
|
||||
{
|
||||
"path": "../opentelemetry-instrumentation"
|
||||
},
|
||||
{
|
||||
"path": "../opentelemetry-propagator-b3"
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue