Skip to content

Commit f08b2a0

Browse files
Naseemmayurkale22
Naseem
andauthored
feat: merge user supplied and default plugin configs (#980)
Co-authored-by: Mayur Kale <[email protected]>
1 parent da11e1b commit f08b2a0

File tree

5 files changed

+140
-41
lines changed

5 files changed

+140
-41
lines changed

examples/grpc/tracer.js

+1-8
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,7 @@ const { ZipkinExporter } = require('@opentelemetry/exporter-zipkin');
99
const EXPORTER = process.env.EXPORTER || '';
1010

1111
module.exports = (serviceName) => {
12-
const provider = new NodeTracerProvider({
13-
plugins: {
14-
grpc: {
15-
enabled: true,
16-
path: '@opentelemetry/plugin-grpc',
17-
},
18-
},
19-
});
12+
const provider = new NodeTracerProvider();
2013

2114
let exporter;
2215
if (EXPORTER.toLowerCase().startsWith('z')) {

packages/opentelemetry-node/README.md

+37-23
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# OpenTelemetry Node SDK
2+
23
[![Gitter chat][gitter-image]][gitter-url]
34
[![NPM Published Version][npm-img]][npm-url]
45
[![dependencies][dependencies-image]][dependencies-url]
@@ -10,14 +11,14 @@ This module provides *automated instrumentation and tracing* for Node.js applica
1011
For manual instrumentation see the
1112
[@opentelemetry/tracing](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-tracing) package.
1213

13-
## How does automated instrumentation work?
14+
## How auto instrumentation works
15+
1416
This package exposes a `NodeTracerProvider` that will automatically hook into the module loader of Node.js.
1517

1618
For this to work, please make sure that `NodeTracerProvider` is initialized before any other module of your application, (like `http` or `express`) is loaded.
1719

1820
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)).
1921

20-
2122
Whenever a module is loaded `NodeTracerProvider` will check if a matching instrumentation plugin has been installed.
2223

2324
> **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
2627
This is done by wrapping all tracing-relevant functions.
2728

2829
This instrumentation code will automatically
30+
2931
- extract a trace-context identifier from inbound requests to allow distributed tracing (if applicable)
3032
- 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)
3133
- 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
3436
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.
3537

3638
## Creating custom spans on top of auto-instrumentation
39+
3740
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.
3841

3942
## Installation
@@ -49,22 +52,16 @@ npm install --save @opentelemetry/plugin-https
4952

5053
## Usage
5154

