Skip to content

Commit

Permalink
Merge branch 'master' into http-sender
Browse files Browse the repository at this point in the history
  • Loading branch information
leonardodalcin committed May 27, 2020
2 parents 0235711 + f08b2a0 commit 0d8b202
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 41 deletions.
9 changes: 1 addition & 8 deletions examples/grpc/tracer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')) {
Expand Down
60 changes: 37 additions & 23 deletions packages/opentelemetry-node/README.md
Original file line number Diff line number Diff line change
@@ -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]
Expand All @@ -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.
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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()
Expand All @@ -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: <https://opentelemetry.io/>
- For more about OpenTelemetry JavaScript: <https://github.com/open-telemetry/opentelemetry-js>
- For help or feedback on this project, join us on [gitter][gitter-url]
Expand Down
38 changes: 36 additions & 2 deletions packages/opentelemetry-node/src/NodeTracerProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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() {
Expand All @@ -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;
}
}
72 changes: 65 additions & 7 deletions packages/opentelemetry-node/test/NodeTracerProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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');
});
});

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 0d8b202

Please sign in to comment.