diff --git a/examples/grpc/tracer.js b/examples/grpc/tracer.js
index cee15c735f..34b03b1030 100644
--- a/examples/grpc/tracer.js
+++ b/examples/grpc/tracer.js
@@ -9,14 +9,7 @@ const { ZipkinExporter } = require('@opentelemetry/exporter-zipkin');
const EXPORTER = process.env.EXPORTER || '';
module.exports = (serviceName) => {
- const provider = new NodeTracerProvider({
- plugins: {
- grpc: {
- enabled: true,
- path: '@opentelemetry/plugin-grpc',
- },
- },
- });
+ const provider = new NodeTracerProvider();
let exporter;
if (EXPORTER.toLowerCase().startsWith('z')) {
diff --git a/packages/opentelemetry-node/README.md b/packages/opentelemetry-node/README.md
index 4dd8d84a74..df932e2ed1 100644
--- a/packages/opentelemetry-node/README.md
+++ b/packages/opentelemetry-node/README.md
@@ -1,4 +1,5 @@
# OpenTelemetry Node SDK
+
[![Gitter chat][gitter-image]][gitter-url]
[![NPM Published Version][npm-img]][npm-url]
[![dependencies][dependencies-image]][dependencies-url]
@@ -10,14 +11,14 @@ This module provides *automated instrumentation and tracing* for Node.js applica
For manual instrumentation see the
[@opentelemetry/tracing](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-tracing) package.
-## How does automated instrumentation work?
+## How auto instrumentation works
+
This package exposes a `NodeTracerProvider` that will automatically hook into the module loader of Node.js.
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/master/doc/plugin-guide.md)).
-
Whenever a module is loaded `NodeTracerProvider` will check if a matching instrumentation plugin has been installed.
> **Please note:** This module does *not* bundle any plugins. They need to be installed separately.
@@ -26,6 +27,7 @@ If the respective plugin was found, it will be used to patch the original module
This is done by wrapping all tracing-relevant functions.
This instrumentation code will automatically
+
- extract a trace-context identifier from inbound requests to allow distributed tracing (if applicable)
- make sure that this current trace-context is propagated while the transaction traverses an application (see [@opentelemetry/context-base](https://github.com/open-telemetry/opentelemetry-js/blob/master/packages/opentelemetry-context-base/README.md) for an in-depth explanation)
- add this trace-context identifier to outbound requests to allow continuing the distributed trace on the next hop (if applicable)
@@ -34,6 +36,7 @@ This instrumentation code will automatically
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/master/packages/opentelemetry-tracing), allowing creating custom spans if needed.
## Installation
@@ -49,22 +52,16 @@ npm install --save @opentelemetry/plugin-https
## Usage
-The following code will configure the `NodeTracerProvider` to instrument `http` using `@opentelemetry/plugin-http`.
+The following code will configure the `NodeTracerProvider` to instrument `http`
+(and any other installed [supported
+modules](https://github.com/open-telemetry/opentelemetry-js#plugins))
+using `@opentelemetry/plugin-http`.
```js
const { NodeTracerProvider } = require('@opentelemetry/node');
// Create and configure NodeTracerProvider
-const provider = new NodeTracerProvider({
- plugins: {
- http: {
- enabled: true,
- // You may use a package name or absolute path to the file.
- path: '@opentelemetry/plugin-http',
- // http plugin options
- }
- }
-});
+const provider = new NodeTracerProvider();
// Initialize the provider
provider.register()
@@ -74,26 +71,43 @@ provider.register()
const http = require('http');
```
-To enable instrumentation for all [supported modules](https://github.com/open-telemetry/opentelemetry-js#plugins), create an instance of `NodeTracerProvider` without providing any plugin configuration to the constructor.
+## Plugin configuration
-```js
-const { NodeTracerProvider } = require('@opentelemetry/node');
+User supplied plugin configuration is merged with the default plugin
+configuration. Furthermore, custom plugins that are configured are implicitly
+enabled just as default plugins are.
-// Create and initialize NodeTracerProvider
-const provider = new NodeTracerProvider();
+In the following example:
-// Initialize the provider
-provider.register()
+- the default express plugin is disabled
+- the http plugin has a custom config for a `requestHook`
+- the customPlugin is loaded from the user supplied path
+- all default plugins are still loaded if installed.
-// Your application code
-// ...
+```js
+const provider = new NodeTracerProvider({
+ plugins: {
+ express: {
+ enabled: false,
+ },
+ http: {
+ requestHook: (span, request) => {
+ span.setAttribute("custom request hook attribute", "request");
+ },
+ },
+ customPlugin: {
+ path: "/path/to/custom/module",
+ },
+ },
+});
```
## Examples
-See how to automatically instrument [http](https://github.com/open-telemetry/opentelemetry-js/tree/master/examples/http) and [gRPC](https://github.com/open-telemetry/opentelemetry-js/tree/master/examples/grpc) using node-sdk.
+See how to automatically instrument [http](https://github.com/open-telemetry/opentelemetry-js/tree/master/examples/http) and [gRPC](https://github.com/open-telemetry/opentelemetry-js/tree/master/examples/grpc) using node-sdk.
## Useful links
+
- For more information on OpenTelemetry, visit:
- For more about OpenTelemetry JavaScript:
- For help or feedback on this project, join us on [gitter][gitter-url]
diff --git a/packages/opentelemetry-node/src/NodeTracerProvider.ts b/packages/opentelemetry-node/src/NodeTracerProvider.ts
index 11a0874e5e..6d7f51737b 100644
--- a/packages/opentelemetry-node/src/NodeTracerProvider.ts
+++ b/packages/opentelemetry-node/src/NodeTracerProvider.ts
@@ -20,7 +20,7 @@ import {
SDKRegistrationConfig,
} from '@opentelemetry/tracing';
import { DEFAULT_INSTRUMENTATION_PLUGINS, NodeTracerConfig } from './config';
-import { PluginLoader } from './instrumentation/PluginLoader';
+import { PluginLoader, Plugins } from './instrumentation/PluginLoader';
/**
* Register this TracerProvider for use with the OpenTelemetry API.
@@ -39,7 +39,12 @@ export class NodeTracerProvider extends BasicTracerProvider {
super(config);
this._pluginLoader = new PluginLoader(this, this.logger);
- this._pluginLoader.load(config.plugins || DEFAULT_INSTRUMENTATION_PLUGINS);
+
+ config.plugins
+ ? this._pluginLoader.load(
+ this._mergePlugins(DEFAULT_INSTRUMENTATION_PLUGINS, config.plugins)
+ )
+ : this._pluginLoader.load(DEFAULT_INSTRUMENTATION_PLUGINS);
}
stop() {
@@ -54,4 +59,33 @@ 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;
+ }
}
diff --git a/packages/opentelemetry-node/test/NodeTracerProvider.test.ts b/packages/opentelemetry-node/test/NodeTracerProvider.test.ts
index d38dda5319..c9594e9288 100644
--- a/packages/opentelemetry-node/test/NodeTracerProvider.test.ts
+++ b/packages/opentelemetry-node/test/NodeTracerProvider.test.ts
@@ -81,29 +81,38 @@ describe('NodeTracerProvider', () => {
assert.ok(provider instanceof NodeTracerProvider);
});
- it('should load user configured plugins', () => {
+ 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': {
- enabled: true,
path: '@opentelemetry/plugin-simple-module',
},
'supported-module': {
- enabled: true,
path: '@opentelemetry/plugin-supported-module',
enhancedDatabaseReporting: false,
ignoreMethods: [],
ignoreUrls: [],
},
+ 'random-module': {
+ enabled: false,
+ path: '@opentelemetry/random-module',
+ },
+ http: {
+ path: '@opentelemetry/plugin-http-module',
+ },
},
});
- const pluginLoader = provider['_pluginLoader'];
- assert.strictEqual(pluginLoader['_plugins'].length, 0);
+ const plugins = provider['_pluginLoader']['_plugins'];
+ assert.strictEqual(plugins.length, 0);
require('simple-module');
- assert.strictEqual(pluginLoader['_plugins'].length, 1);
+ assert.strictEqual(plugins.length, 1);
require('supported-module');
- assert.strictEqual(pluginLoader['_plugins'].length, 2);
+ assert.strictEqual(plugins.length, 2);
+ require('random-module');
+ assert.strictEqual(plugins.length, 2);
+ require('http');
+ assert.strictEqual(plugins.length, 3);
});
it('should construct an instance with default attributes', () => {
@@ -269,3 +278,52 @@ 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');
+ });
+});
diff --git a/packages/opentelemetry-node/test/instrumentation/node_modules/@opentelemetry/plugin-http-module/package.json b/packages/opentelemetry-node/test/instrumentation/node_modules/@opentelemetry/plugin-http-module/package.json
index 59d87df350..bb40eab67d 100644
--- a/packages/opentelemetry-node/test/instrumentation/node_modules/@opentelemetry/plugin-http-module/package.json
+++ b/packages/opentelemetry-node/test/instrumentation/node_modules/@opentelemetry/plugin-http-module/package.json
@@ -1,4 +1,4 @@
{
- "name": "@opentelemetry/plugin-simple-module",
+ "name": "@opentelemetry/plugin-http-module",
"version": "0.0.1"
}