52-
The following code will configure the `NodeTracerProvider` to instrument `http` using `@opentelemetry/plugin-http`.
55+
The following code will configure the `NodeTracerProvider` to instrument `http`
56+
(and any other installed [supported
57+
modules](https://github.com/open-telemetry/opentelemetry-js#plugins))
58+
using `@opentelemetry/plugin-http`.
5359

5460
```js
5561
const { NodeTracerProvider } = require('@opentelemetry/node');
5662

5763
// Create and configure NodeTracerProvider
58-
const provider = new NodeTracerProvider({
59-
plugins: {
60-
http: {
61-
enabled: true,
62-
// You may use a package name or absolute path to the file.
63-
path: '@opentelemetry/plugin-http',
64-
// http plugin options
65-
}
66-
}
67-
});
64+
const provider = new NodeTracerProvider();
6865

6966
// Initialize the provider
7067
provider.register()
@@ -74,26 +71,43 @@ provider.register()
7471
const http = require('http');
7572
```
7673

77-
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.
74+
## Plugin configuration
7875

79-
```js
80-
const { NodeTracerProvider } = require('@opentelemetry/node');
76+
User supplied plugin configuration is merged with the default plugin
77+
configuration. Furthermore, custom plugins that are configured are implicitly
78+
enabled just as default plugins are.
8179

82-
// Create and initialize NodeTracerProvider
83-
const provider = new NodeTracerProvider();
80+
In the following example:
8481

85-
// Initialize the provider
86-
provider.register()
82+
- the default express plugin is disabled
83+
- the http plugin has a custom config for a `requestHook`
84+
- the customPlugin is loaded from the user supplied path
85+
- all default plugins are still loaded if installed.
8786

88-
// Your application code
89-
// ...
87+
```js
88+
const provider = new NodeTracerProvider({
89+
plugins: {
90+
express: {
91+
enabled: false,
92+
},
93+
http: {
94+
requestHook: (span, request) => {
95+
span.setAttribute("custom request hook attribute", "request");
96+
},
97+
},
98+
customPlugin: {
99+
path: "/path/to/custom/module",
100+
},
101+
},
102+
});
90103
```
91104

92105
## Examples
93-
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.
94106

107+
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.
95108

96109
## Useful links
110+
97111
- For more information on OpenTelemetry, visit: <https://opentelemetry.io/>
98112
- For more about OpenTelemetry JavaScript: <https://github.com/open-telemetry/opentelemetry-js>
99113
- For help or feedback on this project, join us on [gitter][gitter-url]

packages/opentelemetry-node/src/NodeTracerProvider.ts

+36-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
SDKRegistrationConfig,
2121
} from '@opentelemetry/tracing';
2222
import { DEFAULT_INSTRUMENTATION_PLUGINS, NodeTracerConfig } from './config';
23-
import { PluginLoader } from './instrumentation/PluginLoader';
23+
import { PluginLoader, Plugins } from './instrumentation/PluginLoader';
2424

2525
/**
2626
* Register this TracerProvider for use with the OpenTelemetry API.
@@ -39,7 +39,12 @@ export class NodeTracerProvider extends BasicTracerProvider {
3939
super(config);
4040

4141
this._pluginLoader = new PluginLoader(this, this.logger);
42-
this._pluginLoader.load(config.plugins || DEFAULT_INSTRUMENTATION_PLUGINS);
42+
43+
config.plugins
44+
? this._pluginLoader.load(
45+
this._mergePlugins(DEFAULT_INSTRUMENTATION_PLUGINS, config.plugins)
46+
)
47+
: this._pluginLoader.load(DEFAULT_INSTRUMENTATION_PLUGINS);
4348
}
4449

4550
stop() {
@@ -54,4 +59,33 @@ export class NodeTracerProvider extends BasicTracerProvider {
5459

5560
super.register(config);
5661
}
62+
63+
/**
64+
* Two layer merge.
65+
* First, for user supplied config of plugin(s) that are loaded by default,
66+
* merge the user supplied and default configs of said plugin(s).
67+
* Then merge the results with the default plugins.
68+
* @returns 2-layer deep merge of default and user supplied plugins.
69+
*/
70+
private _mergePlugins(
71+
defaultPlugins: Plugins,
72+
userSuppliedPlugins: Plugins
73+
): Plugins {
74+
const mergedUserSuppliedPlugins: Plugins = {};
75+
76+
for (const pluginName in userSuppliedPlugins) {
77+
mergedUserSuppliedPlugins[pluginName] = {
78+
// Any user-supplied non-default plugin should be enabled by default
79+
...(DEFAULT_INSTRUMENTATION_PLUGINS[pluginName] || { enabled: true }),
80+
...userSuppliedPlugins[pluginName],
81+
};
82+
}
83+
84+
const mergedPlugins: Plugins = {
85+
...defaultPlugins,
86+
...mergedUserSuppliedPlugins,
87+
};
88+
89+
return mergedPlugins;
90+
}
5791
}

packages/opentelemetry-node/test/NodeTracerProvider.test.ts

+65-7
Original file line numberDiff line numberDiff line change
@@ -81,29 +81,38 @@ describe('NodeTracerProvider', () => {
8181
assert.ok(provider instanceof NodeTracerProvider);
8282
});
8383

84-
it('should load user configured plugins', () => {
84+
it('should load a merge of user configured and default plugins and implictly enable non-default plugins', () => {
8585
provider = new NodeTracerProvider({
8686
logger: new NoopLogger(),
8787
plugins: {
8888
'simple-module': {
89-
enabled: true,
9089
path: '@opentelemetry/plugin-simple-module',
9190
},
9291
'supported-module': {
93-
enabled: true,
9492
path: '@opentelemetry/plugin-supported-module',
9593
enhancedDatabaseReporting: false,
9694
ignoreMethods: [],
9795
ignoreUrls: [],
9896
},
97+
'random-module': {
98+
enabled: false,
99+
path: '@opentelemetry/random-module',
100+
},
101+
http: {
102+
path: '@opentelemetry/plugin-http-module',
103+
},
99104
},
100105
});
101-
const pluginLoader = provider['_pluginLoader'];
102-
assert.strictEqual(pluginLoader['_plugins'].length, 0);
106+
const plugins = provider['_pluginLoader']['_plugins'];
107+
assert.strictEqual(plugins.length, 0);
103108
require('simple-module');
104-
assert.strictEqual(pluginLoader['_plugins'].length, 1);
109+
assert.strictEqual(plugins.length, 1);
105110
require('supported-module');
106-
assert.strictEqual(pluginLoader['_plugins'].length, 2);
111+
assert.strictEqual(plugins.length, 2);
112+
require('random-module');
113+
assert.strictEqual(plugins.length, 2);
114+
require('http');
115+
assert.strictEqual(plugins.length, 3);
107116
});
108117

109118
it('should construct an instance with default attributes', () => {
@@ -269,3 +278,52 @@ describe('NodeTracerProvider', () => {
269278
});
270279
});
271280
});
281+
282+
describe('mergePlugins', () => {
283+
const defaultPlugins = {
284+
module1: {
285+
enabled: true,
286+
path: 'testpath',
287+
},
288+
module2: {
289+
enabled: true,
290+
path: 'testpath2',
291+
},
292+
module3: {
293+
enabled: true,
294+
path: 'testpath3',
295+
},
296+
};
297+
298+
const userPlugins = {
299+
module2: {
300+
path: 'userpath',
301+
},
302+
module3: {
303+
enabled: false,
304+
},
305+
nonDefaultModule: {
306+
path: 'userpath2',
307+
},
308+
};
309+
310+
const provider = new NodeTracerProvider();
311+
312+
const mergedPlugins = provider['_mergePlugins'](defaultPlugins, userPlugins);
313+
314+
it('should merge user and default configs', () => {
315+
assert.equal(mergedPlugins.module1.enabled, true);
316+
assert.equal(mergedPlugins.module1.path, 'testpath');
317+
assert.equal(mergedPlugins.module2.enabled, true);
318+
assert.equal(mergedPlugins.module2.path, 'userpath');
319+
assert.equal(mergedPlugins.module3.enabled, false);
320+
assert.equal(mergedPlugins.nonDefaultModule.enabled, true);
321+
assert.equal(mergedPlugins.nonDefaultModule.path, 'userpath2');
322+
});
323+
324+
it('should should not mangle default config', () => {
325+
assert.equal(defaultPlugins.module2.path, 'testpath2');
326+
assert.equal(defaultPlugins.module3.enabled, true);
327+
assert.equal(defaultPlugins.module3.path, 'testpath3');
328+
});
329+
});

packages/opentelemetry-node/test/instrumentation/node_modules/@opentelemetry/plugin-http-module/package.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)