diff --git a/examples/dns/setup.js b/examples/dns/setup.js index e7e40d199e..7e5bab865b 100644 --- a/examples/dns/setup.js +++ b/examples/dns/setup.js @@ -9,15 +9,19 @@ const EXPORTER = process.env.EXPORTER || ''; function setupTracerAndExporters(service) { const tracer = new NodeTracer({ -      plugins: { -          dns: { -            enabled: true, -            path: '@opentelemetry/plugin-dns', + plugins: { + dns: { + enabled: true, + path: '@opentelemetry/plugin-dns', + options: { + dns: { // Avoid dns lookup loop with http zipkin calls ignoreHostnames: ['localhost'] -        } -      } -  }); + } + } + } + } + }); let exporter; if (EXPORTER.toLowerCase().startsWith('z')) { diff --git a/packages/opentelemetry-core/src/trace/instrumentation/BasePlugin.ts b/packages/opentelemetry-core/src/trace/instrumentation/BasePlugin.ts index 5a58aa6962..fdcf05358d 100644 --- a/packages/opentelemetry-core/src/trace/instrumentation/BasePlugin.ts +++ b/packages/opentelemetry-core/src/trace/instrumentation/BasePlugin.ts @@ -15,15 +15,15 @@ */ import { - Tracer, - Plugin, Logger, - PluginConfig, + Plugin, PluginInternalFiles, PluginInternalFilesVersion, + PluginOptions, + Tracer, } from '@opentelemetry/types'; -import * as semver from 'semver'; import * as path from 'path'; +import * as semver from 'semver'; /** This class represent the base to patch plugin. */ export abstract class BasePlugin implements Plugin { @@ -37,19 +37,19 @@ export abstract class BasePlugin implements Plugin { protected _logger!: Logger; protected _internalFilesExports!: { [module: string]: unknown }; // output for internalFilesExports protected readonly _internalFilesList?: PluginInternalFiles; // required for internalFilesExports - protected _config!: PluginConfig; + protected _options: PluginOptions = {}; enable( moduleExports: T, tracer: Tracer, logger: Logger, - config?: PluginConfig + options: PluginOptions = {} ): T { this._moduleExports = moduleExports; this._tracer = tracer; this._logger = logger; this._internalFilesExports = this._loadInternalFilesExports(); - if (config) this._config = config; + this._options = options; return this.patch(); } diff --git a/packages/opentelemetry-core/src/utils/url.ts b/packages/opentelemetry-core/src/utils/url.ts index 8ed779bca2..9879f6eba2 100644 --- a/packages/opentelemetry-core/src/utils/url.ts +++ b/packages/opentelemetry-core/src/utils/url.ts @@ -14,33 +14,76 @@ * limitations under the License. */ +import { IgnoreMatcher } from '@opentelemetry/types'; + /** - * Check if {@param url} matches {@param urlToMatch} - * @param url - * @param urlToMatch + * Check if {@param url} matches an ignore matcher. An ignore + * matcher is matched if it satisfies one of the following: + * + * 1. Exact string match (case insensitive) + * + * 2. Matches regexp using pattern.test + * + * 3. Function does not throw AND returns a truthy value + * + * @param url e.g URL of request + * @param pattern Match pattern may be a string, regex, or function */ -export function urlMatches(url: string, urlToMatch: string | RegExp): boolean { - if (typeof urlToMatch === 'string') { - return url === urlToMatch; - } else { - return !!url.match(urlToMatch); +export function matchesPattern(url: string, pattern?: IgnoreMatcher): boolean { + if (!pattern) { + return false; + } + + // exact string match + if (typeof pattern === 'string') { + return pattern.toUpperCase() === url.toUpperCase(); + } + + // test regex + if (pattern instanceof RegExp) { + return pattern.test(url); } + + // function does not throw AND returns truthy value + if (typeof pattern === 'function') { + try { + return Boolean(pattern(url)); + } catch { + return false; + } + } + + // If none of the above conditions are met, the + // url is not considered matched. + return false; } + /** - * Check if {@param url} should be ignored when comparing against {@param ignoredUrls} - * @param url - * @param ignoredUrls + * Check if {@param url} matches any matcher in an array of + * ignore matchers. An ignore matcher is matched if it satisfies + * one of the following: + * + * 1. Exact string match (case insensitive) + * + * 2. Matches regexp using pattern.test + * + * 3. Function does not throw AND returns a truthy value + * + * @param url e.g URL of request + * @param patterns Array of match patterns which may be a string, regex, or function */ -export function isUrlIgnored( +export function matchesAnyPattern( url: string, - ignoredUrls?: Array + patterns?: IgnoreMatcher[] ): boolean { - if (!ignoredUrls) { + if (!patterns) { return false; } - for (const ignoreUrl of ignoredUrls) { - if (urlMatches(url, ignoreUrl)) { + // If any matcher in an array is matched, the url + // is considered ignored. + for (const p of patterns) { + if (matchesPattern(url, p)) { return true; } } diff --git a/packages/opentelemetry-core/test/utils/url.test.ts b/packages/opentelemetry-core/test/utils/url.test.ts index 205ec9aa07..6dd56aa3b3 100644 --- a/packages/opentelemetry-core/test/utils/url.test.ts +++ b/packages/opentelemetry-core/test/utils/url.test.ts @@ -16,7 +16,7 @@ import * as assert from 'assert'; -import { isUrlIgnored } from '../../src'; +import { matchesAnyPattern, matchesPattern } from '../../src'; const urlIgnored = 'url should be ignored'; const urlNotIgnored = 'url should NOT be ignored'; @@ -24,30 +24,95 @@ const urlNotIgnored = 'url should NOT be ignored'; const urlToTest = 'http://myaddress.com/somepath'; describe('BasePlugin - Utils', () => { - describe('isUrlIgnored', () => { + describe('matchesPattern', () => { + describe('when a pattern is a string', () => { + describe('and an exact case insensitive match', () => { + it('should return true', () => { + assert.strictEqual(matchesPattern('http://url', 'http://URL'), true); + }); + }); + describe('and not an exact case insensitive match', () => { + it('should return false', () => { + assert.strictEqual( + matchesPattern('http://url', 'http://URL.com'), + false + ); + }); + }); + }); + describe('when a pattern is a regular expression', () => { + describe('and a match', () => { + it('should return true', () => { + assert.strictEqual(matchesPattern('http://url', /url/), true); + }); + }); + describe('and not a match', () => { + it('should return false', () => { + assert.strictEqual(matchesPattern('http://url', /noturl/), false); + }); + }); + }); + describe('when a pattern is a function', () => { + describe('that throws', () => { + it('should return false', () => { + assert.strictEqual( + matchesPattern('http://url', () => { + throw new Error(); + }), + false + ); + }); + }); + describe('that returns a false value', () => { + it('should return false', () => { + assert.strictEqual( + matchesPattern('http://url', url => { + return url === 'noturl'; + }), + false + ); + }); + }); + describe('that returns a true value', () => { + it('should return true', () => { + assert.strictEqual( + matchesPattern('http://url', url => { + return url === 'http://url'; + }), + true + ); + }); + }); + }); + }); + describe('matchesAnyPattern', () => { describe('when ignored urls are undefined', () => { it('should return false', () => { - assert.strictEqual(isUrlIgnored(urlToTest), false, urlNotIgnored); + assert.strictEqual(matchesAnyPattern(urlToTest), false, urlNotIgnored); }); }); describe('when ignored urls are empty', () => { it('should return false', () => { - assert.strictEqual(isUrlIgnored(urlToTest, []), false, urlNotIgnored); + assert.strictEqual( + matchesAnyPattern(urlToTest, []), + false, + urlNotIgnored + ); }); }); - describe('when ignored urls is the same as url', () => { + describe('when ignored urls contain the url', () => { it('should return true', () => { assert.strictEqual( - isUrlIgnored(urlToTest, ['http://myaddress.com/somepath']), + matchesAnyPattern(urlToTest, ['http://myaddress.com/somepath']), true, urlIgnored ); }); }); - describe('when url is part of ignored urls', () => { + describe('when ignored urls does not contain the url', () => { it('should return false', () => { assert.strictEqual( - isUrlIgnored(urlToTest, ['http://myaddress.com/some']), + matchesAnyPattern(urlToTest, ['http://myaddress.com/some']), false, urlNotIgnored ); @@ -56,16 +121,16 @@ describe('BasePlugin - Utils', () => { describe('when ignored urls is part of url - REGEXP', () => { it('should return true', () => { assert.strictEqual( - isUrlIgnored(urlToTest, [/.+?myaddress\.com/]), + matchesAnyPattern(urlToTest, [/.+?myaddress\.com/]), true, urlIgnored ); }); }); - describe('when url is part of ignored urls - REGEXP', () => { + describe('when url is not part of ignored urls - REGEXP', () => { it('should return false', () => { assert.strictEqual( - isUrlIgnored(urlToTest, [/http:\/\/myaddress\.com\/somepath2/]), + matchesAnyPattern(urlToTest, [/http:\/\/myaddress\.com\/somepath2/]), false, urlNotIgnored ); diff --git a/packages/opentelemetry-node/README.md b/packages/opentelemetry-node/README.md index 8a24c5b0d7..022e119032 100644 --- a/packages/opentelemetry-node/README.md +++ b/packages/opentelemetry-node/README.md @@ -64,8 +64,11 @@ const tracer = new NodeTracer({ // You may use a package name or absolute path to the file. path: '@opentelemetry/plugin-http', // http plugin options + options: {}, } - } + }, + // shared plugin options + options: {} }); // Initialize the tracer diff --git a/packages/opentelemetry-node/src/NodeTracer.ts b/packages/opentelemetry-node/src/NodeTracer.ts index 86591fd0ac..7ca83fe7fb 100644 --- a/packages/opentelemetry-node/src/NodeTracer.ts +++ b/packages/opentelemetry-node/src/NodeTracer.ts @@ -17,7 +17,7 @@ import { BasicTracer } from '@opentelemetry/tracing'; import { AsyncHooksScopeManager } from '@opentelemetry/scope-async-hooks'; import { PluginLoader } from './instrumentation/PluginLoader'; -import { NodeTracerConfig, DEFAULT_INSTRUMENTATION_PLUGINS } from './config'; +import { NodeTracerConfig } from './config'; /** * This class represents a node tracer with `async_hooks` module. @@ -36,7 +36,7 @@ export class NodeTracer extends BasicTracer { super(Object.assign({ scopeManager: config.scopeManager }, config)); this._pluginLoader = new PluginLoader(this, this.logger); - this._pluginLoader.load(config.plugins || DEFAULT_INSTRUMENTATION_PLUGINS); + this._pluginLoader.load(config); } stop() { diff --git a/packages/opentelemetry-node/src/config.ts b/packages/opentelemetry-node/src/config.ts index 7fb1b7dd5b..46100b2779 100644 --- a/packages/opentelemetry-node/src/config.ts +++ b/packages/opentelemetry-node/src/config.ts @@ -16,13 +16,17 @@ import { Plugins } from './instrumentation/PluginLoader'; import { BasicTracerConfig } from '@opentelemetry/tracing'; +import { PluginOptions } from '@opentelemetry/types'; /** * NodeTracerConfig provides an interface for configuring a Node Tracer. */ export interface NodeTracerConfig extends BasicTracerConfig { - /** Plugins options. */ + /** Plugins configuration for individual modules */ plugins?: Plugins; + + /** Plugin options applied to all plugins */ + options?: PluginOptions; } /** List of all default supported plugins */ diff --git a/packages/opentelemetry-node/src/instrumentation/PluginLoader.ts b/packages/opentelemetry-node/src/instrumentation/PluginLoader.ts index 3eb8881e6f..ef4869cac8 100644 --- a/packages/opentelemetry-node/src/instrumentation/PluginLoader.ts +++ b/packages/opentelemetry-node/src/instrumentation/PluginLoader.ts @@ -14,8 +14,9 @@ * limitations under the License. */ -import { Logger, Plugin, Tracer, PluginConfig } from '@opentelemetry/types'; +import { Logger, Plugin, PluginConfig, Tracer } from '@opentelemetry/types'; import * as hook from 'require-in-the-middle'; +import { DEFAULT_INSTRUMENTATION_PLUGINS, NodeTracerConfig } from '../config'; import * as utils from './utils'; // States for the Plugin Loader @@ -65,7 +66,39 @@ export class PluginLoader { * @param Plugins an object whose keys are plugin names and whose * {@link PluginConfig} values indicate several configuration options. */ - load(plugins: Plugins): PluginLoader { + load(tracerConfig: NodeTracerConfig): PluginLoader { + // const plugins = Object.assign({}, DEFAULT_INSTRUMENTATION_PLUGINS, tracerConfig.plugins); + + const configuredPluginModuleNames = Array.from( + new Set([ + ...Object.keys(tracerConfig.plugins || {}), + ...Object.keys(DEFAULT_INSTRUMENTATION_PLUGINS), + ]) + ); + + const plugins: Plugins = {}; + for (const pluginModuleName of configuredPluginModuleNames) { + const defaultConfig = + DEFAULT_INSTRUMENTATION_PLUGINS[pluginModuleName] || {}; + const config = + (tracerConfig.plugins && tracerConfig.plugins[pluginModuleName]) || {}; + + const enabled = + typeof config.enabled === 'boolean' + ? config.enabled + : defaultConfig.enabled; + + plugins[pluginModuleName] = { + path: config.path || defaultConfig.path, + enabled: enabled !== false, + options: utils.mergeOptions( + defaultConfig.options, + tracerConfig.options, + config.options + ), + }; + } + if (this._hookState === HookState.UNINITIALIZED) { const pluginsToLoad = filterPlugins(plugins); const modulesToHook = Object.keys(pluginsToLoad); @@ -115,7 +148,12 @@ export class PluginLoader { this._plugins.push(plugin); // Enable each supported plugin. - return plugin.enable(exports, this.tracer, this.logger, config); + return plugin.enable( + exports, + this.tracer, + this.logger, + config.options + ); } catch (e) { this.logger.error( `PluginLoader#load: could not load plugin ${modulePath} of module ${name}. Error: ${e.message}` diff --git a/packages/opentelemetry-node/src/instrumentation/utils.ts b/packages/opentelemetry-node/src/instrumentation/utils.ts index 9460de4ad8..4e8cea3a45 100644 --- a/packages/opentelemetry-node/src/instrumentation/utils.ts +++ b/packages/opentelemetry-node/src/instrumentation/utils.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Logger } from '@opentelemetry/types'; +import { Logger, PluginOptions } from '@opentelemetry/types'; import * as path from 'path'; import * as semver from 'semver'; @@ -72,3 +72,35 @@ export function isSupportedVersion( export function searchPathForTest(searchPath: string) { module.paths.push(searchPath); } + +/** + * Merge plugin options. Rightmost options take precedence. + * + * @param options config objects to merge + */ +export function mergeOptions( + ...options: (PluginOptions | undefined)[] +): PluginOptions { + const out: PluginOptions = {}; + for (const config of options) { + if (!config) { + continue; + } + for (const type of keys(config)) { + for (const option of keys(config[type])) { + const value = config[type]![option]!; + + if (!out[type]) { + out[type] = {}; + } + + out[type]![option] = value; + } + } + } + return out; +} + +function keys(obj: T): Array { + return Object.keys(obj) as Array; +} diff --git a/packages/opentelemetry-node/test/NodeTracer.test.ts b/packages/opentelemetry-node/test/NodeTracer.test.ts index 6c0df61de7..ebdb1e0343 100644 --- a/packages/opentelemetry-node/test/NodeTracer.test.ts +++ b/packages/opentelemetry-node/test/NodeTracer.test.ts @@ -96,9 +96,14 @@ describe('NodeTracer', () => { 'supported-module': { enabled: true, path: '@opentelemetry/plugin-supported-module', - enhancedDatabaseReporting: false, - ignoreMethods: [], - ignoreUrls: [], + options: { + database: { + enhancedDatabaseReporting: false, + }, + http: { + ignoreOutgoingUrls: [], + }, + }, }, }, }); diff --git a/packages/opentelemetry-node/test/instrumentation/PluginLoader.test.ts b/packages/opentelemetry-node/test/instrumentation/PluginLoader.test.ts index af6ca193e7..9bc16dd553 100644 --- a/packages/opentelemetry-node/test/instrumentation/PluginLoader.test.ts +++ b/packages/opentelemetry-node/test/instrumentation/PluginLoader.test.ts @@ -30,8 +30,6 @@ const simplePlugins: Plugins = { 'simple-module': { enabled: true, path: '@opentelemetry/plugin-simple-module', - ignoreMethods: [], - ignoreUrls: [], }, }; @@ -39,8 +37,11 @@ const httpPlugins: Plugins = { http: { enabled: true, path: '@opentelemetry/plugin-http-module', - ignoreMethods: [], - ignoreUrls: [], + options: { + http: { + ignoreOutgoingUrls: ['plugin specific option'], + }, + }, }, }; @@ -107,14 +108,14 @@ describe('PluginLoader', () => { it('transitions from UNINITIALIZED to ENABLED', () => { const pluginLoader = new PluginLoader(tracer, logger); - pluginLoader.load(simplePlugins); + pluginLoader.load({ plugins: simplePlugins }); assert.strictEqual(pluginLoader['_hookState'], HookState.ENABLED); pluginLoader.unload(); }); it('transitions from ENABLED to DISABLED', () => { const pluginLoader = new PluginLoader(tracer, logger); - pluginLoader.load(simplePlugins).unload(); + pluginLoader.load({ plugins: simplePlugins }).unload(); assert.strictEqual(pluginLoader['_hookState'], HookState.DISABLED); }); }); @@ -140,7 +141,7 @@ describe('PluginLoader', () => { it('should load a plugin and patch the target modules', () => { const pluginLoader = new PluginLoader(tracer, logger); assert.strictEqual(pluginLoader['_plugins'].length, 0); - pluginLoader.load(simplePlugins); + pluginLoader.load({ plugins: simplePlugins }); // The hook is only called the first time the module is loaded. const simpleModule = require('simple-module'); assert.strictEqual(pluginLoader['_plugins'].length, 1); @@ -152,18 +153,72 @@ describe('PluginLoader', () => { it('should load a plugin and patch the core module', () => { const pluginLoader = new PluginLoader(tracer, logger); assert.strictEqual(pluginLoader['_plugins'].length, 0); - pluginLoader.load(httpPlugins); + pluginLoader.load({ plugins: 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(); }); + + it('should set shared options on plugins', () => { + const pluginLoader = new PluginLoader(tracer, logger); + assert.strictEqual(pluginLoader['_plugins'].length, 0); + pluginLoader.load({ + plugins: httpPlugins, + options: { dns: { ignoreHostnames: ['shared option'] } }, + }); + // The hook is only called the first time the module is loaded. + const httpModule = require('http'); + assert.strictEqual(pluginLoader['_plugins'].length, 1); + assert.deepStrictEqual( + (pluginLoader['_plugins'][0] as any)._options.dns.ignoreHostnames, + ['shared option'] + ); + assert.strictEqual(httpModule.get(), 'patched'); + pluginLoader.unload(); + }); + + it('should override shared options with explicit ones', () => { + const pluginLoader = new PluginLoader(tracer, logger); + assert.strictEqual(pluginLoader['_plugins'].length, 0); + pluginLoader.load({ + plugins: httpPlugins, + options: { http: { ignoreOutgoingUrls: ['shared'] } }, + }); + // The hook is only called the first time the module is loaded. + const httpModule = require('http'); + assert.strictEqual(pluginLoader['_plugins'].length, 1); + assert.deepStrictEqual( + (pluginLoader['_plugins'][0] as any)._options.http.ignoreOutgoingUrls, + ['plugin specific option'] + ); + assert.strictEqual(httpModule.get(), 'patched'); + pluginLoader.unload(); + }); + + it('should merge shared and explicit options', () => { + const pluginLoader = new PluginLoader(tracer, logger); + assert.strictEqual(pluginLoader['_plugins'].length, 0); + pluginLoader.load({ + plugins: httpPlugins, + options: { dns: { ignoreHostnames: ['shared'] } }, + }); + // The hook is only called the first time the module is loaded. + const httpModule = require('http'); + assert.strictEqual(pluginLoader['_plugins'].length, 1); + assert.deepStrictEqual((pluginLoader['_plugins'][0] as any)._options, { + dns: { ignoreHostnames: ['shared'] }, + http: { ignoreOutgoingUrls: ['plugin specific option'] }, + }); + 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(tracer, logger); assert.strictEqual(pluginLoader['_plugins'].length, 0); - pluginLoader.load(notSupportedVersionPlugins); + pluginLoader.load({ plugins: notSupportedVersionPlugins }); // The hook is only called the first time the module is loaded. require('notsupported-module'); assert.strictEqual(pluginLoader['_plugins'].length, 0); @@ -173,7 +228,7 @@ describe('PluginLoader', () => { it('should load a plugin and patch the target modules when supported versions match', () => { const pluginLoader = new PluginLoader(tracer, logger); assert.strictEqual(pluginLoader['_plugins'].length, 0); - pluginLoader.load(supportedVersionPlugins); + pluginLoader.load({ plugins: supportedVersionPlugins }); // The hook is only called the first time the module is loaded. const simpleModule = require('supported-module'); assert.strictEqual(pluginLoader['_plugins'].length, 1); @@ -185,7 +240,7 @@ describe('PluginLoader', () => { it('should not load a plugin when value is false', () => { const pluginLoader = new PluginLoader(tracer, logger); assert.strictEqual(pluginLoader['_plugins'].length, 0); - pluginLoader.load(disablePlugins); + pluginLoader.load({ plugins: disablePlugins }); const simpleModule = require('simple-module'); assert.strictEqual(pluginLoader['_plugins'].length, 0); assert.strictEqual(simpleModule.value(), 0); @@ -196,7 +251,7 @@ describe('PluginLoader', () => { it('should not load a plugin when value is true but path is missing', () => { const pluginLoader = new PluginLoader(tracer, logger); assert.strictEqual(pluginLoader['_plugins'].length, 0); - pluginLoader.load(missingPathPlugins); + pluginLoader.load({ plugins: missingPathPlugins }); const simpleModule = require('simple-module'); assert.strictEqual(pluginLoader['_plugins'].length, 0); assert.strictEqual(simpleModule.value(), 0); @@ -207,14 +262,14 @@ describe('PluginLoader', () => { it('should not load a non existing plugin', () => { const pluginLoader = new PluginLoader(tracer, logger); assert.strictEqual(pluginLoader['_plugins'].length, 0); - pluginLoader.load(nonexistentPlugins); + pluginLoader.load({ plugins: nonexistentPlugins }); assert.strictEqual(pluginLoader['_plugins'].length, 0); pluginLoader.unload(); }); it(`doesn't patch modules for which plugins aren't specified`, () => { const pluginLoader = new PluginLoader(tracer, logger); - pluginLoader.load({}); + pluginLoader.load({ plugins: {} }); assert.strictEqual(require('simple-module').value(), 0); pluginLoader.unload(); }); @@ -224,7 +279,7 @@ describe('PluginLoader', () => { it('should unload the plugins and unpatch the target module when unloads', () => { const pluginLoader = new PluginLoader(tracer, logger); assert.strictEqual(pluginLoader['_plugins'].length, 0); - pluginLoader.load(simplePlugins); + pluginLoader.load({ plugins: simplePlugins }); // The hook is only called the first time the module is loaded. const simpleModule = require('simple-module'); assert.strictEqual(pluginLoader['_plugins'].length, 1); diff --git a/packages/opentelemetry-plugin-dns/README.md b/packages/opentelemetry-plugin-dns/README.md index 50bf77fd7c..6837812903 100644 --- a/packages/opentelemetry-plugin-dns/README.md +++ b/packages/opentelemetry-plugin-dns/README.md @@ -27,8 +27,11 @@ const tracer = new NodeTracer({ // You may use a package name or absolute path to the file. path: '@opentelemetry/plugin-dns', // dns plugin options + options: {}, } - } + }, + // shared plugin options + options: {} }); ``` diff --git a/packages/opentelemetry-plugin-dns/src/dns.ts b/packages/opentelemetry-plugin-dns/src/dns.ts index 11eac18399..e97c3337ce 100644 --- a/packages/opentelemetry-plugin-dns/src/dns.ts +++ b/packages/opentelemetry-plugin-dns/src/dns.ts @@ -14,35 +14,32 @@ * limitations under the License. */ -import * as shimmer from 'shimmer'; +import { BasePlugin, matchesAnyPattern } from '@opentelemetry/core'; +import { Span, SpanKind, SpanOptions } from '@opentelemetry/types'; +import { LookupAddress } from 'dns'; import * as semver from 'semver'; -import * as utils from './utils'; -import { BasePlugin } from '@opentelemetry/core'; -import { SpanOptions, SpanKind, Span } from '@opentelemetry/types'; +import * as shimmer from 'shimmer'; +import { AddressFamily } from './enums/AddressFamily'; +import { AttributeNames } from './enums/AttributeNames'; import { Dns, - LookupPromiseSignature, + LookupCallbackSignature, LookupFunction, LookupFunctionSignature, - LookupCallbackSignature, - DnsPluginConfig, + LookupPromiseSignature, } from './types'; -import { AttributeNames } from './enums/AttributeNames'; -import { AddressFamily } from './enums/AddressFamily'; -import { LookupAddress } from 'dns'; +import * as utils from './utils'; /** * Dns instrumentation plugin for Opentelemetry */ export class DnsPlugin extends BasePlugin { readonly component: string; - protected _config!: DnsPluginConfig; constructor(readonly moduleName: string, readonly version: string) { super(); // For now component is equal to moduleName but it can change in the future. this.component = this.moduleName; - this._config = {}; } /** Patches DNS functions. */ @@ -105,8 +102,9 @@ export class DnsPlugin extends BasePlugin { ...args: unknown[] ) { if ( - utils.isIgnored(hostname, plugin._config.ignoreHostnames, (e: Error) => - plugin._logger.error('caught ignoreHostname error: ', e) + matchesAnyPattern( + hostname, + plugin._options.dns && plugin._options.dns.ignoreHostnames ) ) { return original.apply(this, [hostname, ...args]); diff --git a/packages/opentelemetry-plugin-dns/src/types.ts b/packages/opentelemetry-plugin-dns/src/types.ts index 0f84de4f60..940c517413 100644 --- a/packages/opentelemetry-plugin-dns/src/types.ts +++ b/packages/opentelemetry-plugin-dns/src/types.ts @@ -15,12 +15,9 @@ */ import * as dns from 'dns'; -import { PluginConfig } from '@opentelemetry/types'; export type Dns = typeof dns; -export type IgnoreMatcher = string | RegExp | ((url: string) => boolean); - export type LookupFunction = (( hostname: string, family: number, @@ -94,7 +91,3 @@ export type LookupCallbackSignature = LookupSimpleCallback & address: string | dns.LookupAddress[], family: number ) => void); - -export interface DnsPluginConfig extends PluginConfig { - ignoreHostnames?: IgnoreMatcher[]; -} diff --git a/packages/opentelemetry-plugin-dns/src/utils.ts b/packages/opentelemetry-plugin-dns/src/utils.ts index d2d1b2ce84..baafd8dbcb 100644 --- a/packages/opentelemetry-plugin-dns/src/utils.ts +++ b/packages/opentelemetry-plugin-dns/src/utils.ts @@ -14,11 +14,10 @@ * limitations under the License. */ -import { Span, CanonicalCode, Status, Attributes } from '@opentelemetry/types'; -import { AttributeNames } from './enums/AttributeNames'; -import { AddressFamily } from './enums/AddressFamily'; +import { Attributes, CanonicalCode, Span, Status } from '@opentelemetry/types'; import * as dns from 'dns'; -import { IgnoreMatcher } from './types'; +import { AddressFamily } from './enums/AddressFamily'; +import { AttributeNames } from './enums/AttributeNames'; /** * Set error attributes on the span passed in params @@ -155,57 +154,3 @@ export const setLookupAttributes = ( span.setAttributes(attributes); }; - -/** - * Check whether the given obj match pattern - * @param constant e.g URL of request - * @param obj obj to inspect - * @param pattern Match pattern - */ -export const satisfiesPattern = ( - constant: string, - pattern: IgnoreMatcher -): boolean => { - if (typeof pattern === 'string') { - return pattern === constant; - } else if (pattern instanceof RegExp) { - return pattern.test(constant); - } else if (typeof pattern === 'function') { - return pattern(constant); - } else { - throw new TypeError('Pattern is in unsupported datatype'); - } -}; - -/** - * Check whether the given dns request is ignored by configuration - * It will not re-throw exceptions from `list` provided by the client - * @param constant e.g URL of request - * @param [list] List of ignore patterns - * @param [onException] callback for doing something when an exception has - * occurred - */ -export const isIgnored = ( - constant: string, - list?: IgnoreMatcher[], - onException?: (error: Error) => void -): boolean => { - if (!list) { - // No ignored urls - trace everything - return false; - } - // Try/catch outside the loop for failing fast - try { - for (const pattern of list) { - if (satisfiesPattern(constant, pattern)) { - return true; - } - } - } catch (e) { - if (onException) { - onException(e); - } - } - - return false; -}; diff --git a/packages/opentelemetry-plugin-dns/test/functionals/utils.test.ts b/packages/opentelemetry-plugin-dns/test/functionals/utils.test.ts index 8bda076750..58672cd559 100644 --- a/packages/opentelemetry-plugin-dns/test/functionals/utils.test.ts +++ b/packages/opentelemetry-plugin-dns/test/functionals/utils.test.ts @@ -15,12 +15,9 @@ */ import * as assert from 'assert'; -import * as sinon from 'sinon'; import { CanonicalCode, SpanKind } from '@opentelemetry/types'; -import { IgnoreMatcher } from '../../src/types'; import * as utils from '../../src/utils'; import { Span, BasicTracer } from '@opentelemetry/tracing'; -import { NoopLogger } from '@opentelemetry/core'; import { AttributeNames } from '../../src/enums/AttributeNames'; describe('Utility', () => { @@ -33,131 +30,6 @@ describe('Utility', () => { }); }); - describe('satisfiesPattern()', () => { - it('string pattern', () => { - const answer1 = utils.satisfiesPattern('localhost', 'localhost'); - assert.strictEqual(answer1, true); - const answer2 = utils.satisfiesPattern('hostname', 'localhost'); - assert.strictEqual(answer2, false); - }); - - it('regex pattern', () => { - const answer1 = utils.satisfiesPattern('LocalHost', /localhost/i); - assert.strictEqual(answer1, true); - const answer2 = utils.satisfiesPattern('Montreal.ca', /montreal.ca/); - assert.strictEqual(answer2, false); - }); - - it('should throw if type is unknown', () => { - try { - utils.satisfiesPattern( - 'google.com', - (true as unknown) as IgnoreMatcher - ); - assert.fail(); - } catch (error) { - assert.strictEqual(error instanceof TypeError, true); - } - }); - - it('function pattern', () => { - const answer1 = utils.satisfiesPattern( - 'montreal.ca', - (url: string) => url === 'montreal.ca' - ); - assert.strictEqual(answer1, true); - const answer2 = utils.satisfiesPattern( - 'montreal.ca', - (url: string) => url !== 'montreal.ca' - ); - assert.strictEqual(answer2, false); - }); - }); - - describe('isIgnored()', () => { - let satisfiesPatternStub: sinon.SinonSpy<[string, IgnoreMatcher], boolean>; - beforeEach(() => { - satisfiesPatternStub = sinon.spy(utils, 'satisfiesPattern'); - }); - - afterEach(() => { - satisfiesPatternStub.restore(); - }); - - it('should call isSatisfyPattern, n match', () => { - const answer1 = utils.isIgnored('localhost', ['test']); - assert.strictEqual(answer1, false); - assert.strictEqual( - (utils.satisfiesPattern as sinon.SinonSpy).callCount, - 1 - ); - }); - - it('should call isSatisfyPattern, match for function', () => { - satisfiesPatternStub.restore(); - const answer1 = utils.isIgnored('api.montreal.ca', [ - url => url.endsWith('montreal.ca'), - ]); - assert.strictEqual(answer1, true); - }); - - it('should not re-throw when function throws an exception', () => { - satisfiesPatternStub.restore(); - const log = new NoopLogger(); - const onException = (e: Error) => { - log.error('error', e); - }; - for (const callback of [undefined, onException]) { - assert.doesNotThrow(() => - utils.isIgnored( - 'test', - [ - url => { - throw new Error('test'); - }, - ], - callback - ) - ); - } - }); - - it('should call onException when function throws an exception', () => { - satisfiesPatternStub.restore(); - const onException = sinon.spy(); - assert.doesNotThrow(() => - utils.isIgnored( - 'test', - [ - url => { - throw new Error('test'); - }, - ], - onException - ) - ); - assert.strictEqual((onException as sinon.SinonSpy).callCount, 1); - }); - - it('should not call isSatisfyPattern', () => { - utils.isIgnored('test', []); - assert.strictEqual( - (utils.satisfiesPattern as sinon.SinonSpy).callCount, - 0 - ); - }); - - it('should return false on empty list', () => { - const answer1 = utils.isIgnored('test', []); - assert.strictEqual(answer1, false); - }); - - it('should not throw and return false when list is undefined', () => { - const answer2 = utils.isIgnored('test', undefined); - assert.strictEqual(answer2, false); - }); - }); - describe('setError()', () => { it('should have error attributes', () => { const errorMessage = 'test error'; diff --git a/packages/opentelemetry-plugin-document-load/src/documentLoad.ts b/packages/opentelemetry-plugin-document-load/src/documentLoad.ts index 208b7d151b..5040afa56c 100644 --- a/packages/opentelemetry-plugin-document-load/src/documentLoad.ts +++ b/packages/opentelemetry-plugin-document-load/src/documentLoad.ts @@ -20,7 +20,7 @@ import { parseTraceParent, TRACE_PARENT_HEADER, } from '@opentelemetry/core'; -import { PluginConfig, Span, SpanOptions } from '@opentelemetry/types'; +import { PluginOptions, Span, SpanOptions } from '@opentelemetry/types'; import { AttributeNames } from './enums/AttributeNames'; import { addSpanNetworkEvent, @@ -37,16 +37,14 @@ export class DocumentLoad extends BasePlugin { readonly component: string = 'document-load'; readonly version: string = '1'; moduleName = this.component; - protected _config!: PluginConfig; /** * * @param config */ - constructor(config: PluginConfig = {}) { + constructor(config: PluginOptions = {}) { super(); this._onDocumentLoaded = this._onDocumentLoaded.bind(this); - this._config = config; } /** diff --git a/packages/opentelemetry-plugin-document-load/test/documentLoad.test.ts b/packages/opentelemetry-plugin-document-load/test/documentLoad.test.ts index c125ae8402..4c40840ed0 100644 --- a/packages/opentelemetry-plugin-document-load/test/documentLoad.test.ts +++ b/packages/opentelemetry-plugin-document-load/test/documentLoad.test.ts @@ -18,9 +18,6 @@ * Can't use Sinon Fake Time here as then cannot stub the performance getEntriesByType with desired metrics */ -import * as assert from 'assert'; -import * as sinon from 'sinon'; - import { ConsoleLogger, TRACE_PARENT_HEADER } from '@opentelemetry/core'; import { BasicTracer, @@ -28,8 +25,9 @@ import { SimpleSpanProcessor, SpanExporter, } from '@opentelemetry/tracing'; -import { Logger, PluginConfig, TimedEvent } from '@opentelemetry/types'; - +import { Logger, PluginOptions, TimedEvent } from '@opentelemetry/types'; +import * as assert from 'assert'; +import * as sinon from 'sinon'; import { ExportResult } from '../../opentelemetry-base/build/src'; import { DocumentLoad } from '../src'; import { PerformanceTimingNames as PTN } from '@opentelemetry/web'; @@ -195,7 +193,7 @@ describe('DocumentLoad Plugin', () => { let moduleExports: any; let tracer: BasicTracer; let logger: Logger; - let config: PluginConfig; + let config: PluginOptions; let spanProcessor: SimpleSpanProcessor; let dummyExporter: DummyExporter; diff --git a/packages/opentelemetry-plugin-grpc/src/grpc.ts b/packages/opentelemetry-plugin-grpc/src/grpc.ts index 2fba28743e..d14da8fe1d 100644 --- a/packages/opentelemetry-plugin-grpc/src/grpc.ts +++ b/packages/opentelemetry-plugin-grpc/src/grpc.ts @@ -27,7 +27,6 @@ import { AttributeNames } from './enums/AttributeNames'; import { grpc, ModuleExportsMapping, - GrpcPluginOptions, ServerCallWithMeta, SendUnaryDataCallback, GrpcClientFunc, @@ -53,11 +52,8 @@ export class GrpcPlugin extends BasePlugin { static readonly component = 'grpc'; readonly supportedVersions = ['1.*']; - protected _config!: GrpcPluginOptions; - constructor(readonly moduleName: string, readonly version: string) { super(); - this._config = {}; } protected readonly _internalFilesList: ModuleExportsMapping = { diff --git a/packages/opentelemetry-plugin-grpc/src/types.ts b/packages/opentelemetry-plugin-grpc/src/types.ts index 15f1f3d119..7f9a634611 100644 --- a/packages/opentelemetry-plugin-grpc/src/types.ts +++ b/packages/opentelemetry-plugin-grpc/src/types.ts @@ -27,8 +27,6 @@ export type SendUnaryDataCallback = ( flags?: grpcModule.writeFlags ) => void; -export interface GrpcPluginOptions {} - interface GrpcStatus { code: number; details: string; diff --git a/packages/opentelemetry-plugin-http/README.md b/packages/opentelemetry-plugin-http/README.md index c1e84e55a8..8076d46b79 100644 --- a/packages/opentelemetry-plugin-http/README.md +++ b/packages/opentelemetry-plugin-http/README.md @@ -31,7 +31,10 @@ const tracer = new NodeTracer({ // You may use a package name or absolute path to the file. path: '@opentelemetry/plugin-http', // http plugin options + options: {} } + // shared plugin options + options: {} } }); ``` diff --git a/packages/opentelemetry-plugin-http/src/http.ts b/packages/opentelemetry-plugin-http/src/http.ts index feeab5a26e..e9c28ea070 100644 --- a/packages/opentelemetry-plugin-http/src/http.ts +++ b/packages/opentelemetry-plugin-http/src/http.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -import { BasePlugin, isValid } from '@opentelemetry/core'; +import { BasePlugin, isValid, matchesAnyPattern } from '@opentelemetry/core'; import { + CanonicalCode, Span, SpanKind, SpanOptions, - CanonicalCode, Status, } from '@opentelemetry/types'; import { @@ -32,17 +32,16 @@ import { import * as semver from 'semver'; import * as shimmer from 'shimmer'; import * as url from 'url'; +import { AttributeNames } from './enums/AttributeNames'; +import { Format } from './enums/Format'; import { - HttpPluginConfig, - Http, + Err, Func, - ResponseEndArgs, - ParsedRequestOptions, + Http, HttpRequestArgs, - Err, + ParsedRequestOptions, + ResponseEndArgs, } from './types'; -import { Format } from './enums/Format'; -import { AttributeNames } from './enums/AttributeNames'; import * as utils from './utils'; import { Socket } from 'net'; @@ -51,7 +50,6 @@ import { Socket } from 'net'; */ export class HttpPlugin extends BasePlugin { readonly component: string; - protected _config!: HttpPluginConfig; /** keep track on spans not ended */ private readonly _spanNotEnded: WeakSet; @@ -60,7 +58,6 @@ export class HttpPlugin extends BasePlugin { // For now component is equal to moduleName but it can change in the future. this.component = this.moduleName; this._spanNotEnded = new WeakSet(); - this._config = {}; } /** Patches HTTP incoming and outcoming request functions. */ @@ -216,11 +213,14 @@ export class HttpPlugin extends BasePlugin { span.setStatus(status); - if (this._config.applyCustomAttributesOnSpan) { + if ( + this._options.http && + this._options.http.applyCustomAttributesOnSpan + ) { this._safeExecute( span, () => - this._config.applyCustomAttributesOnSpan!( + this._options.http!.applyCustomAttributesOnSpan!( span, request, response @@ -276,11 +276,9 @@ export class HttpPlugin extends BasePlugin { plugin._logger.debug('%s plugin incomingRequest', plugin.moduleName); if ( - utils.isIgnored( + matchesAnyPattern( pathname, - plugin._config.ignoreIncomingPaths, - (e: Error) => - plugin._logger.error('caught ignoreIncomingPaths error: ', e) + plugin._options.http && plugin._options.http.ignoreIncomingPaths ) ) { return original.apply(this, [event, ...args]); @@ -293,7 +291,7 @@ export class HttpPlugin extends BasePlugin { kind: SpanKind.SERVER, attributes: utils.getIncomingRequestAttributes(request, { component: plugin.component, - serverName: plugin._config.serverName, + serverName: plugin._options.http && plugin._options.http.serverName, }), }; @@ -333,11 +331,14 @@ export class HttpPlugin extends BasePlugin { .setAttributes(attributes) .setStatus(utils.parseResponseStatus(response.statusCode)); - if (plugin._config.applyCustomAttributesOnSpan) { + if ( + plugin._options.http && + plugin._options.http.applyCustomAttributesOnSpan + ) { plugin._safeExecute( span, () => - plugin._config.applyCustomAttributesOnSpan!( + plugin._options.http!.applyCustomAttributesOnSpan!( span, request, response @@ -386,11 +387,9 @@ export class HttpPlugin extends BasePlugin { if ( utils.isOpenTelemetryRequest(options) || - utils.isIgnored( + matchesAnyPattern( origin + pathname, - plugin._config.ignoreOutgoingUrls, - (e: Error) => - plugin._logger.error('caught ignoreOutgoingUrls error: ', e) + plugin._options.http && plugin._options.http.ignoreOutgoingUrls ) ) { return original.apply(this, [options, ...args]); diff --git a/packages/opentelemetry-plugin-http/src/types.ts b/packages/opentelemetry-plugin-http/src/types.ts index 432235a05a..9857e0a023 100644 --- a/packages/opentelemetry-plugin-http/src/types.ts +++ b/packages/opentelemetry-plugin-http/src/types.ts @@ -14,18 +14,10 @@ * limitations under the License. */ -import { Span, PluginConfig } from '@opentelemetry/types'; -import * as url from 'url'; -import { - ClientRequest, - IncomingMessage, - ServerResponse, - request, - get, -} from 'http'; import * as http from 'http'; +import { get, IncomingMessage, request } from 'http'; +import * as url from 'url'; -export type IgnoreMatcher = string | RegExp | ((url: string) => boolean); export type HttpCallback = (res: IncomingMessage) => void; export type RequestFunction = typeof request; export type GetFunction = typeof get; @@ -49,28 +41,6 @@ export type ResponseEndArgs = | [unknown, ((() => void) | undefined)?] | [unknown, string, ((() => void) | undefined)?]; -export interface HttpCustomAttributeFunction { - ( - span: Span, - request: ClientRequest | IncomingMessage, - response: IncomingMessage | ServerResponse - ): void; -} - -/** - * Options available for the HTTP Plugin (see [documentation](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-plugin-http#http-plugin-options)) - */ -export interface HttpPluginConfig extends PluginConfig { - /** Not trace all incoming requests that match paths */ - ignoreIncomingPaths?: IgnoreMatcher[]; - /** Not trace all outgoing requests that match urls */ - ignoreOutgoingUrls?: IgnoreMatcher[]; - /** Function for adding custom attributes */ - applyCustomAttributesOnSpan?: HttpCustomAttributeFunction; - /** The primary server name of the matched virtual host. */ - serverName?: string; -} - export interface Err extends Error { errno?: number; code?: string; diff --git a/packages/opentelemetry-plugin-http/src/utils.ts b/packages/opentelemetry-plugin-http/src/utils.ts index f49a9978d4..fe5e37a941 100644 --- a/packages/opentelemetry-plugin-http/src/utils.ts +++ b/packages/opentelemetry-plugin-http/src/utils.ts @@ -14,19 +14,19 @@ * limitations under the License. */ -import { Status, CanonicalCode, Span, Attributes } from '@opentelemetry/types'; +import { Attributes, CanonicalCode, Span, Status } from '@opentelemetry/types'; import { - RequestOptions, - IncomingMessage, ClientRequest, IncomingHttpHeaders, + IncomingMessage, OutgoingHttpHeaders, + RequestOptions, ServerResponse, } from 'http'; -import { IgnoreMatcher, Err, ParsedRequestOptions } from './types'; -import { AttributeNames } from './enums/AttributeNames'; -import * as url from 'url'; import { Socket } from 'net'; +import * as url from 'url'; +import { AttributeNames } from './enums/AttributeNames'; +import { Err, ParsedRequestOptions } from './types'; export const OT_REQUEST_HEADER = 'x-opentelemetry-outgoing-request'; /** @@ -104,60 +104,6 @@ export const hasExpectHeader = (options: RequestOptions | url.URL): boolean => { return !!keys.find(key => key.toLowerCase() === 'expect'); }; -/** - * Check whether the given obj match pattern - * @param constant e.g URL of request - * @param obj obj to inspect - * @param pattern Match pattern - */ -export const satisfiesPattern = ( - constant: string, - pattern: IgnoreMatcher -): boolean => { - if (typeof pattern === 'string') { - return pattern === constant; - } else if (pattern instanceof RegExp) { - return pattern.test(constant); - } else if (typeof pattern === 'function') { - return pattern(constant); - } else { - throw new TypeError('Pattern is in unsupported datatype'); - } -}; - -/** - * Check whether the given request is ignored by configuration - * It will not re-throw exceptions from `list` provided by the client - * @param constant e.g URL of request - * @param [list] List of ignore patterns - * @param [onException] callback for doing something when an exception has - * occurred - */ -export const isIgnored = ( - constant: string, - list?: IgnoreMatcher[], - onException?: (error: Error) => void -): boolean => { - if (!list) { - // No ignored urls - trace everything - return false; - } - // Try/catch outside the loop for failing fast - try { - for (const pattern of list) { - if (satisfiesPattern(constant, pattern)) { - return true; - } - } - } catch (e) { - if (onException) { - onException(e); - } - } - - return false; -}; - /** * Sets the span with the error passed in params * @param {Span} span the span that need to be set diff --git a/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts b/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts index fcc49dc30d..6598514f9d 100644 --- a/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts +++ b/packages/opentelemetry-plugin-http/test/functionals/http-enable.test.ts @@ -14,24 +14,29 @@ * limitations under the License. */ +import { NoopLogger } from '@opentelemetry/core'; +import { NodeTracer } from '@opentelemetry/node'; import { InMemorySpanExporter, SimpleSpanProcessor, } from '@opentelemetry/tracing'; -import { NoopLogger } from '@opentelemetry/core'; -import { NodeTracer } from '@opentelemetry/node'; -import { CanonicalCode, Span as ISpan, SpanKind } from '@opentelemetry/types'; +import { + CanonicalCode, + PluginOptions, + Span as ISpan, + SpanKind, +} from '@opentelemetry/types'; import * as assert from 'assert'; import * as http from 'http'; -import * as path from 'path'; import * as nock from 'nock'; +import * as path from 'path'; +import { AttributeNames } from '../../src/enums/AttributeNames'; import { HttpPlugin, plugin } from '../../src/http'; +import { Http } from '../../src/types'; +import { OT_REQUEST_HEADER } from '../../src/utils'; import { assertSpan } from '../utils/assertSpan'; import { DummyPropagation } from '../utils/DummyPropagation'; import { httpRequest } from '../utils/httpRequest'; -import { OT_REQUEST_HEADER } from '../../src/utils'; -import { HttpPluginConfig, Http } from '../../src/types'; -import { AttributeNames } from '../../src/enums/AttributeNames'; const applyCustomAttributesOnSpanErrorMessage = 'bad applyCustomAttributesOnSpan function'; @@ -90,19 +95,21 @@ describe('HttpPlugin', () => { }); before(() => { - const config: HttpPluginConfig = { - ignoreIncomingPaths: [ - (url: string) => { - throw new Error('bad ignoreIncomingPaths function'); + const config: PluginOptions = { + http: { + ignoreIncomingPaths: [ + (url: string) => { + throw new Error('bad ignoreIncomingPaths function'); + }, + ], + ignoreOutgoingUrls: [ + (url: string) => { + throw new Error('bad ignoreOutgoingUrls function'); + }, + ], + applyCustomAttributesOnSpan: () => { + throw new Error(applyCustomAttributesOnSpanErrorMessage); }, - ], - ignoreOutgoingUrls: [ - (url: string) => { - throw new Error('bad ignoreOutgoingUrls function'); - }, - ], - applyCustomAttributesOnSpan: () => { - throw new Error(applyCustomAttributesOnSpanErrorMessage); }, }; pluginWithBadOptions = new HttpPlugin( @@ -173,19 +180,21 @@ describe('HttpPlugin', () => { }); before(() => { - const config: HttpPluginConfig = { - ignoreIncomingPaths: [ - `/ignored/string`, - /\/ignored\/regexp$/i, - (url: string) => url.endsWith(`/ignored/function`), - ], - ignoreOutgoingUrls: [ - `${protocol}://${hostname}:${serverPort}/ignored/string`, - /\/ignored\/regexp$/i, - (url: string) => url.endsWith(`/ignored/function`), - ], - applyCustomAttributesOnSpan: customAttributeFunction, - serverName, + const config: PluginOptions = { + http: { + ignoreIncomingPaths: [ + `/ignored/string`, + /\/ignored\/regexp$/i, + (url: string) => url.endsWith(`/ignored/function`), + ], + ignoreOutgoingUrls: [ + `${protocol}://${hostname}:${serverPort}/ignored/string`, + /\/ignored\/regexp$/i, + (url: string) => url.endsWith(`/ignored/function`), + ], + applyCustomAttributesOnSpan: customAttributeFunction, + serverName, + }, }; plugin.enable(http, tracer, tracer.logger, config); server = http.createServer((request, response) => { diff --git a/packages/opentelemetry-plugin-http/test/functionals/http-package.test.ts b/packages/opentelemetry-plugin-http/test/functionals/http-package.test.ts index 4976e88a33..235716f3c1 100644 --- a/packages/opentelemetry-plugin-http/test/functionals/http-package.test.ts +++ b/packages/opentelemetry-plugin-http/test/functionals/http-package.test.ts @@ -15,7 +15,7 @@ */ import { NoopLogger } from '@opentelemetry/core'; -import { SpanKind } from '@opentelemetry/types'; +import { SpanKind, PluginOptions } from '@opentelemetry/types'; import * as assert from 'assert'; import * as http from 'http'; import * as nock from 'nock'; @@ -34,7 +34,6 @@ import { SimpleSpanProcessor, } from '@opentelemetry/tracing'; -import { HttpPluginConfig } from '../../src/types'; import { customAttributeFunction } from './http-enable.test'; const memoryExporter = new InMemorySpanExporter(); @@ -55,8 +54,10 @@ describe('Packages', () => { }); before(() => { - const config: HttpPluginConfig = { - applyCustomAttributesOnSpan: customAttributeFunction, + const config: PluginOptions = { + http: { + applyCustomAttributesOnSpan: customAttributeFunction, + }, }; plugin.enable(http, tracer, tracer.logger, config); }); diff --git a/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts b/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts index 138cccac81..f959deeddb 100644 --- a/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts +++ b/packages/opentelemetry-plugin-http/test/functionals/utils.test.ts @@ -14,17 +14,14 @@ * limitations under the License. */ -import * as assert from 'assert'; -import * as sinon from 'sinon'; -import * as url from 'url'; -import { CanonicalCode, SpanKind } from '@opentelemetry/types'; import { NoopScopeManager } from '@opentelemetry/scope-base'; -import { IgnoreMatcher } from '../../src/types'; -import * as utils from '../../src/utils'; +import { BasicTracer, Span } from '@opentelemetry/tracing'; +import { CanonicalCode, SpanKind } from '@opentelemetry/types'; +import * as assert from 'assert'; import * as http from 'http'; -import { Span, BasicTracer } from '@opentelemetry/tracing'; +import * as url from 'url'; import { AttributeNames } from '../../src'; -import { NoopLogger } from '@opentelemetry/core'; +import * as utils from '../../src/utils'; describe('Utility', () => { describe('parseResponseStatus()', () => { @@ -97,128 +94,6 @@ describe('Utility', () => { }); }); - describe('satisfiesPattern()', () => { - it('string pattern', () => { - const answer1 = utils.satisfiesPattern('/test/1', '/test/1'); - assert.strictEqual(answer1, true); - const answer2 = utils.satisfiesPattern('/test/1', '/test/11'); - assert.strictEqual(answer2, false); - }); - - it('regex pattern', () => { - const answer1 = utils.satisfiesPattern('/TeSt/1', /\/test/i); - assert.strictEqual(answer1, true); - const answer2 = utils.satisfiesPattern('/2/tEst/1', /\/test/); - assert.strictEqual(answer2, false); - }); - - it('should throw if type is unknown', () => { - try { - utils.satisfiesPattern('/TeSt/1', (true as unknown) as IgnoreMatcher); - assert.fail(); - } catch (error) { - assert.strictEqual(error instanceof TypeError, true); - } - }); - - it('function pattern', () => { - const answer1 = utils.satisfiesPattern( - '/test/home', - (url: string) => url === '/test/home' - ); - assert.strictEqual(answer1, true); - const answer2 = utils.satisfiesPattern( - '/test/home', - (url: string) => url !== '/test/home' - ); - assert.strictEqual(answer2, false); - }); - }); - - describe('isIgnored()', () => { - let satisfiesPatternStub: sinon.SinonSpy<[string, IgnoreMatcher], boolean>; - beforeEach(() => { - satisfiesPatternStub = sinon.spy(utils, 'satisfiesPattern'); - }); - - afterEach(() => { - satisfiesPatternStub.restore(); - }); - - it('should call isSatisfyPattern, n match', () => { - const answer1 = utils.isIgnored('/test/1', ['/test/11']); - assert.strictEqual(answer1, false); - assert.strictEqual( - (utils.satisfiesPattern as sinon.SinonSpy).callCount, - 1 - ); - }); - - it('should call isSatisfyPattern, match for function', () => { - satisfiesPatternStub.restore(); - const answer1 = utils.isIgnored('/test/1', [ - url => url.endsWith('/test/1'), - ]); - assert.strictEqual(answer1, true); - }); - - it('should not re-throw when function throws an exception', () => { - satisfiesPatternStub.restore(); - const log = new NoopLogger(); - const onException = (e: Error) => { - log.error('error', e); - }; - for (const callback of [undefined, onException]) { - assert.doesNotThrow(() => - utils.isIgnored( - '/test/1', - [ - url => { - throw new Error('test'); - }, - ], - callback - ) - ); - } - }); - - it('should call onException when function throws an exception', () => { - satisfiesPatternStub.restore(); - const onException = sinon.spy(); - assert.doesNotThrow(() => - utils.isIgnored( - '/test/1', - [ - url => { - throw new Error('test'); - }, - ], - onException - ) - ); - assert.strictEqual((onException as sinon.SinonSpy).callCount, 1); - }); - - it('should not call isSatisfyPattern', () => { - utils.isIgnored('/test/1', []); - assert.strictEqual( - (utils.satisfiesPattern as sinon.SinonSpy).callCount, - 0 - ); - }); - - it('should return false on empty list', () => { - const answer1 = utils.isIgnored('/test/1', []); - assert.strictEqual(answer1, false); - }); - - it('should not throw and return false when list is undefined', () => { - const answer2 = utils.isIgnored('/test/1', undefined); - assert.strictEqual(answer2, false); - }); - }); - describe('getAbsoluteUrl()', () => { it('should return absolute url with localhost', () => { const path = '/test/1'; diff --git a/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts b/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts index 7f79643e4f..92e3cd0534 100644 --- a/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts +++ b/packages/opentelemetry-plugin-http/test/integrations/http-enable.test.ts @@ -15,22 +15,21 @@ */ import { NoopLogger } from '@opentelemetry/core'; -import { SpanKind, Span } from '@opentelemetry/types'; +import { NodeTracer } from '@opentelemetry/node'; +import { + InMemorySpanExporter, + SimpleSpanProcessor, +} from '@opentelemetry/tracing'; +import { PluginOptions, Span, SpanKind } from '@opentelemetry/types'; import * as assert from 'assert'; import * as http from 'http'; +import * as url from 'url'; +import { AttributeNames } from '../../src/enums/AttributeNames'; import { plugin } from '../../src/http'; import { assertSpan } from '../utils/assertSpan'; import { DummyPropagation } from '../utils/DummyPropagation'; import { httpRequest } from '../utils/httpRequest'; -import * as url from 'url'; import * as utils from '../utils/utils'; -import { NodeTracer } from '@opentelemetry/node'; -import { - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/tracing'; -import { HttpPluginConfig } from '../../src/types'; -import { AttributeNames } from '../../src/enums/AttributeNames'; const protocol = 'http'; const serverPort = 32345; const hostname = 'localhost'; @@ -75,10 +74,12 @@ describe('HttpPlugin Integration tests', () => { /\/ignored\/regexp$/i, (url: string) => url.endsWith(`/ignored/function`), ]; - const config: HttpPluginConfig = { - ignoreIncomingPaths: ignoreConfig, - ignoreOutgoingUrls: ignoreConfig, - applyCustomAttributesOnSpan: customAttributeFunction, + const config: PluginOptions = { + http: { + ignoreIncomingPaths: ignoreConfig, + ignoreOutgoingUrls: ignoreConfig, + applyCustomAttributesOnSpan: customAttributeFunction, + }, }; try { plugin.disable(); diff --git a/packages/opentelemetry-plugin-https/README.md b/packages/opentelemetry-plugin-https/README.md index c8aff10265..e4284a06ec 100644 --- a/packages/opentelemetry-plugin-https/README.md +++ b/packages/opentelemetry-plugin-https/README.md @@ -31,7 +31,10 @@ const tracer = new NodeTracer({ // You may use a package name or absolute path to the file. path: '@opentelemetry/plugin-https', // https plugin options + options: {} } + // shared plugin options + options: {} } }); ``` diff --git a/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts b/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts index c87f3635ca..1ca48f9002 100644 --- a/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts +++ b/packages/opentelemetry-plugin-https/test/functionals/https-enable.test.ts @@ -14,25 +14,29 @@ * limitations under the License. */ -import { - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/tracing'; import { NoopLogger } from '@opentelemetry/core'; import { NodeTracer } from '@opentelemetry/node'; import { + AttributeNames, Http, - HttpPluginConfig, OT_REQUEST_HEADER, - AttributeNames, } from '@opentelemetry/plugin-http'; -import { CanonicalCode, Span as ISpan, SpanKind } from '@opentelemetry/types'; +import { + InMemorySpanExporter, + SimpleSpanProcessor, +} from '@opentelemetry/tracing'; +import { + CanonicalCode, + PluginOptions, + Span as ISpan, + SpanKind, +} from '@opentelemetry/types'; import * as assert from 'assert'; import * as fs from 'fs'; import * as http from 'http'; import * as https from 'https'; -import * as path from 'path'; import * as nock from 'nock'; +import * as path from 'path'; import { HttpsPlugin, plugin } from '../../src/https'; import { assertSpan } from '../utils/assertSpan'; import { DummyPropagation } from '../utils/DummyPropagation'; @@ -95,19 +99,21 @@ describe('HttpsPlugin', () => { }); before(() => { - const config: HttpPluginConfig = { - ignoreIncomingPaths: [ - (url: string) => { - throw new Error('bad ignoreIncomingPaths function'); - }, - ], - ignoreOutgoingUrls: [ - (url: string) => { - throw new Error('bad ignoreOutgoingUrls function'); + const config: PluginOptions = { + http: { + ignoreIncomingPaths: [ + (url: string) => { + throw new Error('bad ignoreIncomingPaths function'); + }, + ], + ignoreOutgoingUrls: [ + (url: string) => { + throw new Error('bad ignoreOutgoingUrls function'); + }, + ], + applyCustomAttributesOnSpan: () => { + throw new Error(applyCustomAttributesOnSpanErrorMessage); }, - ], - applyCustomAttributesOnSpan: () => { - throw new Error(applyCustomAttributesOnSpanErrorMessage); }, }; pluginWithBadOptions = new HttpsPlugin(process.versions.node); @@ -186,19 +192,21 @@ describe('HttpsPlugin', () => { }); before(() => { - const config: HttpPluginConfig = { - ignoreIncomingPaths: [ - `/ignored/string`, - /\/ignored\/regexp$/i, - (url: string) => url.endsWith(`/ignored/function`), - ], - ignoreOutgoingUrls: [ - `${protocol}://${hostname}:${serverPort}/ignored/string`, - /\/ignored\/regexp$/i, - (url: string) => url.endsWith(`/ignored/function`), - ], - applyCustomAttributesOnSpan: customAttributeFunction, - serverName, + const config: PluginOptions = { + http: { + ignoreIncomingPaths: [ + `/ignored/string`, + /\/ignored\/regexp$/i, + (url: string) => url.endsWith(`/ignored/function`), + ], + ignoreOutgoingUrls: [ + `${protocol}://${hostname}:${serverPort}/ignored/string`, + /\/ignored\/regexp$/i, + (url: string) => url.endsWith(`/ignored/function`), + ], + applyCustomAttributesOnSpan: customAttributeFunction, + serverName, + }, }; plugin.enable( (https as unknown) as Http, diff --git a/packages/opentelemetry-plugin-https/test/functionals/https-package.test.ts b/packages/opentelemetry-plugin-https/test/functionals/https-package.test.ts index 811a3e32c5..bb1bf635bf 100644 --- a/packages/opentelemetry-plugin-https/test/functionals/https-package.test.ts +++ b/packages/opentelemetry-plugin-https/test/functionals/https-package.test.ts @@ -15,27 +15,26 @@ */ import { NoopLogger } from '@opentelemetry/core'; -import { SpanKind } from '@opentelemetry/types'; +import { NodeTracer } from '@opentelemetry/node'; +import { Http } from '@opentelemetry/plugin-http'; +import { + InMemorySpanExporter, + SimpleSpanProcessor, +} from '@opentelemetry/tracing'; +import { PluginOptions, SpanKind } from '@opentelemetry/types'; import * as assert from 'assert'; -import * as https from 'https'; +import axios, { AxiosResponse } from 'axios'; +import * as got from 'got'; import * as http from 'http'; +import * as https from 'https'; import * as nock from 'nock'; +import * as path from 'path'; +import * as request from 'request-promise-native'; +import * as superagent from 'superagent'; +import * as url from 'url'; import { plugin } from '../../src/https'; import { assertSpan } from '../utils/assertSpan'; import { DummyPropagation } from '../utils/DummyPropagation'; -import * as url from 'url'; -import axios, { AxiosResponse } from 'axios'; -import * as superagent from 'superagent'; -import * as got from 'got'; -import * as request from 'request-promise-native'; -import * as path from 'path'; -import { NodeTracer } from '@opentelemetry/node'; -import { - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/tracing'; - -import { Http, HttpPluginConfig } from '@opentelemetry/plugin-http'; import { customAttributeFunction } from './https-enable.test'; const memoryExporter = new InMemorySpanExporter(); @@ -56,8 +55,10 @@ describe('Packages', () => { }); before(() => { - const config: HttpPluginConfig = { - applyCustomAttributesOnSpan: customAttributeFunction, + const config: PluginOptions = { + http: { + applyCustomAttributesOnSpan: customAttributeFunction, + }, }; plugin.enable((https as unknown) as Http, tracer, tracer.logger, config); }); diff --git a/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts b/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts index e415816961..ded229c0c4 100644 --- a/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts +++ b/packages/opentelemetry-plugin-https/test/integrations/https-enable.test.ts @@ -15,26 +15,22 @@ */ import { NoopLogger } from '@opentelemetry/core'; +import { NodeTracer } from '@opentelemetry/node'; +import { AttributeNames, Http } from '@opentelemetry/plugin-http'; import { - HttpPluginConfig, - Http, - AttributeNames, -} from '@opentelemetry/plugin-http'; -import { SpanKind, Span } from '@opentelemetry/types'; + InMemorySpanExporter, + SimpleSpanProcessor, +} from '@opentelemetry/tracing'; +import { PluginOptions, Span, SpanKind } from '@opentelemetry/types'; import * as assert from 'assert'; import * as http from 'http'; import * as https from 'https'; +import * as url from 'url'; import { plugin } from '../../src/https'; import { assertSpan } from '../utils/assertSpan'; import { DummyPropagation } from '../utils/DummyPropagation'; import { httpsRequest } from '../utils/httpsRequest'; -import * as url from 'url'; import * as utils from '../utils/utils'; -import { NodeTracer } from '@opentelemetry/node'; -import { - InMemorySpanExporter, - SimpleSpanProcessor, -} from '@opentelemetry/tracing'; const protocol = 'https'; const serverPort = 42345; @@ -80,10 +76,12 @@ describe('HttpsPlugin Integration tests', () => { /\/ignored\/regexp$/i, (url: string) => url.endsWith(`/ignored/function`), ]; - const config: HttpPluginConfig = { - ignoreIncomingPaths: ignoreConfig, - ignoreOutgoingUrls: ignoreConfig, - applyCustomAttributesOnSpan: customAttributeFunction, + const config: PluginOptions = { + http: { + ignoreIncomingPaths: ignoreConfig, + ignoreOutgoingUrls: ignoreConfig, + applyCustomAttributesOnSpan: customAttributeFunction, + }, }; try { plugin.disable(); diff --git a/packages/opentelemetry-plugin-mongodb-core/README.md b/packages/opentelemetry-plugin-mongodb-core/README.md index d4da15c5d4..4f0b351890 100644 --- a/packages/opentelemetry-plugin-mongodb-core/README.md +++ b/packages/opentelemetry-plugin-mongodb-core/README.md @@ -29,7 +29,11 @@ const tracer = new NodeTracer({ enabled: true, // You may use a package name or absolute path to the file. path: '@opentelemetry/plugin-mongodb-core', + // mongodb-core plugin options + options: {} } + // shared plugin options + options: {} } }); ``` diff --git a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/src/pg-pool.ts b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/src/pg-pool.ts index be335c1203..ac58f6bb1a 100644 --- a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/src/pg-pool.ts +++ b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/src/pg-pool.ts @@ -19,16 +19,10 @@ import { CanonicalCode, SpanKind } from '@opentelemetry/types'; import { AttributeNames } from './enums'; import * as shimmer from 'shimmer'; import * as pgPoolTypes from 'pg-pool'; -import { - PostgresPoolPluginOptions, - PgPoolCallback, - PgPoolExtended, -} from './types'; +import { PgPoolCallback, PgPoolExtended } from './types'; import * as utils from './utils'; export class PostgresPoolPlugin extends BasePlugin { - protected _config: PostgresPoolPluginOptions; - static readonly COMPONENT = 'pg-pool'; static readonly DB_TYPE = 'sql'; @@ -36,7 +30,6 @@ export class PostgresPoolPlugin extends BasePlugin { constructor(readonly moduleName: string) { super(); - this._config = {}; } protected patch(): typeof pgPoolTypes { diff --git a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/src/types.ts b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/src/types.ts index 5f9648faf2..233addc28f 100644 --- a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/src/types.ts +++ b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg-pool/src/types.ts @@ -17,8 +17,6 @@ import * as pgTypes from 'pg'; import * as pgPoolTypes from 'pg-pool'; -export interface PostgresPoolPluginOptions {} - export type PgPoolCallback = ( err: Error, client: any, diff --git a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/src/pg.ts b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/src/pg.ts index a6841ae33a..2bb491a2b9 100644 --- a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/src/pg.ts +++ b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/src/pg.ts @@ -17,7 +17,6 @@ import { BasePlugin } from '@opentelemetry/core'; import { CanonicalCode, Span } from '@opentelemetry/types'; import { - PostgresPluginOptions, PgClientExtended, PgPluginQueryConfig, PostgresCallback, @@ -27,8 +26,6 @@ import * as shimmer from 'shimmer'; import * as utils from './utils'; export class PostgresPlugin extends BasePlugin { - protected _config: PostgresPluginOptions; - static readonly COMPONENT = 'pg'; static readonly DB_TYPE = 'sql'; @@ -38,7 +35,6 @@ export class PostgresPlugin extends BasePlugin { constructor(readonly moduleName: string) { super(); - this._config = {}; } protected patch(): typeof pgTypes { diff --git a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/src/types.ts b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/src/types.ts index c9d1f70974..8c065e816d 100644 --- a/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/src/types.ts +++ b/packages/opentelemetry-plugin-postgres/opentelemetry-plugin-pg/src/types.ts @@ -16,8 +16,6 @@ import * as pgTypes from 'pg'; -export interface PostgresPluginOptions {} - export type PostgresCallback = (err: Error, res: object) => unknown; // These are not included in @types/pg, so manually define them. diff --git a/packages/opentelemetry-plugin-redis/README.md b/packages/opentelemetry-plugin-redis/README.md index 25f5f62f3f..4e4dd701e7 100644 --- a/packages/opentelemetry-plugin-redis/README.md +++ b/packages/opentelemetry-plugin-redis/README.md @@ -32,7 +32,11 @@ const tracer = new NodeTracer({ enabled: true, // You may use a package name or absolute path to the file. path: '@opentelemetry/plugin-redis', + // redis plugin options + options: {} } + // shared plugin options + options: {} } }); ``` diff --git a/packages/opentelemetry-plugin-xml-http-request/src/types.ts b/packages/opentelemetry-plugin-xml-http-request/src/types.ts index 92c64f982f..906d1f502c 100644 --- a/packages/opentelemetry-plugin-xml-http-request/src/types.ts +++ b/packages/opentelemetry-plugin-xml-http-request/src/types.ts @@ -64,9 +64,3 @@ export interface XhrMem { // callback to remove events from xhr once the span ends callbackToRemoveEvents?: Function; } - -export type PropagateTraceHeaderCorsUrl = string | RegExp; - -export type PropagateTraceHeaderCorsUrls = - | PropagateTraceHeaderCorsUrl - | PropagateTraceHeaderCorsUrl[]; diff --git a/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts b/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts index 82375d03aa..ef9e70e6db 100644 --- a/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts +++ b/packages/opentelemetry-plugin-xml-http-request/src/xhr.ts @@ -17,10 +17,10 @@ import { BasePlugin, hrTime, - isUrlIgnored, isWrapped, + matchesAnyPattern, + matchesPattern, otperformance, - urlMatches, } from '@opentelemetry/core'; import * as types from '@opentelemetry/types'; import { @@ -33,12 +33,7 @@ import * as shimmer from 'shimmer'; import { AttributeNames } from './enums/AttributeNames'; import { EventNames } from './enums/EventNames'; import { Format } from './enums/Format'; -import { - OpenFunction, - PropagateTraceHeaderCorsUrls, - SendFunction, - XhrMem, -} from './types'; +import { OpenFunction, SendFunction, XhrMem } from './types'; import { VERSION } from './version'; // how long to wait for observer to collect information about resources @@ -47,20 +42,6 @@ import { VERSION } from './version'; // safe enough const OBSERVER_WAIT_TIME_MS = 300; -/** - * XMLHttpRequest config - */ -export interface XMLHttpRequestPluginConfig extends types.PluginConfig { - // the number of timing resources is limited, after the limit - // (chrome 250, safari 150) the information is not collected anymore - // the only way to prevent that is to regularly clean the resources - // whenever it is possible, this is needed only when PerformanceObserver - // is not available - clearTimingResources?: boolean; - // urls which should include trace headers when origin doesn't match - propagateTraceHeaderCorsUrls?: PropagateTraceHeaderCorsUrls; -} - /** * This class represents a XMLHttpRequest plugin for auto instrumentation */ @@ -69,15 +50,19 @@ export class XMLHttpRequestPlugin extends BasePlugin { readonly version: string = VERSION; moduleName = this.component; - protected _config!: XMLHttpRequestPluginConfig; + private _xhrOptions: types.XMLHttpRequestPluginOptions; + private _ignoreOutgoingUrls: types.IgnoreMatcher[]; private _tasksCount = 0; private _xhrMem = new WeakMap(); private _usedResources = new WeakSet(); - constructor(config: XMLHttpRequestPluginConfig = {}) { + constructor(config: types.PluginOptions = {}) { super(); - this._config = config; + this._options = config; + this._xhrOptions = config.xhr || {}; + this._ignoreOutgoingUrls = + (config.http && config.http.ignoreOutgoingUrls) || []; } /** @@ -101,13 +86,13 @@ export class XMLHttpRequestPlugin extends BasePlugin { } /** - * checks if trace headers shoudl be propagated + * checks if trace headers should be propagated * @param spanUrl * @private */ _shouldPropagateTraceHeaders(spanUrl: string) { let propagateTraceHeaderUrls = - this._config.propagateTraceHeaderCorsUrls || []; + this._xhrOptions.propagateTraceHeaderCorsUrls || []; if ( typeof propagateTraceHeaderUrls === 'string' || propagateTraceHeaderUrls instanceof RegExp @@ -120,7 +105,7 @@ export class XMLHttpRequestPlugin extends BasePlugin { return true; } else { for (const propagateTraceHeaderUrl of propagateTraceHeaderUrls) { - if (urlMatches(spanUrl, propagateTraceHeaderUrl)) { + if (matchesPattern(spanUrl, propagateTraceHeaderUrl)) { return true; } } @@ -232,7 +217,7 @@ export class XMLHttpRequestPlugin extends BasePlugin { * @private */ private _clearResources() { - if (this._tasksCount === 0 && this._config.clearTimingResources) { + if (this._tasksCount === 0 && this._xhrOptions.clearTimingResources) { ((otperformance as unknown) as Performance).clearResourceTimings(); this._xhrMem = new WeakMap(); this._usedResources = new WeakSet(); @@ -316,7 +301,7 @@ export class XMLHttpRequestPlugin extends BasePlugin { url: string, method: string ): types.Span | undefined { - if (isUrlIgnored(url, this._config.ignoreUrls)) { + if (matchesAnyPattern(url, this._ignoreOutgoingUrls)) { this._logger.debug('ignoring span as url matches ignored url'); return; } diff --git a/packages/opentelemetry-plugin-xml-http-request/test/xhr.test.ts b/packages/opentelemetry-plugin-xml-http-request/test/xhr.test.ts index 2d920b0bfb..08d95a4e59 100644 --- a/packages/opentelemetry-plugin-xml-http-request/test/xhr.test.ts +++ b/packages/opentelemetry-plugin-xml-http-request/test/xhr.test.ts @@ -142,7 +142,9 @@ describe('xhr', () => { scopeManager: new ZoneScopeManager(), plugins: [ new XMLHttpRequestPlugin({ - propagateTraceHeaderCorsUrls: propagateTraceHeaderCorsUrls, + xhr: { + propagateTraceHeaderCorsUrls: propagateTraceHeaderCorsUrls, + }, }), ], }); diff --git a/packages/opentelemetry-types/src/trace/instrumentation/Plugin.ts b/packages/opentelemetry-types/src/trace/instrumentation/Plugin.ts index efc63f8edf..235b17c986 100644 --- a/packages/opentelemetry-types/src/trace/instrumentation/Plugin.ts +++ b/packages/opentelemetry-types/src/trace/instrumentation/Plugin.ts @@ -14,11 +14,12 @@ * limitations under the License. */ -import { Tracer } from '../tracer'; +import { ClientRequest, IncomingMessage, ServerResponse } from 'http'; import { Logger } from '../../common/Logger'; +import { Span } from '../span'; +import { Tracer } from '../tracer'; /** Interface Plugin to apply patch. */ -// tslint:disable-next-line:no-any export interface Plugin { /** * Contains all supported versions. @@ -40,7 +41,7 @@ export interface Plugin { moduleExports: T, tracer: Tracer, logger: Logger, - config?: PluginConfig + config?: PluginOptions ): T; /** Method to disable the instrumentation */ @@ -61,23 +62,18 @@ export interface PluginConfig { path?: string; /** - * Request methods that match any string in ignoreMethods will not be traced. - */ - ignoreMethods?: string[]; - - /** - * URLs that partially match any regex in ignoreUrls will not be traced. - * In addition, URLs that are _exact matches_ of strings in ignoreUrls will - * also not be traced. + * These plugin options override the values provided in the + * shared plugin options section. */ - ignoreUrls?: Array; + options?: PluginOptions; +} - /** - * List of internal files that need patch and are not exported by - * default. - */ - internalFilesExports?: PluginInternalFiles; +export type IgnoreMatcher = string | RegExp | ((url: string) => boolean); +/** + * These options are used by database plugins like mysql, pg, and mongodb. + */ +export interface DatabasePluginOptions { /** * If true, additional information about query parameters and * results will be attached (as `attributes`) to spans representing @@ -86,6 +82,76 @@ export interface PluginConfig { enhancedDatabaseReporting?: boolean; } +/** + * These options are used by dns module plugins. + */ +export interface DNSPluginOptions { + /** + * Used by dns plugin. Ignores tracing for host names which match one of + * the configured matchers by either being an exact string match, matching + * a regular expression, or evaluating to true. + */ + ignoreHostnames?: IgnoreMatcher[]; +} + +export interface HttpCustomAttributeFunction { + ( + span: Span, + request: ClientRequest | IncomingMessage, + response: IncomingMessage | ServerResponse + ): void; +} + +/** + * These options are used by http plugins like http, https, and http2. + */ +export interface HttpPluginOptions { + /** Not trace all incoming requests that match paths */ + ignoreIncomingPaths?: IgnoreMatcher[]; + /** Not trace all outgoing requests that match urls */ + ignoreOutgoingUrls?: IgnoreMatcher[]; + /** Function for adding custom attributes */ + applyCustomAttributesOnSpan?: HttpCustomAttributeFunction; + /** The primary server name of the matched virtual host. */ + serverName?: string; +} + +export type PropagateTraceHeaderCorsUrl = string | RegExp; + +export type PropagateTraceHeaderCorsUrls = + | PropagateTraceHeaderCorsUrl + | PropagateTraceHeaderCorsUrl[]; + +/** + * XMLHttpRequest config + */ +export interface XMLHttpRequestPluginOptions { + // the number of timing resources is limited, after the limit + // (chrome 250, safari 150) the information is not collected anymore + // the only way to prevent that is to regularly clean the resources + // whenever it is possible, this is needed only when PerformanceObserver + // is not available + clearTimingResources?: boolean; + // urls which should include trace headers when origin doesn't match + propagateTraceHeaderCorsUrls?: PropagateTraceHeaderCorsUrls; +} + +/** + * This is a configuration section where plugin authors + * can define their own custom configuration options. + */ +export interface CustomPluginOptions { + [key: string]: number | string | boolean | undefined; +} + +export interface PluginOptions { + database?: DatabasePluginOptions; + dns?: DNSPluginOptions; + http?: HttpPluginOptions; + xhr?: XMLHttpRequestPluginOptions; + custom?: CustomPluginOptions; +} + export interface PluginInternalFilesVersion { [pluginName: string]: string; }