From 8ff1f91138f252da28ce22a94fb149553b94ecc9 Mon Sep 17 00:00:00 2001 From: Bartlomiej Obecny Date: Tue, 8 Dec 2020 14:33:49 +0100 Subject: [PATCH 1/8] feat: creating one auto loader for instrumentation and old plugins --- .../src/autoLoader.ts | 48 ++++ .../src/autoLoaderUtils.ts | 79 ++++++ .../src/index.ts | 1 + .../src/platform/browser/index.ts | 1 + .../src/platform/browser/old/autoLoader.ts | 31 +++ .../src/platform/node/index.ts | 1 + .../src/platform/node/old/PluginLoader.ts | 229 ++++++++++++++++++ .../src/platform/node/old/autoLoader.ts | 78 ++++++ .../src/platform/node/old/utils.ts | 76 ++++++ .../src/types.ts | 27 +++ .../src/types_plugin_only.ts | 116 +++++++++ 11 files changed, 687 insertions(+) create mode 100644 packages/opentelemetry-instrumentation/src/autoLoader.ts create mode 100644 packages/opentelemetry-instrumentation/src/autoLoaderUtils.ts create mode 100644 packages/opentelemetry-instrumentation/src/platform/browser/old/autoLoader.ts create mode 100644 packages/opentelemetry-instrumentation/src/platform/node/old/PluginLoader.ts create mode 100644 packages/opentelemetry-instrumentation/src/platform/node/old/autoLoader.ts create mode 100644 packages/opentelemetry-instrumentation/src/platform/node/old/utils.ts create mode 100644 packages/opentelemetry-instrumentation/src/types_plugin_only.ts diff --git a/packages/opentelemetry-instrumentation/src/autoLoader.ts b/packages/opentelemetry-instrumentation/src/autoLoader.ts new file mode 100644 index 0000000000..13e333c128 --- /dev/null +++ b/packages/opentelemetry-instrumentation/src/autoLoader.ts @@ -0,0 +1,48 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as api from '@opentelemetry/api'; +import { + enableInstrumentations, + parseInstrumentationOptions, +} from './autoLoaderUtils'; +import { loadOldPlugins } from './platform'; +import { AutoLoaderOptions } from './types'; + +export function registerInstrumentations(options: AutoLoaderOptions) { + const { + instrumentations, + pluginsNode, + pluginsWeb, + } = parseInstrumentationOptions(options.instrumentations); + const tracerWithLogger = (options.tracerProvider as unknown) as { + logger: api.Logger; + }; + const tracerProvider = + options.tracerProvider || api.trace.getTracerProvider(); + const meterProvider = options.meterProvider || api.metrics.getMeterProvider(); + const logger = + options.logger || tracerWithLogger?.logger || new api.NoopLogger(); + + enableInstrumentations( + instrumentations, + logger, + tracerProvider, + meterProvider + ); + + loadOldPlugins(pluginsNode, pluginsWeb, logger, tracerProvider); +} diff --git a/packages/opentelemetry-instrumentation/src/autoLoaderUtils.ts b/packages/opentelemetry-instrumentation/src/autoLoaderUtils.ts new file mode 100644 index 0000000000..5e6a92cb11 --- /dev/null +++ b/packages/opentelemetry-instrumentation/src/autoLoaderUtils.ts @@ -0,0 +1,79 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Logger, MeterProvider, TracerProvider } from '@opentelemetry/api'; +import { InstrumentationBase } from './platform'; +import { + AutoLoaderResult, + Instrumentation, + InstrumentationOption, +} from './types'; + +import { + NodePlugins, + NodePluginsTracerConfiguration, + OldClassPlugin, +} from './types_plugin_only'; + +export function parseInstrumentationOptions( + options: InstrumentationOption[] +): AutoLoaderResult { + let instrumentations: Instrumentation[] = []; + let pluginsNode: NodePlugins = {}; + const pluginsWeb: OldClassPlugin[] = []; + for (let i = 0, j = options.length; i < j; i++) { + const option = options[i]; + const OptionClass = option as any; + + if (OptionClass.prototype instanceof InstrumentationBase) { + instrumentations.push(new OptionClass()); + } else if (option instanceof InstrumentationBase) { + instrumentations.push(option); + } else if (Array.isArray(option)) { + instrumentations = instrumentations.concat( + parseInstrumentationOptions(option).instrumentations + ); + } else if ((option as NodePluginsTracerConfiguration).plugins) { + pluginsNode = Object.assign( + {}, + pluginsNode, + (option as NodePluginsTracerConfiguration).plugins + ); + } else { + pluginsWeb.push(option as OldClassPlugin); + } + } + + return { instrumentations, pluginsNode, pluginsWeb }; +} + +export function enableInstrumentations( + instrumentations: Instrumentation[], + logger: Logger, + tracerProvider?: TracerProvider, + meterProvider?: MeterProvider +) { + for (let i = 0, j = instrumentations.length; i < j; i++) { + const instrumentation = instrumentations[i]; + if (tracerProvider) { + instrumentation.setTracerProvider(tracerProvider); + } + if (meterProvider) { + instrumentation.setMeterProvider(meterProvider); + } + instrumentation.enable(); + } +} diff --git a/packages/opentelemetry-instrumentation/src/index.ts b/packages/opentelemetry-instrumentation/src/index.ts index acb0986848..078c877124 100644 --- a/packages/opentelemetry-instrumentation/src/index.ts +++ b/packages/opentelemetry-instrumentation/src/index.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +export * from './autoLoader'; export * from './platform/index'; export * from './types'; export * from './utils'; diff --git a/packages/opentelemetry-instrumentation/src/platform/browser/index.ts b/packages/opentelemetry-instrumentation/src/platform/browser/index.ts index 24c76056a1..fc42da7384 100644 --- a/packages/opentelemetry-instrumentation/src/platform/browser/index.ts +++ b/packages/opentelemetry-instrumentation/src/platform/browser/index.ts @@ -15,3 +15,4 @@ */ export * from './instrumentation'; +export * from './old/autoLoader'; diff --git a/packages/opentelemetry-instrumentation/src/platform/browser/old/autoLoader.ts b/packages/opentelemetry-instrumentation/src/platform/browser/old/autoLoader.ts new file mode 100644 index 0000000000..7782b7b49d --- /dev/null +++ b/packages/opentelemetry-instrumentation/src/platform/browser/old/autoLoader.ts @@ -0,0 +1,31 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This should be removed after plugins are gone + +import * as api from '@opentelemetry/api'; +import { NodePlugins, OldClassPlugin } from '../../../types_plugin_only'; + +export function loadOldPlugins( + pluginsNode: NodePlugins, + pluginsWeb: OldClassPlugin[], + logger: api.Logger, + tracerProvider: api.TracerProvider +): void { + pluginsWeb.forEach(plugin => { + plugin.enable([], tracerProvider, logger); + }); +} diff --git a/packages/opentelemetry-instrumentation/src/platform/node/index.ts b/packages/opentelemetry-instrumentation/src/platform/node/index.ts index 42310c807a..e5f3252405 100644 --- a/packages/opentelemetry-instrumentation/src/platform/node/index.ts +++ b/packages/opentelemetry-instrumentation/src/platform/node/index.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +export * from './old/autoLoader'; export * from './instrumentation'; export * from './instrumentationNodeModuleDefinition'; export * from './instrumentationNodeModuleFile'; diff --git a/packages/opentelemetry-instrumentation/src/platform/node/old/PluginLoader.ts b/packages/opentelemetry-instrumentation/src/platform/node/old/PluginLoader.ts new file mode 100644 index 0000000000..9580b7e0b0 --- /dev/null +++ b/packages/opentelemetry-instrumentation/src/platform/node/old/PluginLoader.ts @@ -0,0 +1,229 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This is copy from previous version, should be removed after plugins are gone + +import { Logger, TracerProvider } from '@opentelemetry/api'; +import * as RequireInTheMiddle from 'require-in-the-middle'; +import { OldClassPlugin, OldPluginConfig } from '../../../types_plugin_only'; +import * as utils from './utils'; + +// States for the Plugin Loader +export enum HookState { + UNINITIALIZED, + ENABLED, + DISABLED, +} + +/** + * Environment variable which will contain list of modules to not load corresponding plugins for + * e.g.OTEL_NO_PATCH_MODULES=pg,https,mongodb + */ +export const ENV_PLUGIN_DISABLED_LIST = 'OTEL_NO_PATCH_MODULES'; + +/** + * Wildcard symbol. If ignore list is set to this, disable all plugins + */ +const DISABLE_ALL_PLUGINS = '*'; + +export interface Plugins { + [pluginName: string]: OldPluginConfig; +} + +/** + * Returns the Plugins object that meet the below conditions. + * Valid criteria: 1. It should be enabled. 2. Should have non-empty path. + */ +function filterPlugins(plugins: Plugins): Plugins { + const keys = Object.keys(plugins); + return keys.reduce((acc: Plugins, key: string) => { + if (plugins[key].enabled && (plugins[key].path || plugins[key].plugin)) { + acc[key] = plugins[key]; + } + return acc; + }, {}); +} + +/** + * Parse process.env[ENV_PLUGIN_DISABLED_LIST] for a list of modules + * not to load corresponding plugins for. + */ +function getIgnoreList(): string[] | typeof DISABLE_ALL_PLUGINS { + const envIgnoreList: string = process.env[ENV_PLUGIN_DISABLED_LIST] || ''; + if (envIgnoreList === DISABLE_ALL_PLUGINS) { + return envIgnoreList; + } + return envIgnoreList.split(',').map(v => v.trim()); +} + +/** + * The PluginLoader class can load instrumentation plugins that use a patch + * mechanism to enable automatic tracing for specific target modules. + */ +export class PluginLoader { + /** A list of loaded plugins. */ + private _plugins: OldClassPlugin[] = []; + /** + * A field that tracks whether the require-in-the-middle hook has been loaded + * for the first time, as well as whether the hook body is activated or not. + */ + private _hookState = HookState.UNINITIALIZED; + + /** Constructs a new PluginLoader instance. */ + constructor(readonly provider: TracerProvider, readonly logger: Logger) {} + + /** + * Loads a list of plugins. Each plugin module should implement the core + * {@link Plugin} interface and export an instance named as 'plugin'. This + * function will attach a hook to be called the first time the module is + * loaded. + * @param Plugins an object whose keys are plugin names and whose + * {@link OldPluginConfig} values indicate several configuration options. + */ + load(plugins: Plugins): PluginLoader { + if (this._hookState === HookState.UNINITIALIZED) { + const pluginsToLoad = filterPlugins(plugins); + const modulesToHook = Object.keys(pluginsToLoad); + const modulesToIgnore = getIgnoreList(); + // Do not hook require when no module is provided. In this case it is + // not necessary. With skipping this step we lower our footprint in + // customer applications and require-in-the-middle won't show up in CPU + // frames. + if (modulesToHook.length === 0) { + this._hookState = HookState.DISABLED; + return this; + } + + const alreadyRequiredModules = Object.keys(require.cache); + const requiredModulesToHook = modulesToHook.filter( + name => + alreadyRequiredModules.find(cached => { + try { + return require.resolve(name) === cached; + } catch (err) { + return false; + } + }) !== undefined + ); + if (requiredModulesToHook.length > 0) { + this.logger.warn( + `Some modules (${requiredModulesToHook.join( + ', ' + )}) were already required when their respective plugin was loaded, some plugins might not work. Make sure the SDK is setup before you require in other modules.` + ); + } + + // Enable the require hook. + RequireInTheMiddle(modulesToHook, (exports, name, baseDir) => { + if (this._hookState !== HookState.ENABLED) return exports; + const config = pluginsToLoad[name]; + const modulePath = config.path!; + const modulePlugin = config.plugin; + let version = null; + + if (!baseDir) { + // basedir is the directory where the module is located, + // or undefined for core modules. + // lets plugins restrict what they support for core modules (see plugin.supportedVersions) + version = process.versions.node; + } else { + // Get the module version. + version = utils.getPackageVersion(this.logger, baseDir); + } + + // Skip loading of all modules if '*' is provided + if (modulesToIgnore === DISABLE_ALL_PLUGINS) { + this.logger.info( + `PluginLoader#load: skipped patching module ${name} because all plugins are disabled (${ENV_PLUGIN_DISABLED_LIST})` + ); + return exports; + } + + if (modulesToIgnore.includes(name)) { + this.logger.info( + `PluginLoader#load: skipped patching module ${name} because it was on the ignore list (${ENV_PLUGIN_DISABLED_LIST})` + ); + return exports; + } + + this.logger.info( + `PluginLoader#load: trying to load ${name}@${version}` + ); + + if (!version) return exports; + + this.logger.debug( + `PluginLoader#load: applying patch to ${name}@${version} using ${modulePath} module` + ); + + // Expecting a plugin from module; + try { + const plugin: OldClassPlugin = + modulePlugin ?? require(modulePath).plugin; + if (!utils.isSupportedVersion(version, plugin.supportedVersions)) { + this.logger.warn( + `PluginLoader#load: Plugin ${name} only supports module ${plugin.moduleName} with the versions: ${plugin.supportedVersions}` + ); + return exports; + } + if (plugin.moduleName !== name) { + this.logger.error( + `PluginLoader#load: Entry ${name} use a plugin that instruments ${plugin.moduleName}` + ); + return exports; + } + + this._plugins.push(plugin); + // Enable each supported plugin. + return plugin.enable(exports, this.provider, this.logger, config); + } catch (e) { + this.logger.error( + `PluginLoader#load: could not load plugin ${modulePath} of module ${name}. Error: ${e.message}` + ); + return exports; + } + }); + this._hookState = HookState.ENABLED; + } else if (this._hookState === HookState.DISABLED) { + this.logger.error( + 'PluginLoader#load: Currently cannot re-enable plugin loader.' + ); + } else { + this.logger.error('PluginLoader#load: Plugin loader already enabled.'); + } + return this; + } + + /** Unloads plugins. */ + unload(): PluginLoader { + if (this._hookState === HookState.ENABLED) { + for (const plugin of this._plugins) { + plugin.disable(); + } + this._plugins = []; + this._hookState = HookState.DISABLED; + } + return this; + } +} + +/** + * Adds a search path for plugin modules. Intended for testing purposes only. + * @param searchPath The path to add. + */ +export function searchPathForTest(searchPath: string) { + module.paths.push(searchPath); +} diff --git a/packages/opentelemetry-instrumentation/src/platform/node/old/autoLoader.ts b/packages/opentelemetry-instrumentation/src/platform/node/old/autoLoader.ts new file mode 100644 index 0000000000..1e20234bf9 --- /dev/null +++ b/packages/opentelemetry-instrumentation/src/platform/node/old/autoLoader.ts @@ -0,0 +1,78 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This should be removed after plugins are gone + +import * as api from '@opentelemetry/api'; +import { NodePlugins, OldClassPlugin } from '../../../types_plugin_only'; +import { PluginLoader } from './PluginLoader'; + +/** List of all default supported plugins */ +export const DEFAULT_INSTRUMENTATION_PLUGINS: NodePlugins = { + mongodb: { enabled: true, path: '@opentelemetry/plugin-mongodb' }, + grpc: { enabled: true, path: '@opentelemetry/plugin-grpc' }, + '@grpc/grpc-js': { enabled: true, path: '@opentelemetry/plugin-grpc-js' }, + http: { enabled: true, path: '@opentelemetry/plugin-http' }, + https: { enabled: true, path: '@opentelemetry/plugin-https' }, + mysql: { enabled: true, path: '@opentelemetry/plugin-mysql' }, + pg: { enabled: true, path: '@opentelemetry/plugin-pg' }, + redis: { enabled: true, path: '@opentelemetry/plugin-redis' }, + ioredis: { enabled: true, path: '@opentelemetry/plugin-ioredis' }, + 'pg-pool': { enabled: true, path: '@opentelemetry/plugin-pg-pool' }, + express: { enabled: true, path: '@opentelemetry/plugin-express' }, + '@hapi/hapi': { enabled: true, path: '@opentelemetry/hapi-instrumentation' }, + koa: { enabled: true, path: '@opentelemetry/koa-instrumentation' }, + dns: { enabled: true, path: '@opentelemetry/plugin-dns' }, +}; + +export function loadOldPlugins( + pluginsNode: NodePlugins, + pluginsWeb: OldClassPlugin[], + logger: api.Logger, + tracerProvider: api.TracerProvider +): void { + let allPlugins; + if (Object.keys(pluginsNode).length > 0) { + // allPlugins = mergePlugins(DEFAULT_INSTRUMENTATION_PLUGINS, pluginsNode); + allPlugins = mergePlugins({}, pluginsNode); + } else { + allPlugins = pluginsNode; + } + const pluginLoader = new PluginLoader(tracerProvider, logger); + pluginLoader.load(allPlugins); +} + +function mergePlugins( + defaultPlugins: NodePlugins, + userSuppliedPlugins: NodePlugins +): NodePlugins { + const mergedUserSuppliedPlugins: NodePlugins = {}; + + 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: NodePlugins = { + ...defaultPlugins, + ...mergedUserSuppliedPlugins, + }; + + return mergedPlugins; +} diff --git a/packages/opentelemetry-instrumentation/src/platform/node/old/utils.ts b/packages/opentelemetry-instrumentation/src/platform/node/old/utils.ts new file mode 100644 index 0000000000..f02e48a703 --- /dev/null +++ b/packages/opentelemetry-instrumentation/src/platform/node/old/utils.ts @@ -0,0 +1,76 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This is copy from previous version, should be removed after plugins are gone + +import { Logger } from '@opentelemetry/api'; +import * as path from 'path'; +import * as semver from 'semver'; + +/** + * Gets the package version. + * @param logger The logger to use. + * @param basedir The base directory. + */ +export function getPackageVersion( + logger: Logger, + basedir: string +): string | null { + const pjsonPath = path.join(basedir, 'package.json'); + try { + const version = require(pjsonPath).version; + // Attempt to parse a string as a semantic version, returning either a + // SemVer object or null. + if (!semver.parse(version)) { + logger.error( + `getPackageVersion: [${pjsonPath}|${version}] Version string could not be parsed.` + ); + return null; + } + return version; + } catch (e) { + logger.error( + `getPackageVersion: [${pjsonPath}] An error occurred while retrieving version string. ${e.message}` + ); + return null; + } +} + +/** + * Determines if a version is supported + * @param moduleVersion a version in [semver](https://semver.org/spec/v2.0.0.html) format. + * @param [supportedVersions] a list of supported versions ([semver](https://semver.org/spec/v2.0.0.html) format). + */ +export function isSupportedVersion( + moduleVersion: string, + supportedVersions?: string[] +) { + if (!Array.isArray(supportedVersions) || supportedVersions.length === 0) { + return true; + } + + return supportedVersions.some(supportedVersion => + semver.satisfies(moduleVersion, supportedVersion) + ); +} + +/** + * Adds a search path for plugin modules. Intended for testing purposes only. + * @param searchPath The path to add. + */ +export function searchPathForTest(searchPath: string) { + module.paths.push(searchPath); +} diff --git a/packages/opentelemetry-instrumentation/src/types.ts b/packages/opentelemetry-instrumentation/src/types.ts index a6501d3aad..b8558ad1fc 100644 --- a/packages/opentelemetry-instrumentation/src/types.ts +++ b/packages/opentelemetry-instrumentation/src/types.ts @@ -15,6 +15,12 @@ */ import { Logger, MeterProvider, TracerProvider } from '@opentelemetry/api'; +import { InstrumentationBase } from './platform'; +import { + NodePlugins, + NodePluginsTracerConfiguration, + OldClassPlugin, +} from './types_plugin_only'; /** Interface Instrumentation to apply patch. */ export interface Instrumentation { @@ -77,3 +83,24 @@ export interface ShimWrapped { __unwrap: Function; __original: Function; } + +export type InstrumentationOption = + | InstrumentationBase + | InstrumentationBase[] + | Instrumentation + | Instrumentation[] + | NodePluginsTracerConfiguration + | OldClassPlugin; + +export interface AutoLoaderResult { + instrumentations: Instrumentation[]; + pluginsNode: NodePlugins; + pluginsWeb: OldClassPlugin[]; +} + +export interface AutoLoaderOptions { + instrumentations: InstrumentationOption[]; + tracerProvider?: TracerProvider; + meterProvider?: MeterProvider; + logger?: Logger; +} diff --git a/packages/opentelemetry-instrumentation/src/types_plugin_only.ts b/packages/opentelemetry-instrumentation/src/types_plugin_only.ts new file mode 100644 index 0000000000..967ea3559b --- /dev/null +++ b/packages/opentelemetry-instrumentation/src/types_plugin_only.ts @@ -0,0 +1,116 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Logger, TracerProvider } from '@opentelemetry/api'; + +export interface NodePlugins { + [pluginName: string]: OldPluginConfig; +} + +export interface NodePluginsTracerConfiguration { + plugins: NodePlugins; +} + +/** Interface Plugin to apply patch. */ +export interface OldClassPlugin { + /** + * Contains all supported versions. + * All versions must be compatible with [semver](https://semver.org/spec/v2.0.0.html) format. + * If the version is not supported, we won't apply instrumentation patch (see `enable` method). + * If omitted, all versions of the module will be patched. + */ + supportedVersions?: string[]; + + /** + * Name of the module that the plugin instrument. + */ + moduleName: string; + + /** + * Method that enables the instrumentation patch. + * @param moduleExports The value of the `module.exports` property that would + * normally be exposed by the required module. ex: `http`, `https` etc. + * @param TracerProvider a tracer provider. + * @param logger a logger instance. + * @param [config] an object to configure the plugin. + */ + enable( + moduleExports: T, + TracerProvider: TracerProvider, + logger: Logger, + config?: OldPluginConfig + ): T; + + /** Method to disable the instrumentation */ + disable(): void; +} + +export interface OldPluginConfig { + /** + * Whether to enable the plugin. + * @default true + */ + enabled?: boolean; + + /** + * Path of the trace plugin to load. + * @default '@opentelemetry/plugin-http' in case of http. + */ + path?: string; + + /** + * Plugin to load + * @example import {plugin} from '@opentelemetry/plugin-http' in case of http. + */ + plugin?: OldClassPlugin; + + /** + * 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. + */ + ignoreUrls?: Array; + + /** + * List of internal files that need patch and are not exported by + * default. + */ + internalFilesExports?: PluginInternalFiles; + + /** + * If true, additional information about query parameters and + * results will be attached (as `attributes`) to spans representing + * database operations. + */ + enhancedDatabaseReporting?: boolean; +} + +export interface PluginInternalFilesVersion { + [pluginName: string]: string; +} + +/** + * Each key should be the name of the module to trace, and its value + * a mapping of a property name to a internal plugin file name. + */ +export interface PluginInternalFiles { + [versions: string]: PluginInternalFilesVersion; +} From e52158503f8c482366f0ba93350dfc24304cefc6 Mon Sep 17 00:00:00 2001 From: Bartlomiej Obecny Date: Tue, 8 Dec 2020 15:28:11 +0100 Subject: [PATCH 2/8] feat: adding temporary tests for autoloader --- examples/test/README.md | 1 + examples/test/client.js | 49 +++++ examples/test/docker/collector-config.yaml | 29 +++ examples/test/docker/docker-compose.yaml | 30 +++ examples/test/docker/prometheus.yaml | 9 + examples/test/package.json | 52 +++++ examples/test/schema.js | 202 ++++++++++++++++++ examples/test/server-express.js | 19 ++ examples/test/tracer.js | 88 ++++++++ .../examples/xml-http-request/index.js | 23 +- examples/tracer-web/package.json | 5 +- examples/tracer-web/webpack.config.js | 2 +- lerna.json | 2 + .../tsconfig.json | 1 + 14 files changed, 507 insertions(+), 5 deletions(-) create mode 100644 examples/test/README.md create mode 100644 examples/test/client.js create mode 100644 examples/test/docker/collector-config.yaml create mode 100644 examples/test/docker/docker-compose.yaml create mode 100644 examples/test/docker/prometheus.yaml create mode 100644 examples/test/package.json create mode 100644 examples/test/schema.js create mode 100644 examples/test/server-express.js create mode 100644 examples/test/tracer.js diff --git a/examples/test/README.md b/examples/test/README.md new file mode 100644 index 0000000000..f00b526a98 --- /dev/null +++ b/examples/test/README.md @@ -0,0 +1 @@ +# Testing diff --git a/examples/test/client.js b/examples/test/client.js new file mode 100644 index 0000000000..3aa487a1ad --- /dev/null +++ b/examples/test/client.js @@ -0,0 +1,49 @@ +'use strict'; + +const url = require('url'); +const http = require('http'); +// Construct a schema, using GraphQL schema language + +const source = ` +query { + books { + name + authors { + name + address { + country + } + } + } +} +`; + +makeRequest(source).then(console.log); + +function makeRequest(query) { + return new Promise((resolve, reject) => { + const parsedUrl = new url.URL('http://localhost:4000/graphql'); + const options = { + hostname: parsedUrl.hostname, + port: parsedUrl.port, + path: parsedUrl.pathname, + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + }; + const req = http.request(options, (res) => { + const data = []; + res.on('data', (chunk) => data.push(chunk)); + res.on('end', () => { + resolve(data.toString()); + }); + res.on('error', (err) => { + reject(err); + }); + }); + + req.write(JSON.stringify({ query })); + req.end(); + }); +} diff --git a/examples/test/docker/collector-config.yaml b/examples/test/docker/collector-config.yaml new file mode 100644 index 0000000000..e9a909d78f --- /dev/null +++ b/examples/test/docker/collector-config.yaml @@ -0,0 +1,29 @@ +receivers: + otlp: + protocols: + grpc: + http: + cors_allowed_origins: + - http://* + - https://* + +exporters: + zipkin: + endpoint: "http://zipkin-all-in-one:9411/api/v2/spans" + prometheus: + endpoint: "0.0.0.0:9464" + +processors: + batch: + queued_retry: + +service: + pipelines: + traces: + receivers: [otlp] + exporters: [zipkin] + processors: [batch, queued_retry] + metrics: + receivers: [otlp] + exporters: [prometheus] + processors: [batch, queued_retry] diff --git a/examples/test/docker/docker-compose.yaml b/examples/test/docker/docker-compose.yaml new file mode 100644 index 0000000000..d4e0a42a19 --- /dev/null +++ b/examples/test/docker/docker-compose.yaml @@ -0,0 +1,30 @@ +version: "3" +services: + # Collector + collector: +# image: otel/opentelemetry-collector:latest + image: otel/opentelemetry-collector:0.13.0 + command: ["--config=/conf/collector-config.yaml", "--log-level=DEBUG"] + volumes: + - ./collector-config.yaml:/conf/collector-config.yaml + ports: + - "9464:9464" + - "55680:55680" + - "55681:55681" + depends_on: + - zipkin-all-in-one + + # Zipkin + zipkin-all-in-one: + image: openzipkin/zipkin:latest + ports: + - "9411:9411" + + # Prometheus + prometheus: + container_name: prometheus + image: prom/prometheus:latest + volumes: + - ./prometheus.yaml:/etc/prometheus/prometheus.yml + ports: + - "9090:9090" diff --git a/examples/test/docker/prometheus.yaml b/examples/test/docker/prometheus.yaml new file mode 100644 index 0000000000..b027daf9a0 --- /dev/null +++ b/examples/test/docker/prometheus.yaml @@ -0,0 +1,9 @@ +global: + scrape_interval: 15s # Default is every 1 minute. + +scrape_configs: + - job_name: 'collector' + # metrics_path defaults to '/metrics' + # scheme defaults to 'http'. + static_configs: + - targets: ['collector:9464'] diff --git a/examples/test/package.json b/examples/test/package.json new file mode 100644 index 0000000000..686087905a --- /dev/null +++ b/examples/test/package.json @@ -0,0 +1,52 @@ +{ + "name": "opentelemetry-testing", + "private": true, + "version": "0.11.0", + "description": "Testing", + "main": "index.js", + "scripts": { + "docker:start": "cd ./docker && docker-compose down && docker-compose up", + "docker:stop": "cd ./docker && docker-compose down", + "start:test": "node ./client.js", + "server:express": "node ./server-express.js" + }, + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/open-telemetry/opentelemetry-js.git" + }, + "keywords": [ + "opentelemetry", + "http", + "tracing", + "graphql" + ], + "engines": { + "node": ">=8" + }, + "author": "OpenTelemetry Authors", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/open-telemetry/opentelemetry-js/issues" + }, + "dependencies": { + "@opentelemetry/api": "^0.12.0", + "@opentelemetry/context-async-hooks": "^0.12.0", + "@opentelemetry/exporter-collector": "^0.12.0", + "@opentelemetry/node": "^0.12.0", + "@opentelemetry/plugin-express": "^0.11.0", + "@opentelemetry/instrumentation": "^0.12.0", + "@opentelemetry/instrumentation-graphql": "^0.11.0", + "@opentelemetry/plugin-http": "^0.12.0", + "@opentelemetry/plugin-https": "^0.12.0", + "@opentelemetry/tracing": "^0.12.0", + "apollo-server": "^2.18.1", + "express": "^4.17.1", + "express-graphql": "^0.11.0", + "graphql": "^15.3.0", + "qs-middleware": "^1.0.3" + }, + "homepage": "https://github.com/open-telemetry/opentelemetry-js#readme", + "devDependencies": { + "cross-env": "^6.0.0" + } +} diff --git a/examples/test/schema.js b/examples/test/schema.js new file mode 100644 index 0000000000..be98379c40 --- /dev/null +++ b/examples/test/schema.js @@ -0,0 +1,202 @@ +'use strict'; + +const https = require('https'); +const graphql = require('graphql'); + +const url1 = 'https://raw.githubusercontent.com/open-telemetry/opentelemetry-js/master/package.json'; + +function getData(url) { + return new Promise((resolve, reject) => { + https.get(url, (response) => { + let data = ''; + response.on('data', (chunk) => { + data += chunk; + }); + response.on('end', () => { + resolve(JSON.parse(data)); + }); + }).on('error', (err) => { + reject(err); + }); + }); +} + +const authors = []; +const books = []; + +function addBook(name, authorIds) { + let authorIdsLocal = authorIds; + if (typeof authorIdsLocal === 'string') { + authorIdsLocal = authorIdsLocal.split(',').map((id) => parseInt(id, 10)); + } + const id = books.length; + books.push({ + id, + name, + authorIds: authorIdsLocal, + }); + return books[books.length - 1]; +} + +function addAuthor(name, country, city) { + const id = authors.length; + authors.push({ + id, + name, + address: { + country, + city, + }, + }); + return authors[authors.length - 1]; +} + +function getBook(id) { + return books[id]; +} + +function getAuthor(id) { + return authors[id]; +} + +function prepareData() { + addAuthor('John', 'Poland', 'Szczecin'); + addAuthor('Alice', 'Poland', 'Warsaw'); + addAuthor('Bob', 'England', 'London'); + addAuthor('Christine', 'France', 'Paris'); + addBook('First Book', [0, 1]); + addBook('Second Book', [2]); + addBook('Third Book', [3]); +} + +prepareData(); +module.exports = function buildSchema() { + const Author = new graphql.GraphQLObjectType({ + name: 'Author', + fields: { + id: { + type: graphql.GraphQLString, + resolve(obj, _args) { + return obj.id; + }, + }, + name: { + type: graphql.GraphQLString, + resolve(obj, _args) { + return obj.name; + }, + }, + description: { + type: graphql.GraphQLString, + resolve(_obj, _args) { + return new Promise((resolve, reject) => { + getData(url1).then((response) => { + resolve(response.description); + }, reject); + }); + }, + }, + address: { + type: new graphql.GraphQLObjectType({ + name: 'Address', + fields: { + country: { + type: graphql.GraphQLString, + resolve(obj, _args) { + return obj.country; + }, + }, + city: { + type: graphql.GraphQLString, + resolve(obj, _args) { + return obj.city; + }, + }, + }, + }), + resolve(obj, _args) { + return obj.address; + }, + }, + }, + }); + + const Book = new graphql.GraphQLObjectType({ + name: 'Book', + fields: { + id: { + type: graphql.GraphQLInt, + resolve(obj, _args) { + return obj.id; + }, + }, + name: { + type: graphql.GraphQLString, + resolve(obj, _args) { + return obj.name; + }, + }, + authors: { + type: new graphql.GraphQLList(Author), + resolve(obj, _args) { + return obj.authorIds.map((id) => authors[id]); + }, + }, + }, + }); + + const query = new graphql.GraphQLObjectType({ + name: 'Query', + fields: { + author: { + type: Author, + args: { + id: { type: graphql.GraphQLInt }, + }, + resolve(obj, args, _context) { + return Promise.resolve(getAuthor(args.id)); + }, + }, + authors: { + type: new graphql.GraphQLList(Author), + resolve(_obj, _args, _context) { + return Promise.resolve(authors); + }, + }, + book: { + type: Book, + args: { + id: { type: graphql.GraphQLInt }, + }, + resolve(obj, args, _context) { + return Promise.resolve(getBook(args.id)); + }, + }, + books: { + type: new graphql.GraphQLList(Book), + resolve(_obj, _args, _context) { + return Promise.resolve(books); + }, + }, + }, + }); + + const mutation = new graphql.GraphQLObjectType({ + name: 'Mutation', + fields: { + addBook: { + type: Book, + args: { + name: { type: new graphql.GraphQLNonNull(graphql.GraphQLString) }, + authorIds: { type: new graphql.GraphQLNonNull(graphql.GraphQLString) }, + }, + resolve(obj, args, _context) { + return Promise.resolve(addBook(args.name, args.authorIds)); + }, + }, + }, + }); + + const schema = new graphql.GraphQLSchema({ query, mutation }); + return schema; +}; diff --git a/examples/test/server-express.js b/examples/test/server-express.js new file mode 100644 index 0000000000..6bba89f817 --- /dev/null +++ b/examples/test/server-express.js @@ -0,0 +1,19 @@ +'use strict'; + +require('./tracer'); + +const express = require('express'); +const { graphqlHTTP } = require('express-graphql'); +const buildSchema = require('./schema'); + +const schema = buildSchema(); + +const app = express(); +app.use('/graphql', graphqlHTTP({ + schema, + graphiql: true, +})); + +app.listen(4000); + +console.log('Running a GraphQL API server at http://localhost:4000/graphql'); diff --git a/examples/test/tracer.js b/examples/test/tracer.js new file mode 100644 index 0000000000..1c5ea694f5 --- /dev/null +++ b/examples/test/tracer.js @@ -0,0 +1,88 @@ +'use strict'; + +const { GraphQLInstrumentation } = require('@opentelemetry/instrumentation-graphql'); + +const { ConsoleSpanExporter, SimpleSpanProcessor } = require('@opentelemetry/tracing'); +const { NodeTracerProvider } = require('@opentelemetry/node'); +const { BasicTracerProvider } = require('@opentelemetry/tracing'); +const { CollectorTraceExporter } = require('@opentelemetry/exporter-collector'); +const { registerInstrumentations } = require('@opentelemetry/instrumentation'); +const httpPlugin = require('@opentelemetry/plugin-http').plugin; +const httpsPlugin = require('@opentelemetry/plugin-https').plugin; +const expressPlugin = require('@opentelemetry/plugin-express').plugin; +const { ConsoleLogger } = require('@opentelemetry/core'); +const { AsyncHooksContextManager, AsyncLocalStorageContextManager } = require('@opentelemetry/context-async-hooks'); + +const provider = new BasicTracerProvider(); + +// const provider = new NodeTracerProvider({ +// plugins: { +// http: { enabled: false }, +// https: { enabled: false }, +// express: { enabled: false }, +// }, +// }); + +const currentDir = __dirname; +registerInstrumentations({ + instrumentations: [ + GraphQLInstrumentation, + { + plugins: { + // http: { enabled: true, path: `${currentDir}/node_modules/@opentelemetry/plugin-http/build/src/` }, + https: { enabled: false, path: `${currentDir}/node_modules/@opentelemetry/plugin-https/build/src/` }, + express: { enabled: true, path: `${currentDir}/node_modules/@opentelemetry/plugin-express/build/src/` }, + + http: { enabled: true, plugin: httpPlugin }, + // https: { enabled: true, plugin: httpsPlugin }, + // express: { enabled: true, path: '@opentelemetry/plugin-express' }, + + // http: { enabled: false, path: '@opentelemetry/plugin-http' }, + // https: { enabled: false, path: '@opentelemetry/plugin-https' }, + // express: { enabled: true, plugin: expressPlugin }, + }, + }, + ], + tracerProvider: provider, + logger: new ConsoleLogger() +}); + +const exporter = new CollectorTraceExporter({ + serviceName: 'basic-service', +}); + +// const provider = new NodeTracerProvider({ +// plugins: { +// http: { enabled: false }, +// https: { enabled: false }, +// express: { enabled: false }, +// http: { enabled: true, path: `${currentDir}/node_modules/@opentelemetry/plugin-http/build/src/` }, +// https: { enabled: false, path: `${currentDir}/node_modules/@opentelemetry/plugin-https/build/src/` }, +// express: { enabled: true, path: `${currentDir}/node_modules/@opentelemetry/plugin-express/build/src/` }, +// }, +// }); + +// const graphQLInstrumentation = new GraphQLInstrumentation({ +// // allowAttributes: true, +// // depth: 2, +// // mergeItems: true, +// }); +// +// graphQLInstrumentation.setTracerProvider(provider); +// +// graphQLInstrumentation.enable(); +// +provider.addSpanProcessor(new SimpleSpanProcessor(exporter)); +provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); + // if (config.contextManager === undefined) { + // const ContextManager = semver.gte(process.version, '14.8.0') + // ? AsyncLocalStorageContextManager + // : AsyncHooksContextManager; + // config.contextManager = new ContextManager(); + // config.contextManager.enable(); + // } + +provider.register({ + contextManager: new AsyncLocalStorageContextManager(), + // contextManager: new AsyncHooksContextManager(), +}); diff --git a/examples/tracer-web/examples/xml-http-request/index.js b/examples/tracer-web/examples/xml-http-request/index.js index 2532037bc9..c4e27fd0dd 100644 --- a/examples/tracer-web/examples/xml-http-request/index.js +++ b/examples/tracer-web/examples/xml-http-request/index.js @@ -4,16 +4,35 @@ import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xm import { ZoneContextManager } from '@opentelemetry/context-zone'; import { CollectorTraceExporter } from '@opentelemetry/exporter-collector'; import { B3Propagator } from '@opentelemetry/propagator-b3'; +import { registerInstrumentations } from '@opentelemetry/instrumentation'; +import { UserInteractionPlugin } from '@opentelemetry/plugin-user-interaction'; +import { DocumentLoad } from '@opentelemetry/plugin-document-load'; + const providerWithZone = new WebTracerProvider({ - plugins: [ + // plugins: [ + // new XMLHttpRequestInstrumentation({ + // ignoreUrls: [/localhost:8090\/sockjs-node/], + // propagateTraceHeaderCorsUrls: [ + // 'https://httpbin.org/get', + // ], + // }), + // ], +}); + +registerInstrumentations({ + // instrumentations: [XMLHttpRequestInstrumentation], + instrumentations: [ new XMLHttpRequestInstrumentation({ ignoreUrls: [/localhost:8090\/sockjs-node/], propagateTraceHeaderCorsUrls: [ 'https://httpbin.org/get', ], }), + new UserInteractionPlugin(), + new DocumentLoad(), ], + tracerProvider: providerWithZone, }); providerWithZone.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); @@ -56,7 +75,7 @@ const prepareClickEvent = () => { getData(url1).then((_data) => { webTracerWithZone.getCurrentSpan().addEvent('fetching-span1-completed'); span1.end(); - }, ()=> { + }, () => { webTracerWithZone.getCurrentSpan().addEvent('fetching-error'); span1.end(); }); diff --git a/examples/tracer-web/package.json b/examples/tracer-web/package.json index d802ca286e..a6d816df8d 100644 --- a/examples/tracer-web/package.json +++ b/examples/tracer-web/package.json @@ -40,9 +40,10 @@ "@opentelemetry/exporter-zipkin": "^0.13.0", "@opentelemetry/metrics": "^0.13.0", "@opentelemetry/propagator-b3": "^0.13.0", - "@opentelemetry/plugin-document-load": "^0.9.0", + "@opentelemetry/plugin-document-load": "^0.11.0", "@opentelemetry/plugin-fetch": "^0.13.0", - "@opentelemetry/plugin-user-interaction": "^0.9.0", + "@opentelemetry/plugin-user-interaction": "^0.11.0", + "@opentelemetry/instrumentation": "^0.13.0", "@opentelemetry/instrumentation-xml-http-request": "^0.13.0", "@opentelemetry/tracing": "^0.13.0", "@opentelemetry/web": "^0.13.0" diff --git a/examples/tracer-web/webpack.config.js b/examples/tracer-web/webpack.config.js index 00773db28b..20db2ce403 100644 --- a/examples/tracer-web/webpack.config.js +++ b/examples/tracer-web/webpack.config.js @@ -17,7 +17,7 @@ const common = { output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js', - sourceMapFilename: '[file].map', + // sourceMapFilename: '[file].map', }, target: 'web', module: { diff --git a/lerna.json b/lerna.json index 55f8b325c5..870ba4258f 100644 --- a/lerna.json +++ b/lerna.json @@ -2,6 +2,8 @@ "lerna": "3.13.4", "npmClient": "npm", "packages": [ + "examples/test", + "examples/tracer-web", "benchmark/*", "backwards-compatability/*", "metapackages/*", diff --git a/packages/opentelemetry-instrumentation/tsconfig.json b/packages/opentelemetry-instrumentation/tsconfig.json index a2042cd68b..0c36dd20f4 100644 --- a/packages/opentelemetry-instrumentation/tsconfig.json +++ b/packages/opentelemetry-instrumentation/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../tsconfig.base", "compilerOptions": { +// "sourceMap": false, "rootDir": ".", "outDir": "build" }, From ce5104163796ce3d41adfbd4d43e74bd070e1bd8 Mon Sep 17 00:00:00 2001 From: Bartlomiej Obecny Date: Fri, 18 Dec 2020 16:58:47 +0100 Subject: [PATCH 3/8] chore: adding auto loader for instrumentation and plugins --- examples/test/tracer.js | 1 + .../examples/xml-http-request/index.js | 14 +- .../opentelemetry-instrumentation/.gitignore | 2 + .../opentelemetry-instrumentation/README.md | 103 +++++ .../src/autoLoader.ts | 25 +- .../src/autoLoaderUtils.ts | 55 ++- .../src/platform/browser/old/autoLoader.ts | 15 +- .../src/platform/node/old/PluginLoader.ts | 8 +- .../src/platform/node/old/autoLoader.ts | 21 +- .../src/types.ts | 27 -- .../src/types_internal.ts | 46 +++ .../test/browser/autoLoader.test.ts | 76 ++++ .../test/common/autoLoader.test.ts | 93 +++++ .../test/common/autoLoaderUtils.test.ts | 117 ++++++ .../test/node/BasePlugin.ts | 45 +++ .../test/node/PluginLoader.test.ts | 364 ++++++++++++++++++ .../test/node/autoLoader.test.ts | 105 +++++ .../plugin-http-module/http-module.js | 22 ++ .../plugin-http-module/index.js | 5 + .../plugin-http-module/package.json | 4 + .../plugin-notsupported-module/index.js | 5 + .../plugin-notsupported-module/package.json | 4 + .../simple-module.js | 25 ++ .../plugin-simple-module/index.js | 5 + .../plugin-simple-module/package.json | 4 + .../plugin-simple-module/simple-module.js | 24 ++ .../plugin-supported-module/index.js | 5 + .../plugin-supported-module/package.json | 4 + .../plugin-supported-module/simple-module.js | 25 ++ .../already-require-module/index.js | 4 + .../already-require-module/package.json | 4 + .../node_modules/notsupported-module/index.js | 4 + .../notsupported-module/package.json | 4 + .../node/node_modules/random-module/index.js | 4 + .../node_modules/random-module/package.json | 4 + .../node/node_modules/simple-module/index.js | 4 + .../node_modules/simple-module/package.json | 4 + .../node_modules/supported-module/index.js | 4 + .../supported-module/package.json | 4 + .../test/node/utils.test.ts | 95 +++++ .../tsconfig.json | 1 - 41 files changed, 1315 insertions(+), 70 deletions(-) create mode 100644 packages/opentelemetry-instrumentation/.gitignore create mode 100644 packages/opentelemetry-instrumentation/src/types_internal.ts create mode 100644 packages/opentelemetry-instrumentation/test/browser/autoLoader.test.ts create mode 100644 packages/opentelemetry-instrumentation/test/common/autoLoader.test.ts create mode 100644 packages/opentelemetry-instrumentation/test/common/autoLoaderUtils.test.ts create mode 100644 packages/opentelemetry-instrumentation/test/node/BasePlugin.ts create mode 100644 packages/opentelemetry-instrumentation/test/node/PluginLoader.test.ts create mode 100644 packages/opentelemetry-instrumentation/test/node/autoLoader.test.ts create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/http-module.js create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/index.js create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/package.json create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/index.js create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/package.json create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/simple-module.js create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/index.js create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/package.json create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/simple-module.js create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/index.js create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/package.json create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/simple-module.js create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/already-require-module/index.js create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/already-require-module/package.json create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/notsupported-module/index.js create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/notsupported-module/package.json create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/random-module/index.js create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/random-module/package.json create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/simple-module/index.js create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/simple-module/package.json create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/supported-module/index.js create mode 100644 packages/opentelemetry-instrumentation/test/node/node_modules/supported-module/package.json create mode 100644 packages/opentelemetry-instrumentation/test/node/utils.test.ts diff --git a/examples/test/tracer.js b/examples/test/tracer.js index 1c5ea694f5..790ddb79f4 100644 --- a/examples/test/tracer.js +++ b/examples/test/tracer.js @@ -26,6 +26,7 @@ const provider = new BasicTracerProvider(); const currentDir = __dirname; registerInstrumentations({ instrumentations: [ + // new GraphQLInstrumentation(), GraphQLInstrumentation, { plugins: { diff --git a/examples/tracer-web/examples/xml-http-request/index.js b/examples/tracer-web/examples/xml-http-request/index.js index c4e27fd0dd..d3e08c6818 100644 --- a/examples/tracer-web/examples/xml-http-request/index.js +++ b/examples/tracer-web/examples/xml-http-request/index.js @@ -23,12 +23,14 @@ const providerWithZone = new WebTracerProvider({ registerInstrumentations({ // instrumentations: [XMLHttpRequestInstrumentation], instrumentations: [ - new XMLHttpRequestInstrumentation({ - ignoreUrls: [/localhost:8090\/sockjs-node/], - propagateTraceHeaderCorsUrls: [ - 'https://httpbin.org/get', - ], - }), + XMLHttpRequestInstrumentation, + // new XMLHttpRequestInstrumentation({ + // ignoreUrls: [/localhost:8090\/sockjs-node/], + // propagateTraceHeaderCorsUrls: [ + // 'https://httpbin.org/get', + // ], + // }), + new UserInteractionPlugin(), new UserInteractionPlugin(), new DocumentLoad(), ], diff --git a/packages/opentelemetry-instrumentation/.gitignore b/packages/opentelemetry-instrumentation/.gitignore new file mode 100644 index 0000000000..473e814b60 --- /dev/null +++ b/packages/opentelemetry-instrumentation/.gitignore @@ -0,0 +1,2 @@ +# Dependency directories +!test/node/node_modules diff --git a/packages/opentelemetry-instrumentation/README.md b/packages/opentelemetry-instrumentation/README.md index 9988303d16..2ca9a1c6cb 100644 --- a/packages/opentelemetry-instrumentation/README.md +++ b/packages/opentelemetry-instrumentation/README.md @@ -113,6 +113,7 @@ const myPLugin = new MyPlugin(); myPLugin.setTracerProvider(provider); // this is optional myPLugin.setMeterProvider(meterProvider); // this is optional myPLugin.enable(); +// or use Auto Loader ``` ## Usage in Web @@ -154,6 +155,108 @@ const myPLugin = new MyPlugin(); myPLugin.setTracerProvider(provider); myPLugin.setMeterProvider(meterProvider); myPLugin.enable(); +// or use Auto Loader +``` + +## AutoLoader +Successor of loading plugins through TracerProvider "plugins" option. +It also supersedes PluginLoader for node. The old configurations usually looks like + +### NODE - old way using TracerProvider +```javascript +const { NodeTracerProvider } = require('@opentelemetry/node'); +const { B3Propagator } = require('@opentelemetry/propagator-b3'); +const provider = new NodeTracerProvider({ + plugins: { + http: { enabled: false }, + }, +}); +provider.register({ + propagator: new B3Propagator(), +}); +``` + +### WEB - old way using TracerProvider +```javascript +const { WebTracerProvider } = require('@opentelemetry/web'); +const { UserInteractionPlugin } = require('@opentelemetry/plugin-user-interaction'); +const { XMLHttpRequestInstrumentation } = require('@opentelemetry/instrumentation-xml-http-request'); +const { B3Propagator } = require('@opentelemetry/propagator-b3'); +const provider = new WebTracerProvider({ + plugins: [ + new UserInteractionPlugin(), + new XMLHttpRequestInstrumentation({ + ignoreUrls: [/localhost/], + propagateTraceHeaderCorsUrls: [ + 'http://localhost:8090', + ], + }), + ], +}); +provider.register({ + propagator: new B3Propagator(), +}); +``` + +After change it will look like this - mixing plugins and instrumentations together +All plugins will be bound to TracerProvider as well as instrumentations + +### NODE - Auto Loader +```javascript +const { B3Propagator } = require('@opentelemetry/propagator-b3'); +const { registerInstrumentations } = require('@opentelemetry/instrumentation'); +const { GraphQLInstrumentation } = require('@opentelemetry/instrumentation-graphql'); +const { NodeTracerProvider } = require('@opentelemetry/node'); +const tracerProvider = new NodeTracerProvider(); + +registerInstrumentations({ + instrumentations: [ + new UserInteractionPlugin(), + new XMLHttpRequestInstrumentation({ + ignoreUrls: [/localhost/], + propagateTraceHeaderCorsUrls: [ + 'http://localhost:8090', + ], + }), + ], + meterProvider: meterProvider, + tracerProvider: tracerProvider, + logger: new ConsoleLogger(), // optional +}); + +tracerProvider.register({ + propagator: new B3Propagator(), +}); + +``` + +### WEB - Auto Loader +```javascript +const { B3Propagator } = require('@opentelemetry/propagator-b3'); +const { registerInstrumentations } = require('@opentelemetry/instrumentation'); +import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xml-http-request'; +const { UserInteractionPlugin } = require('@opentelemetry/plugin-user-interaction'); +const { WebTracerProvider } = require('@opentelemetry/web'); +const tracerProvider = new WebTracerProvider(); + +registerInstrumentations({ + instrumentations: [ + new GraphQLInstrumentation(), + { + plugins: { + http: { enabled: false }, + }, + } + ], + meterProvider: meterProvider, + tracerProvider: tracerProvider, + logger: new ConsoleLogger(), // optional +}); + +tracerProvider.register({ + propagator: new B3Propagator(), +}); + ``` ## License diff --git a/packages/opentelemetry-instrumentation/src/autoLoader.ts b/packages/opentelemetry-instrumentation/src/autoLoader.ts index 13e333c128..24dc70e99c 100644 --- a/packages/opentelemetry-instrumentation/src/autoLoader.ts +++ b/packages/opentelemetry-instrumentation/src/autoLoader.ts @@ -16,13 +16,22 @@ import * as api from '@opentelemetry/api'; import { + disableInstrumentations, enableInstrumentations, parseInstrumentationOptions, } from './autoLoaderUtils'; import { loadOldPlugins } from './platform'; -import { AutoLoaderOptions } from './types'; +import { AutoLoaderOptions } from './types_internal'; -export function registerInstrumentations(options: AutoLoaderOptions) { +/** + * It will register instrumentations and plugins + * @param options + * @return returns function to unload instrumentation and plugins that were + * registered + */ +export function registerInstrumentations( + options: AutoLoaderOptions +): () => void { const { instrumentations, pluginsNode, @@ -44,5 +53,15 @@ export function registerInstrumentations(options: AutoLoaderOptions) { meterProvider ); - loadOldPlugins(pluginsNode, pluginsWeb, logger, tracerProvider); + const unload = loadOldPlugins( + pluginsNode, + pluginsWeb, + logger, + tracerProvider + ); + + return () => { + unload(); + disableInstrumentations(instrumentations); + }; } diff --git a/packages/opentelemetry-instrumentation/src/autoLoaderUtils.ts b/packages/opentelemetry-instrumentation/src/autoLoaderUtils.ts index 5e6a92cb11..4d0660478a 100644 --- a/packages/opentelemetry-instrumentation/src/autoLoaderUtils.ts +++ b/packages/opentelemetry-instrumentation/src/autoLoaderUtils.ts @@ -15,12 +15,8 @@ */ import { Logger, MeterProvider, TracerProvider } from '@opentelemetry/api'; -import { InstrumentationBase } from './platform'; -import { - AutoLoaderResult, - Instrumentation, - InstrumentationOption, -} from './types'; +import { Instrumentation } from './types'; +import { AutoLoaderResult, InstrumentationOption } from './types_internal'; import { NodePlugins, @@ -28,31 +24,35 @@ import { OldClassPlugin, } from './types_plugin_only'; +/** + * Parses the options and returns instrumentations, node plugins and + * web plugins + * @param options + */ export function parseInstrumentationOptions( - options: InstrumentationOption[] + options: InstrumentationOption[] = [] ): AutoLoaderResult { let instrumentations: Instrumentation[] = []; let pluginsNode: NodePlugins = {}; - const pluginsWeb: OldClassPlugin[] = []; + let pluginsWeb: OldClassPlugin[] = []; for (let i = 0, j = options.length; i < j; i++) { - const option = options[i]; - const OptionClass = option as any; - - if (OptionClass.prototype instanceof InstrumentationBase) { - instrumentations.push(new OptionClass()); - } else if (option instanceof InstrumentationBase) { - instrumentations.push(option); - } else if (Array.isArray(option)) { - instrumentations = instrumentations.concat( - parseInstrumentationOptions(option).instrumentations - ); + const option = options[i] as any; + if (Array.isArray(option)) { + const results = parseInstrumentationOptions(option); + instrumentations = instrumentations.concat(results.instrumentations); + pluginsWeb = pluginsWeb.concat(results.pluginsWeb); + pluginsNode = Object.assign({}, pluginsNode, results.pluginsNode); } else if ((option as NodePluginsTracerConfiguration).plugins) { pluginsNode = Object.assign( {}, pluginsNode, (option as NodePluginsTracerConfiguration).plugins ); - } else { + } else if (typeof option === 'function') { + instrumentations.push(new option()); + } else if ((option as Instrumentation).instrumentationName) { + instrumentations.push(option); + } else if ((option as OldClassPlugin).moduleName) { pluginsWeb.push(option as OldClassPlugin); } } @@ -60,6 +60,13 @@ export function parseInstrumentationOptions( return { instrumentations, pluginsNode, pluginsWeb }; } +/** + * Enable instrumentations + * @param instrumentations + * @param logger + * @param tracerProvider + * @param meterProvider + */ export function enableInstrumentations( instrumentations: Instrumentation[], logger: Logger, @@ -77,3 +84,11 @@ export function enableInstrumentations( instrumentation.enable(); } } + +/** + * Disable instrumentations + * @param instrumentations + */ +export function disableInstrumentations(instrumentations: Instrumentation[]) { + instrumentations.forEach(instrumentation => instrumentation.disable()); +} diff --git a/packages/opentelemetry-instrumentation/src/platform/browser/old/autoLoader.ts b/packages/opentelemetry-instrumentation/src/platform/browser/old/autoLoader.ts index 7782b7b49d..1b95510efe 100644 --- a/packages/opentelemetry-instrumentation/src/platform/browser/old/autoLoader.ts +++ b/packages/opentelemetry-instrumentation/src/platform/browser/old/autoLoader.ts @@ -19,13 +19,26 @@ import * as api from '@opentelemetry/api'; import { NodePlugins, OldClassPlugin } from '../../../types_plugin_only'; +/** + * Loads provided web plugins + * @param pluginsNode + * @param pluginsWeb + * @param logger + * @param tracerProvider + * @return returns function to disable all plugins + */ export function loadOldPlugins( pluginsNode: NodePlugins, pluginsWeb: OldClassPlugin[], logger: api.Logger, tracerProvider: api.TracerProvider -): void { +): () => void { pluginsWeb.forEach(plugin => { plugin.enable([], tracerProvider, logger); }); + return () => { + pluginsWeb.forEach(plugin => { + plugin.disable(); + }); + }; } diff --git a/packages/opentelemetry-instrumentation/src/platform/node/old/PluginLoader.ts b/packages/opentelemetry-instrumentation/src/platform/node/old/PluginLoader.ts index 9580b7e0b0..06b8068ac9 100644 --- a/packages/opentelemetry-instrumentation/src/platform/node/old/PluginLoader.ts +++ b/packages/opentelemetry-instrumentation/src/platform/node/old/PluginLoader.ts @@ -75,7 +75,7 @@ function getIgnoreList(): string[] | typeof DISABLE_ALL_PLUGINS { */ export class PluginLoader { /** A list of loaded plugins. */ - private _plugins: OldClassPlugin[] = []; + plugins: OldClassPlugin[] = []; /** * A field that tracks whether the require-in-the-middle hook has been loaded * for the first time, as well as whether the hook body is activated or not. @@ -186,7 +186,7 @@ export class PluginLoader { return exports; } - this._plugins.push(plugin); + this.plugins.push(plugin); // Enable each supported plugin. return plugin.enable(exports, this.provider, this.logger, config); } catch (e) { @@ -210,10 +210,10 @@ export class PluginLoader { /** Unloads plugins. */ unload(): PluginLoader { if (this._hookState === HookState.ENABLED) { - for (const plugin of this._plugins) { + for (const plugin of this.plugins) { plugin.disable(); } - this._plugins = []; + this.plugins = []; this._hookState = HookState.DISABLED; } return this; diff --git a/packages/opentelemetry-instrumentation/src/platform/node/old/autoLoader.ts b/packages/opentelemetry-instrumentation/src/platform/node/old/autoLoader.ts index 1e20234bf9..c3880b96ec 100644 --- a/packages/opentelemetry-instrumentation/src/platform/node/old/autoLoader.ts +++ b/packages/opentelemetry-instrumentation/src/platform/node/old/autoLoader.ts @@ -38,21 +38,26 @@ export const DEFAULT_INSTRUMENTATION_PLUGINS: NodePlugins = { dns: { enabled: true, path: '@opentelemetry/plugin-dns' }, }; +/** + * Loads provided node plugins + * @param pluginsNode + * @param pluginsWeb + * @param logger + * @param tracerProvider + * @return returns function to disable all plugins + */ export function loadOldPlugins( pluginsNode: NodePlugins, pluginsWeb: OldClassPlugin[], logger: api.Logger, tracerProvider: api.TracerProvider -): void { - let allPlugins; - if (Object.keys(pluginsNode).length > 0) { - // allPlugins = mergePlugins(DEFAULT_INSTRUMENTATION_PLUGINS, pluginsNode); - allPlugins = mergePlugins({}, pluginsNode); - } else { - allPlugins = pluginsNode; - } +): () => void { + const allPlugins = mergePlugins(DEFAULT_INSTRUMENTATION_PLUGINS, pluginsNode); const pluginLoader = new PluginLoader(tracerProvider, logger); pluginLoader.load(allPlugins); + return () => { + pluginLoader.unload(); + }; } function mergePlugins( diff --git a/packages/opentelemetry-instrumentation/src/types.ts b/packages/opentelemetry-instrumentation/src/types.ts index b8558ad1fc..a6501d3aad 100644 --- a/packages/opentelemetry-instrumentation/src/types.ts +++ b/packages/opentelemetry-instrumentation/src/types.ts @@ -15,12 +15,6 @@ */ import { Logger, MeterProvider, TracerProvider } from '@opentelemetry/api'; -import { InstrumentationBase } from './platform'; -import { - NodePlugins, - NodePluginsTracerConfiguration, - OldClassPlugin, -} from './types_plugin_only'; /** Interface Instrumentation to apply patch. */ export interface Instrumentation { @@ -83,24 +77,3 @@ export interface ShimWrapped { __unwrap: Function; __original: Function; } - -export type InstrumentationOption = - | InstrumentationBase - | InstrumentationBase[] - | Instrumentation - | Instrumentation[] - | NodePluginsTracerConfiguration - | OldClassPlugin; - -export interface AutoLoaderResult { - instrumentations: Instrumentation[]; - pluginsNode: NodePlugins; - pluginsWeb: OldClassPlugin[]; -} - -export interface AutoLoaderOptions { - instrumentations: InstrumentationOption[]; - tracerProvider?: TracerProvider; - meterProvider?: MeterProvider; - logger?: Logger; -} diff --git a/packages/opentelemetry-instrumentation/src/types_internal.ts b/packages/opentelemetry-instrumentation/src/types_internal.ts new file mode 100644 index 0000000000..49a328db81 --- /dev/null +++ b/packages/opentelemetry-instrumentation/src/types_internal.ts @@ -0,0 +1,46 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Logger, MeterProvider, TracerProvider } from '@opentelemetry/api'; +import { InstrumentationBase } from './platform'; +import { Instrumentation } from './types'; +import { + NodePlugins, + NodePluginsTracerConfiguration, + OldClassPlugin, +} from './types_plugin_only'; + +export type InstrumentationOption = + | typeof InstrumentationBase + | typeof InstrumentationBase[] + | Instrumentation + | Instrumentation[] + | NodePluginsTracerConfiguration + | OldClassPlugin + | OldClassPlugin[]; + +export interface AutoLoaderResult { + instrumentations: Instrumentation[]; + pluginsNode: NodePlugins; + pluginsWeb: OldClassPlugin[]; +} + +export interface AutoLoaderOptions { + instrumentations?: InstrumentationOption[]; + tracerProvider?: TracerProvider; + meterProvider?: MeterProvider; + logger?: Logger; +} diff --git a/packages/opentelemetry-instrumentation/test/browser/autoLoader.test.ts b/packages/opentelemetry-instrumentation/test/browser/autoLoader.test.ts new file mode 100644 index 0000000000..1f3e6d5cc6 --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/browser/autoLoader.test.ts @@ -0,0 +1,76 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { NOOP_METER_PROVIDER, NOOP_TRACER_PROVIDER } from '@opentelemetry/api'; +import * as assert from 'assert'; +import * as sinon from 'sinon'; + +import { registerInstrumentations } from '../../src'; + +import { OldClassPlugin } from '../../src/types_plugin_only'; + +class WebPlugin implements OldClassPlugin { + moduleName = 'WebPlugin'; + enable() {} + disable() {} +} + +describe('autoLoader', () => { + let sandbox: sinon.SinonSandbox; + let unload: Function | undefined; + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + afterEach(() => { + sandbox.restore(); + if (typeof unload === 'function') { + unload(); + unload = undefined; + } + }); + + describe('registerInstrumentations', () => { + describe('Old Plugins', () => { + let enableSpy: sinon.SinonSpy; + const tracerProvider = NOOP_TRACER_PROVIDER; + const meterProvider = NOOP_METER_PROVIDER; + let webPlugin: WebPlugin; + beforeEach(() => { + webPlugin = new WebPlugin(); + enableSpy = sandbox.spy(webPlugin, 'enable'); + unload = registerInstrumentations({ + instrumentations: [webPlugin], + tracerProvider, + meterProvider, + }); + }); + afterEach(() => { + if (typeof unload === 'function') { + unload(); + unload = undefined; + } + }); + + it('should enable a required plugin', () => { + assert.strictEqual(enableSpy.callCount, 1); + }); + + it('should set TracerProvider', () => { + assert.ok(enableSpy.lastCall.args[1] === tracerProvider); + }); + }); + }); +}); diff --git a/packages/opentelemetry-instrumentation/test/common/autoLoader.test.ts b/packages/opentelemetry-instrumentation/test/common/autoLoader.test.ts new file mode 100644 index 0000000000..6916c61157 --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/common/autoLoader.test.ts @@ -0,0 +1,93 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { NOOP_METER_PROVIDER, NOOP_TRACER_PROVIDER } from '@opentelemetry/api'; +import * as assert from 'assert'; +import * as sinon from 'sinon'; + +import { InstrumentationBase, registerInstrumentations } from '../../src'; + +class FooInstrumentation extends InstrumentationBase { + init() { + return []; + } + enable() {} + disable() {} +} + +describe('autoLoader', () => { + let sandbox: sinon.SinonSandbox; + let unload: Function | undefined; + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + afterEach(() => { + sandbox.restore(); + if (typeof unload === 'function') { + unload(); + unload = undefined; + } + }); + + describe('registerInstrumentations', () => { + describe('InstrumentationBase', () => { + let instrumentation: InstrumentationBase; + let enableSpy: sinon.SinonSpy; + let setTracerProviderSpy: sinon.SinonSpy; + let setsetMeterProvider: sinon.SinonSpy; + const tracerProvider = NOOP_TRACER_PROVIDER; + const meterProvider = NOOP_METER_PROVIDER; + beforeEach(() => { + instrumentation = new FooInstrumentation('foo', '1', {}); + enableSpy = sandbox.spy(instrumentation, 'enable'); + setTracerProviderSpy = sandbox.stub( + instrumentation, + 'setTracerProvider' + ); + setsetMeterProvider = sandbox.stub(instrumentation, 'setMeterProvider'); + unload = registerInstrumentations({ + instrumentations: [instrumentation], + tracerProvider, + meterProvider, + }); + }); + + afterEach(() => { + Object.keys(require.cache).forEach(key => delete require.cache[key]); + if (typeof unload === 'function') { + unload(); + unload = undefined; + } + }); + + it('should enable instrumentation', () => { + assert.strictEqual(enableSpy.callCount, 1); + }); + + it('should set TracerProvider', () => { + assert.strictEqual(setTracerProviderSpy.callCount, 1); + assert.ok(setTracerProviderSpy.lastCall.args[0] === tracerProvider); + assert.strictEqual(setTracerProviderSpy.lastCall.args.length, 1); + }); + + it('should set MeterProvider', () => { + assert.strictEqual(setsetMeterProvider.callCount, 1); + assert.ok(setsetMeterProvider.lastCall.args[0] === meterProvider); + assert.strictEqual(setsetMeterProvider.lastCall.args.length, 1); + }); + }); + }); +}); diff --git a/packages/opentelemetry-instrumentation/test/common/autoLoaderUtils.test.ts b/packages/opentelemetry-instrumentation/test/common/autoLoaderUtils.test.ts new file mode 100644 index 0000000000..f234ad9c16 --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/common/autoLoaderUtils.test.ts @@ -0,0 +1,117 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as assert from 'assert'; +import * as sinon from 'sinon'; +import { InstrumentationBase } from '../../src'; +import { parseInstrumentationOptions } from '../../src/autoLoaderUtils'; +import { InstrumentationOption } from '../../src/types_internal'; +import { OldClassPlugin } from '../../src/types_plugin_only'; + +class FooInstrumentation extends InstrumentationBase { + constructor() { + super('foo', '1', {}); + } + + init() { + return []; + } + + enable() {} + + disable() {} +} + +class FooWebPlugin implements OldClassPlugin { + moduleName = 'foo'; + + enable() {} + + disable() {} +} + +// const fooInstrumentation = new FooInstrumentation(); + +describe('autoLoaderUtils', () => { + let sandbox: sinon.SinonSandbox; + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + afterEach(() => { + sandbox.restore(); + }); + + describe('parseInstrumentationOptions', () => { + it('should create a new instrumentation from class', () => { + const { instrumentations } = parseInstrumentationOptions([ + FooInstrumentation, + ]); + assert.strictEqual(instrumentations.length, 1); + const instrumentation = instrumentations[0]; + assert.ok(instrumentation instanceof InstrumentationBase); + }); + + it('should return an instrumentation from Instrumentation', () => { + const { instrumentations } = parseInstrumentationOptions([ + new FooInstrumentation(), + ]); + assert.strictEqual(instrumentations.length, 1); + const instrumentation = instrumentations[0]; + assert.ok(instrumentation instanceof InstrumentationBase); + }); + + it('should return node old plugin', () => { + const { pluginsNode } = parseInstrumentationOptions([ + { + plugins: { + http: { enabled: false }, + }, + }, + ]); + assert.strictEqual(Object.keys(pluginsNode).length, 1); + }); + + it('should return web old plugin', () => { + const { pluginsWeb } = parseInstrumentationOptions([new FooWebPlugin()]); + assert.strictEqual(pluginsWeb.length, 1); + }); + + it('should handle mix of plugins and instrumentations', () => { + const nodePlugins = { + plugins: { + http: { enabled: false }, + https: { enabled: false }, + }, + }; + const options: InstrumentationOption[] = []; + + options.push(new FooWebPlugin()); + options.push(nodePlugins); + options.push([new FooInstrumentation(), new FooInstrumentation()]); + options.push([new FooWebPlugin(), new FooWebPlugin()]); + + const { + pluginsWeb, + pluginsNode, + instrumentations, + } = parseInstrumentationOptions(options); + + assert.strictEqual(pluginsWeb.length, 3); + assert.strictEqual(Object.keys(pluginsNode).length, 2); + assert.strictEqual(instrumentations.length, 2); + }); + }); +}); diff --git a/packages/opentelemetry-instrumentation/test/node/BasePlugin.ts b/packages/opentelemetry-instrumentation/test/node/BasePlugin.ts new file mode 100644 index 0000000000..462dc6cb65 --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/BasePlugin.ts @@ -0,0 +1,45 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Logger, TracerProvider } from '@opentelemetry/api'; +import { OldClassPlugin, OldPluginConfig } from '../../src/types_plugin_only'; + +/** This class represent the base to patch plugin. */ +export abstract class BasePlugin implements OldClassPlugin { + abstract readonly moduleName: string; // required for internalFilesExports + protected _moduleExports!: T; + constructor( + protected readonly _tracerName: string, + protected readonly _tracerVersion?: string + ) {} + + enable( + moduleExports: T, + tracerProvider: TracerProvider, + logger: Logger, + config?: OldPluginConfig + ): T { + this._moduleExports = moduleExports; + return this.patch(); + } + + disable(): void { + this.unpatch(); + } + + protected abstract patch(): T; + protected abstract unpatch(): void; +} diff --git a/packages/opentelemetry-instrumentation/test/node/PluginLoader.test.ts b/packages/opentelemetry-instrumentation/test/node/PluginLoader.test.ts new file mode 100644 index 0000000000..6407f15029 --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/PluginLoader.test.ts @@ -0,0 +1,364 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { NoopLogger, NoopTracerProvider } from '@opentelemetry/api'; +import * as assert from 'assert'; +import * as path from 'path'; +import { + HookState, + PluginLoader, + Plugins, + searchPathForTest, + ENV_PLUGIN_DISABLED_LIST, +} from '../../src/platform/node/old/PluginLoader'; + +const INSTALLED_PLUGINS_PATH = path.join(__dirname, 'node_modules'); +/* eslint-disable node/no-extraneous-require */ +const simplePlugins: Plugins = { + 'simple-module': { + enabled: true, + path: '@opentelemetry/plugin-simple-module', + ignoreMethods: [], + ignoreUrls: [], + }, +}; + +const httpPlugins: Plugins = { + http: { + enabled: true, + path: '@opentelemetry/plugin-http-module', + ignoreMethods: [], + ignoreUrls: [], + }, +}; + +const disablePlugins: Plugins = { + 'simple-module': { + enabled: false, + path: '@opentelemetry/plugin-simple-module', + }, + nonexistent: { + enabled: false, + path: '@opentelemetry/plugin-nonexistent-module', + }, +}; + +const nonexistentPlugins: Plugins = { + nonexistent: { + enabled: true, + path: '@opentelemetry/plugin-nonexistent-module', + }, +}; + +const missingPathPlugins: Plugins = { + 'simple-module': { + enabled: true, + }, + nonexistent: { + enabled: true, + }, +}; + +const supportedVersionPlugins: Plugins = { + 'supported-module': { + enabled: true, + path: '@opentelemetry/plugin-supported-module', + }, +}; + +const notSupportedVersionPlugins: Plugins = { + 'notsupported-module': { + enabled: true, + path: 'notsupported-module', + }, +}; + +const alreadyRequiredPlugins: Plugins = { + 'already-require-module': { + enabled: true, + path: '@opentelemetry/plugin-supported-module', + }, +}; + +const differentNamePlugins: Plugins = { + 'random-module': { + enabled: true, + path: '@opentelemetry/plugin-http-module', + }, +}; + +describe('PluginLoader', () => { + const provider = new NoopTracerProvider(); + const logger = new NoopLogger(); + + before(() => { + module.paths.push(INSTALLED_PLUGINS_PATH); + searchPathForTest(INSTALLED_PLUGINS_PATH); + }); + + afterEach(() => { + // clear require cache + Object.keys(require.cache).forEach(key => delete require.cache[key]); + }); + + describe('.state()', () => { + it('returns UNINITIALIZED when first called', () => { + const pluginLoader = new PluginLoader(provider, logger); + assert.strictEqual(pluginLoader['_hookState'], HookState.UNINITIALIZED); + }); + + it('transitions from UNINITIALIZED to ENABLED', () => { + const pluginLoader = new PluginLoader(provider, logger); + pluginLoader.load(simplePlugins); + assert.strictEqual(pluginLoader['_hookState'], HookState.ENABLED); + pluginLoader.unload(); + }); + + it('transitions from ENABLED to DISABLED', () => { + const pluginLoader = new PluginLoader(provider, logger); + pluginLoader.load(simplePlugins).unload(); + assert.strictEqual(pluginLoader['_hookState'], HookState.DISABLED); + }); + }); + + describe('.load()', () => { + afterEach(() => { + delete process.env[ENV_PLUGIN_DISABLED_LIST]; + }); + + it('sanity check', () => { + // Ensure that module fixtures contain values that we expect. + const simpleModule = require('simple-module'); + const simpleModule001 = require('supported-module'); + const simpleModule100 = require('notsupported-module'); + + assert.strictEqual(simpleModule.name(), 'simple-module'); + assert.strictEqual(simpleModule001.name(), 'supported-module'); + assert.strictEqual(simpleModule100.name(), 'notsupported-module'); + + assert.strictEqual(simpleModule.value(), 0); + assert.strictEqual(simpleModule001.value(), 0); + assert.strictEqual(simpleModule100.value(), 0); + + assert.throws(() => require('nonexistent-module')); + }); + + it('should not load a plugin on the ignore list environment variable', () => { + // Set ignore list env var + process.env[ENV_PLUGIN_DISABLED_LIST] = 'simple-module'; + const pluginLoader = new PluginLoader(provider, logger); + pluginLoader.load({ ...simplePlugins, ...supportedVersionPlugins }); + + assert.strictEqual(pluginLoader['plugins'].length, 0); + + const simpleModule = require('simple-module'); + assert.strictEqual(pluginLoader['plugins'].length, 0); + assert.strictEqual(simpleModule.value(), 0); + assert.strictEqual(simpleModule.name(), 'simple-module'); + + const supportedModule = require('supported-module'); + assert.strictEqual(pluginLoader['plugins'].length, 1); + assert.strictEqual(supportedModule.value(), 1); + assert.strictEqual(supportedModule.name(), 'patched-supported-module'); + + pluginLoader.unload(); + }); + + it('should not load plugins on the ignore list environment variable', () => { + // Set ignore list env var + process.env[ENV_PLUGIN_DISABLED_LIST] = 'simple-module,http'; + const pluginLoader = new PluginLoader(provider, logger); + pluginLoader.load({ + ...simplePlugins, + ...supportedVersionPlugins, + ...httpPlugins, + }); + + assert.strictEqual(pluginLoader['plugins'].length, 0); + + const simpleModule = require('simple-module'); + assert.strictEqual(pluginLoader['plugins'].length, 0); + assert.strictEqual(simpleModule.value(), 0); + assert.strictEqual(simpleModule.name(), 'simple-module'); + + const httpModule = require('http'); + assert.ok(httpModule); + assert.strictEqual(pluginLoader['plugins'].length, 0); + + const supportedModule = require('supported-module'); + assert.strictEqual(pluginLoader['plugins'].length, 1); + assert.strictEqual(supportedModule.value(), 1); + assert.strictEqual(supportedModule.name(), 'patched-supported-module'); + + pluginLoader.unload(); + }); + + it('should not load any plugins if ignore list environment variable is set to "*"', () => { + // Set ignore list env var + process.env[ENV_PLUGIN_DISABLED_LIST] = '*'; + const pluginLoader = new PluginLoader(provider, logger); + pluginLoader.load({ + ...simplePlugins, + ...supportedVersionPlugins, + ...httpPlugins, + }); + + assert.strictEqual(pluginLoader['plugins'].length, 0); + + const simpleModule = require('simple-module'); + const httpModule = require('http'); + const supportedModule = require('supported-module'); + + assert.strictEqual( + pluginLoader['plugins'].length, + 0, + 'No plugins were loaded' + ); + assert.strictEqual(simpleModule.value(), 0); + assert.strictEqual(simpleModule.name(), 'simple-module'); + assert.ok(httpModule); + assert.strictEqual(supportedModule.value(), 0); + assert.strictEqual(supportedModule.name(), 'supported-module'); + + pluginLoader.unload(); + }); + + it('should load a plugin and patch the target modules', () => { + const pluginLoader = new PluginLoader(provider, logger); + assert.strictEqual(pluginLoader['plugins'].length, 0); + pluginLoader.load(simplePlugins); + // The hook is only called the first time the module is loaded. + const simpleModule = require('simple-module'); + assert.strictEqual(pluginLoader['plugins'].length, 1); + assert.strictEqual(simpleModule.value(), 1); + assert.strictEqual(simpleModule.name(), 'patched-simple-module'); + pluginLoader.unload(); + }); + + it('should load a plugin and patch the core module', () => { + const pluginLoader = new PluginLoader(provider, logger); + assert.strictEqual(pluginLoader['plugins'].length, 0); + pluginLoader.load(httpPlugins); + // The hook is only called the first time the module is loaded. + const httpModule = require('http'); + assert.strictEqual(pluginLoader['plugins'].length, 1); + assert.strictEqual(httpModule.get(), 'patched'); + pluginLoader.unload(); + }); + // @TODO: simplify this test once we can load module with custom path + it('should not load the plugin when supported versions does not match', () => { + const pluginLoader = new PluginLoader(provider, logger); + assert.strictEqual(pluginLoader['plugins'].length, 0); + pluginLoader.load(notSupportedVersionPlugins); + // The hook is only called the first time the module is loaded. + require('notsupported-module'); + assert.strictEqual(pluginLoader['plugins'].length, 0); + pluginLoader.unload(); + }); + // @TODO: simplify this test once we can load module with custom path + it('should load a plugin and patch the target modules when supported versions match', () => { + const pluginLoader = new PluginLoader(provider, logger); + assert.strictEqual(pluginLoader['plugins'].length, 0); + pluginLoader.load(supportedVersionPlugins); + // The hook is only called the first time the module is loaded. + const simpleModule = require('supported-module'); + assert.strictEqual(pluginLoader['plugins'].length, 1); + assert.strictEqual(simpleModule.value(), 1); + assert.strictEqual(simpleModule.name(), 'patched-supported-module'); + pluginLoader.unload(); + }); + + it('should not load a plugin when value is false', () => { + const pluginLoader = new PluginLoader(provider, logger); + assert.strictEqual(pluginLoader['plugins'].length, 0); + pluginLoader.load(disablePlugins); + const simpleModule = require('simple-module'); + assert.strictEqual(pluginLoader['plugins'].length, 0); + assert.strictEqual(simpleModule.value(), 0); + assert.strictEqual(simpleModule.name(), 'simple-module'); + pluginLoader.unload(); + }); + + it('should not load a plugin when value is true but path is missing', () => { + const pluginLoader = new PluginLoader(provider, logger); + assert.strictEqual(pluginLoader['plugins'].length, 0); + pluginLoader.load(missingPathPlugins); + const simpleModule = require('simple-module'); + assert.strictEqual(pluginLoader['plugins'].length, 0); + assert.strictEqual(simpleModule.value(), 0); + assert.strictEqual(simpleModule.name(), 'simple-module'); + pluginLoader.unload(); + }); + + it('should not load a non existing plugin', () => { + const pluginLoader = new PluginLoader(provider, logger); + assert.strictEqual(pluginLoader['plugins'].length, 0); + pluginLoader.load(nonexistentPlugins); + assert.strictEqual(pluginLoader['plugins'].length, 0); + pluginLoader.unload(); + }); + + it("doesn't patch modules for which plugins aren't specified", () => { + const pluginLoader = new PluginLoader(provider, logger); + pluginLoader.load({}); + assert.strictEqual(require('simple-module').value(), 0); + pluginLoader.unload(); + }); + + it('should warn when module was already loaded', callback => { + const verifyWarnLogger = { + error: logger.error, + info: logger.info, + debug: logger.debug, + warn: (message: string, ...args: unknown[]) => { + assert(message.match(/were already required when/)); + assert(message.match(/(already-require-module)/)); + return callback(); + }, + }; + require('already-require-module'); + const pluginLoader = new PluginLoader(provider, verifyWarnLogger); + pluginLoader.load(alreadyRequiredPlugins); + pluginLoader.unload(); + }); + + it('should not load a plugin that patches a different module that the one configured', () => { + const pluginLoader = new PluginLoader(provider, logger); + assert.strictEqual(pluginLoader['plugins'].length, 0); + pluginLoader.load(differentNamePlugins); + require('random-module'); + assert.strictEqual(pluginLoader['plugins'].length, 0); + pluginLoader.unload(); + }); + }); + + describe('.unload()', () => { + it('should unload the plugins and unpatch the target module when unloads', () => { + const pluginLoader = new PluginLoader(provider, logger); + assert.strictEqual(pluginLoader['plugins'].length, 0); + pluginLoader.load(simplePlugins); + // The hook is only called the first time the module is loaded. + const simpleModule = require('simple-module'); + assert.strictEqual(pluginLoader['plugins'].length, 1); + assert.strictEqual(simpleModule.value(), 1); + assert.strictEqual(simpleModule.name(), 'patched-simple-module'); + pluginLoader.unload(); + assert.strictEqual(pluginLoader['plugins'].length, 0); + assert.strictEqual(simpleModule.name(), 'simple-module'); + assert.strictEqual(simpleModule.value(), 0); + }); + }); +}); diff --git a/packages/opentelemetry-instrumentation/test/node/autoLoader.test.ts b/packages/opentelemetry-instrumentation/test/node/autoLoader.test.ts new file mode 100644 index 0000000000..5e14c2424e --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/autoLoader.test.ts @@ -0,0 +1,105 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { NOOP_METER_PROVIDER, NOOP_TRACER_PROVIDER } from '@opentelemetry/api'; +import * as assert from 'assert'; +import * as path from 'path'; +import * as sinon from 'sinon'; + +import { registerInstrumentations } from '../../src'; +import { + Plugins, + searchPathForTest, +} from '../../src/platform/node/old/PluginLoader'; + +const INSTALLED_PLUGINS_PATH = path.join(__dirname, 'node_modules'); + +const httpPlugin: Plugins = { + http: { + enabled: true, + path: '@opentelemetry/plugin-http-module', + ignoreMethods: [], + ignoreUrls: [], + }, +}; + +describe('autoLoader', () => { + let sandbox: sinon.SinonSandbox; + let unload: Function | undefined; + before(() => { + module.paths.push(INSTALLED_PLUGINS_PATH); + searchPathForTest(INSTALLED_PLUGINS_PATH); + }); + + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + afterEach(() => { + sandbox.restore(); + Object.keys(require.cache).forEach(key => delete require.cache[key]); + if (typeof unload === 'function') { + unload(); + unload = undefined; + } + }); + + describe('registerInstrumentations', () => { + describe('Old Plugins', () => { + let enableSpy: sinon.SinonSpy; + const tracerProvider = NOOP_TRACER_PROVIDER; + const meterProvider = NOOP_METER_PROVIDER; + beforeEach(() => { + // eslint-disable-next-line node/no-extraneous-require + const simpleModule = require('@opentelemetry/plugin-simple-module') + .plugin; + enableSpy = sandbox.spy(simpleModule, 'enable'); + unload = registerInstrumentations({ + instrumentations: [ + { + plugins: { + ...httpPlugin, + 'simple-module': { enabled: true, plugin: simpleModule }, + }, + }, + ], + tracerProvider, + meterProvider, + }); + }); + afterEach(() => { + Object.keys(require.cache).forEach(key => delete require.cache[key]); + if (typeof unload === 'function') { + unload(); + unload = undefined; + } + }); + + it('should enable a required plugin', () => { + // eslint-disable-next-line node/no-extraneous-require + const simpleModule = require('simple-module'); + assert.ok(simpleModule); + assert.strictEqual(enableSpy.callCount, 1); + }); + + it('should set TracerProvider', () => { + // eslint-disable-next-line node/no-extraneous-require + const simpleModule = require('simple-module'); + assert.ok(simpleModule); + assert.ok(enableSpy.lastCall.args[1] === tracerProvider); + }); + }); + }); +}); diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/http-module.js b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/http-module.js new file mode 100644 index 0000000000..99c16a8af2 --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/http-module.js @@ -0,0 +1,22 @@ +Object.defineProperty(exports, "__esModule", { value: true }); +const { BasePlugin } = require('../../../BasePlugin'); +const shimmer = require("shimmer"); + +class HttpModulePlugin extends BasePlugin { + constructor() { + super(); + this.moduleName = 'http'; + } + + patch() { + shimmer.wrap(this._moduleExports, 'get', orig => () => 'patched'); + return this._moduleExports; + } + + unpatch() { + shimmer.unwrap(this._moduleExports, 'get'); + } +} +exports.HttpModulePlugin = HttpModulePlugin; +const plugin = new HttpModulePlugin(); +exports.plugin = plugin; diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/index.js b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/index.js new file mode 100644 index 0000000000..4847af1405 --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/index.js @@ -0,0 +1,5 @@ +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} +Object.defineProperty(exports, "__esModule", { value: true }); +__export(require("./http-module")); diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/package.json b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/package.json new file mode 100644 index 0000000000..bb40eab67d --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-http-module/package.json @@ -0,0 +1,4 @@ +{ + "name": "@opentelemetry/plugin-http-module", + "version": "0.0.1" +} diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/index.js b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/index.js new file mode 100644 index 0000000000..1b22b5ce90 --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/index.js @@ -0,0 +1,5 @@ +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} +Object.defineProperty(exports, "__esModule", { value: true }); +__export(require("./simple-module")); diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/package.json b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/package.json new file mode 100644 index 0000000000..4db9e49b1d --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/package.json @@ -0,0 +1,4 @@ +{ + "name": "@opentelemetry/plugin-notsupported-module", + "version": "1.0.0" +} diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/simple-module.js b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/simple-module.js new file mode 100644 index 0000000000..3001ad4c61 --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-notsupported-module/simple-module.js @@ -0,0 +1,25 @@ +Object.defineProperty(exports, "__esModule", { value: true }); +const { BasePlugin } = require('../../../BasePlugin'); +const shimmer = require("shimmer"); + +class SimpleModulePlugin extends BasePlugin { + constructor() { + super(); + this.moduleName = 'notsupported-module'; + } + + patch() { + shimmer.wrap(this._moduleExports, 'name', orig => () => 'patched-' + orig.apply()); + shimmer.wrap(this._moduleExports, 'value', orig => () => orig.apply() + 1); + return this._moduleExports; + } + + unpatch() { + shimmer.unwrap(this._moduleExports, 'name'); + shimmer.unwrap(this._moduleExports, 'value'); + } +} +exports.SimpleModulePlugin = SimpleModulePlugin; +const plugin = new SimpleModulePlugin(); +plugin.supportedVersions = ['1.0.0']; +exports.plugin = plugin; diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/index.js b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/index.js new file mode 100644 index 0000000000..1b22b5ce90 --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/index.js @@ -0,0 +1,5 @@ +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} +Object.defineProperty(exports, "__esModule", { value: true }); +__export(require("./simple-module")); diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/package.json b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/package.json new file mode 100644 index 0000000000..59d87df350 --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/package.json @@ -0,0 +1,4 @@ +{ + "name": "@opentelemetry/plugin-simple-module", + "version": "0.0.1" +} diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/simple-module.js b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/simple-module.js new file mode 100644 index 0000000000..3cfacba5fa --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-simple-module/simple-module.js @@ -0,0 +1,24 @@ +Object.defineProperty(exports, "__esModule", { value: true }); +const { BasePlugin } = require('../../../BasePlugin'); +const shimmer = require("shimmer"); + +class SimpleModulePlugin extends BasePlugin { + constructor() { + super(); + this.moduleName = 'simple-module'; + } + + patch() { + shimmer.wrap(this._moduleExports, 'name', orig => () => 'patched-' + orig.apply()); + shimmer.wrap(this._moduleExports, 'value', orig => () => orig.apply() + 1); + return this._moduleExports; + } + + unpatch() { + shimmer.unwrap(this._moduleExports, 'name'); + shimmer.unwrap(this._moduleExports, 'value'); + } +} +exports.SimpleModulePlugin = SimpleModulePlugin; +const plugin = new SimpleModulePlugin(); +exports.plugin = plugin; diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/index.js b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/index.js new file mode 100644 index 0000000000..1b22b5ce90 --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/index.js @@ -0,0 +1,5 @@ +function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; +} +Object.defineProperty(exports, "__esModule", { value: true }); +__export(require("./simple-module")); diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/package.json b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/package.json new file mode 100644 index 0000000000..ca18bafa63 --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/package.json @@ -0,0 +1,4 @@ +{ + "name": "@opentelemetry/plugin-supported-module", + "version": "0.0.1" +} diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/simple-module.js b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/simple-module.js new file mode 100644 index 0000000000..3a77642f79 --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/@opentelemetry/plugin-supported-module/simple-module.js @@ -0,0 +1,25 @@ +Object.defineProperty(exports, "__esModule", { value: true }); +const { BasePlugin } = require('../../../BasePlugin'); +const shimmer = require("shimmer"); + +class SimpleModulePlugin extends BasePlugin { + constructor() { + super(); + this.moduleName = 'supported-module'; + } + + patch() { + shimmer.wrap(this._moduleExports, 'name', orig => () => 'patched-' + orig.apply()); + shimmer.wrap(this._moduleExports, 'value', orig => () => orig.apply() + 1); + return this._moduleExports; + } + + unpatch() { + shimmer.unwrap(this._moduleExports, 'name'); + shimmer.unwrap(this._moduleExports, 'value'); + } +} +exports.SimpleModulePlugin = SimpleModulePlugin; +const plugin = new SimpleModulePlugin(); +plugin.supportedVersions = ['^0.0.1']; +exports.plugin = plugin; diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/already-require-module/index.js b/packages/opentelemetry-instrumentation/test/node/node_modules/already-require-module/index.js new file mode 100644 index 0000000000..18c0f69a3b --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/already-require-module/index.js @@ -0,0 +1,4 @@ +module.exports = { + name: () => 'already-module', + value: () => 0, +}; diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/already-require-module/package.json b/packages/opentelemetry-instrumentation/test/node/node_modules/already-require-module/package.json new file mode 100644 index 0000000000..7ae0ab8f09 --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/already-require-module/package.json @@ -0,0 +1,4 @@ +{ + "name": "already-module", + "version": "0.1.0" +} diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/notsupported-module/index.js b/packages/opentelemetry-instrumentation/test/node/node_modules/notsupported-module/index.js new file mode 100644 index 0000000000..4fe98dae33 --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/notsupported-module/index.js @@ -0,0 +1,4 @@ +module.exports = { + name: () => 'notsupported-module', + value: () => 0, +}; diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/notsupported-module/package.json b/packages/opentelemetry-instrumentation/test/node/node_modules/notsupported-module/package.json new file mode 100644 index 0000000000..9494b2866e --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/notsupported-module/package.json @@ -0,0 +1,4 @@ +{ + "name": "notsupported-module", + "version": "0.0.1" +} diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/random-module/index.js b/packages/opentelemetry-instrumentation/test/node/node_modules/random-module/index.js new file mode 100644 index 0000000000..35a4110c28 --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/random-module/index.js @@ -0,0 +1,4 @@ +module.exports = { + name: () => 'random-module', + value: () => 0, +}; diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/random-module/package.json b/packages/opentelemetry-instrumentation/test/node/node_modules/random-module/package.json new file mode 100644 index 0000000000..a5c840081b --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/random-module/package.json @@ -0,0 +1,4 @@ +{ + "name": "random-module", + "version": "0.1.0" +} diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/simple-module/index.js b/packages/opentelemetry-instrumentation/test/node/node_modules/simple-module/index.js new file mode 100644 index 0000000000..8ec2e77ffd --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/simple-module/index.js @@ -0,0 +1,4 @@ +module.exports = { + name: () => 'simple-module', + value: () => 0, +}; diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/simple-module/package.json b/packages/opentelemetry-instrumentation/test/node/node_modules/simple-module/package.json new file mode 100644 index 0000000000..2eba36a5bd --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/simple-module/package.json @@ -0,0 +1,4 @@ +{ + "name": "simple-module", + "version": "0.1.0" +} diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/supported-module/index.js b/packages/opentelemetry-instrumentation/test/node/node_modules/supported-module/index.js new file mode 100644 index 0000000000..090d0db5fb --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/supported-module/index.js @@ -0,0 +1,4 @@ +module.exports = { + name: () => 'supported-module', + value: () => 0, +}; diff --git a/packages/opentelemetry-instrumentation/test/node/node_modules/supported-module/package.json b/packages/opentelemetry-instrumentation/test/node/node_modules/supported-module/package.json new file mode 100644 index 0000000000..ffd520afda --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/node_modules/supported-module/package.json @@ -0,0 +1,4 @@ +{ + "name": "supported-module", + "version": "0.0.1" +} diff --git a/packages/opentelemetry-instrumentation/test/node/utils.test.ts b/packages/opentelemetry-instrumentation/test/node/utils.test.ts new file mode 100644 index 0000000000..273c98e2dc --- /dev/null +++ b/packages/opentelemetry-instrumentation/test/node/utils.test.ts @@ -0,0 +1,95 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { NoopLogger } from '@opentelemetry/api'; +import * as assert from 'assert'; +import * as path from 'path'; +import * as utils from '../../src/platform/node/old/utils'; + +const INSTALLED_PLUGINS_PATH = path.join(__dirname, 'node_modules'); +const TEST_MODULES: Array<{ name: string; version: string | null }> = [ + { + name: 'simple-module', + version: '0.1.0', + }, + { + name: 'nonexistent-module', + version: null, + }, + { + name: 'http', + version: null, + }, +]; + +describe('Instrumentation#utils', () => { + const logger = new NoopLogger(); + + before(() => { + utils.searchPathForTest(INSTALLED_PLUGINS_PATH); + }); + + describe('getPackageVersion', () => { + TEST_MODULES.forEach(testCase => { + it(`should return ${testCase.version} for ${testCase.name}`, () => { + assert.strictEqual( + utils.getPackageVersion(logger, testCase.name), + testCase.version + ); + }); + }); + }); + describe('isSupportedVersion', () => { + const version = '1.0.1'; + + it('should return true when supportedVersions is not defined', () => { + assert.strictEqual(utils.isSupportedVersion('1.0.0', undefined), true); + }); + + [ + ['1.X'], + [version], + ['1.X.X', '3.X.X'], + ['^1.0.0'], + ['~1.0.0', '^0.1.0'], + ['*'], + ['>1.0.0'], + [], + ].forEach(supportedVersion => { + it(`should return true when version is equal to ${version} and supportedVersions is equal to ${supportedVersion}`, () => { + assert.strictEqual( + utils.isSupportedVersion(version, supportedVersion), + true + ); + }); + }); + + [['0.X'], ['0.1.0'], ['0.X.X'], ['^0.1.0'], ['1.0.0'], ['<1.0.0']].forEach( + supportedVersion => { + it(`should return false when version is equal to ${version} and supportedVersions is equal to ${supportedVersion}`, () => { + assert.strictEqual( + utils.isSupportedVersion(version, supportedVersion), + false + ); + }); + } + ); + + it("should return false when version is equal to null and supportedVersions is equal to '*'", () => { + assert.strictEqual(utils.isSupportedVersion(null as any, ['*']), false); + }); + }); +}); diff --git a/packages/opentelemetry-instrumentation/tsconfig.json b/packages/opentelemetry-instrumentation/tsconfig.json index 0c36dd20f4..a2042cd68b 100644 --- a/packages/opentelemetry-instrumentation/tsconfig.json +++ b/packages/opentelemetry-instrumentation/tsconfig.json @@ -1,7 +1,6 @@ { "extends": "../tsconfig.base", "compilerOptions": { -// "sourceMap": false, "rootDir": ".", "outDir": "build" }, From 4be9902bbee7beaf90ef959255997f4a3a7fb429 Mon Sep 17 00:00:00 2001 From: Bartlomiej Obecny Date: Fri, 18 Dec 2020 17:01:21 +0100 Subject: [PATCH 4/8] chore: removing temporary test --- examples/test/README.md | 1 - examples/test/client.js | 49 ----- examples/test/docker/collector-config.yaml | 29 --- examples/test/docker/docker-compose.yaml | 30 --- examples/test/docker/prometheus.yaml | 9 - examples/test/package.json | 52 ------ examples/test/schema.js | 202 --------------------- examples/test/server-express.js | 19 -- examples/test/tracer.js | 89 --------- 9 files changed, 480 deletions(-) delete mode 100644 examples/test/README.md delete mode 100644 examples/test/client.js delete mode 100644 examples/test/docker/collector-config.yaml delete mode 100644 examples/test/docker/docker-compose.yaml delete mode 100644 examples/test/docker/prometheus.yaml delete mode 100644 examples/test/package.json delete mode 100644 examples/test/schema.js delete mode 100644 examples/test/server-express.js delete mode 100644 examples/test/tracer.js diff --git a/examples/test/README.md b/examples/test/README.md deleted file mode 100644 index f00b526a98..0000000000 --- a/examples/test/README.md +++ /dev/null @@ -1 +0,0 @@ -# Testing diff --git a/examples/test/client.js b/examples/test/client.js deleted file mode 100644 index 3aa487a1ad..0000000000 --- a/examples/test/client.js +++ /dev/null @@ -1,49 +0,0 @@ -'use strict'; - -const url = require('url'); -const http = require('http'); -// Construct a schema, using GraphQL schema language - -const source = ` -query { - books { - name - authors { - name - address { - country - } - } - } -} -`; - -makeRequest(source).then(console.log); - -function makeRequest(query) { - return new Promise((resolve, reject) => { - const parsedUrl = new url.URL('http://localhost:4000/graphql'); - const options = { - hostname: parsedUrl.hostname, - port: parsedUrl.port, - path: parsedUrl.pathname, - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - }; - const req = http.request(options, (res) => { - const data = []; - res.on('data', (chunk) => data.push(chunk)); - res.on('end', () => { - resolve(data.toString()); - }); - res.on('error', (err) => { - reject(err); - }); - }); - - req.write(JSON.stringify({ query })); - req.end(); - }); -} diff --git a/examples/test/docker/collector-config.yaml b/examples/test/docker/collector-config.yaml deleted file mode 100644 index e9a909d78f..0000000000 --- a/examples/test/docker/collector-config.yaml +++ /dev/null @@ -1,29 +0,0 @@ -receivers: - otlp: - protocols: - grpc: - http: - cors_allowed_origins: - - http://* - - https://* - -exporters: - zipkin: - endpoint: "http://zipkin-all-in-one:9411/api/v2/spans" - prometheus: - endpoint: "0.0.0.0:9464" - -processors: - batch: - queued_retry: - -service: - pipelines: - traces: - receivers: [otlp] - exporters: [zipkin] - processors: [batch, queued_retry] - metrics: - receivers: [otlp] - exporters: [prometheus] - processors: [batch, queued_retry] diff --git a/examples/test/docker/docker-compose.yaml b/examples/test/docker/docker-compose.yaml deleted file mode 100644 index d4e0a42a19..0000000000 --- a/examples/test/docker/docker-compose.yaml +++ /dev/null @@ -1,30 +0,0 @@ -version: "3" -services: - # Collector - collector: -# image: otel/opentelemetry-collector:latest - image: otel/opentelemetry-collector:0.13.0 - command: ["--config=/conf/collector-config.yaml", "--log-level=DEBUG"] - volumes: - - ./collector-config.yaml:/conf/collector-config.yaml - ports: - - "9464:9464" - - "55680:55680" - - "55681:55681" - depends_on: - - zipkin-all-in-one - - # Zipkin - zipkin-all-in-one: - image: openzipkin/zipkin:latest - ports: - - "9411:9411" - - # Prometheus - prometheus: - container_name: prometheus - image: prom/prometheus:latest - volumes: - - ./prometheus.yaml:/etc/prometheus/prometheus.yml - ports: - - "9090:9090" diff --git a/examples/test/docker/prometheus.yaml b/examples/test/docker/prometheus.yaml deleted file mode 100644 index b027daf9a0..0000000000 --- a/examples/test/docker/prometheus.yaml +++ /dev/null @@ -1,9 +0,0 @@ -global: - scrape_interval: 15s # Default is every 1 minute. - -scrape_configs: - - job_name: 'collector' - # metrics_path defaults to '/metrics' - # scheme defaults to 'http'. - static_configs: - - targets: ['collector:9464'] diff --git a/examples/test/package.json b/examples/test/package.json deleted file mode 100644 index 686087905a..0000000000 --- a/examples/test/package.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "name": "opentelemetry-testing", - "private": true, - "version": "0.11.0", - "description": "Testing", - "main": "index.js", - "scripts": { - "docker:start": "cd ./docker && docker-compose down && docker-compose up", - "docker:stop": "cd ./docker && docker-compose down", - "start:test": "node ./client.js", - "server:express": "node ./server-express.js" - }, - "repository": { - "type": "git", - "url": "git+ssh://git@github.com/open-telemetry/opentelemetry-js.git" - }, - "keywords": [ - "opentelemetry", - "http", - "tracing", - "graphql" - ], - "engines": { - "node": ">=8" - }, - "author": "OpenTelemetry Authors", - "license": "Apache-2.0", - "bugs": { - "url": "https://github.com/open-telemetry/opentelemetry-js/issues" - }, - "dependencies": { - "@opentelemetry/api": "^0.12.0", - "@opentelemetry/context-async-hooks": "^0.12.0", - "@opentelemetry/exporter-collector": "^0.12.0", - "@opentelemetry/node": "^0.12.0", - "@opentelemetry/plugin-express": "^0.11.0", - "@opentelemetry/instrumentation": "^0.12.0", - "@opentelemetry/instrumentation-graphql": "^0.11.0", - "@opentelemetry/plugin-http": "^0.12.0", - "@opentelemetry/plugin-https": "^0.12.0", - "@opentelemetry/tracing": "^0.12.0", - "apollo-server": "^2.18.1", - "express": "^4.17.1", - "express-graphql": "^0.11.0", - "graphql": "^15.3.0", - "qs-middleware": "^1.0.3" - }, - "homepage": "https://github.com/open-telemetry/opentelemetry-js#readme", - "devDependencies": { - "cross-env": "^6.0.0" - } -} diff --git a/examples/test/schema.js b/examples/test/schema.js deleted file mode 100644 index be98379c40..0000000000 --- a/examples/test/schema.js +++ /dev/null @@ -1,202 +0,0 @@ -'use strict'; - -const https = require('https'); -const graphql = require('graphql'); - -const url1 = 'https://raw.githubusercontent.com/open-telemetry/opentelemetry-js/master/package.json'; - -function getData(url) { - return new Promise((resolve, reject) => { - https.get(url, (response) => { - let data = ''; - response.on('data', (chunk) => { - data += chunk; - }); - response.on('end', () => { - resolve(JSON.parse(data)); - }); - }).on('error', (err) => { - reject(err); - }); - }); -} - -const authors = []; -const books = []; - -function addBook(name, authorIds) { - let authorIdsLocal = authorIds; - if (typeof authorIdsLocal === 'string') { - authorIdsLocal = authorIdsLocal.split(',').map((id) => parseInt(id, 10)); - } - const id = books.length; - books.push({ - id, - name, - authorIds: authorIdsLocal, - }); - return books[books.length - 1]; -} - -function addAuthor(name, country, city) { - const id = authors.length; - authors.push({ - id, - name, - address: { - country, - city, - }, - }); - return authors[authors.length - 1]; -} - -function getBook(id) { - return books[id]; -} - -function getAuthor(id) { - return authors[id]; -} - -function prepareData() { - addAuthor('John', 'Poland', 'Szczecin'); - addAuthor('Alice', 'Poland', 'Warsaw'); - addAuthor('Bob', 'England', 'London'); - addAuthor('Christine', 'France', 'Paris'); - addBook('First Book', [0, 1]); - addBook('Second Book', [2]); - addBook('Third Book', [3]); -} - -prepareData(); -module.exports = function buildSchema() { - const Author = new graphql.GraphQLObjectType({ - name: 'Author', - fields: { - id: { - type: graphql.GraphQLString, - resolve(obj, _args) { - return obj.id; - }, - }, - name: { - type: graphql.GraphQLString, - resolve(obj, _args) { - return obj.name; - }, - }, - description: { - type: graphql.GraphQLString, - resolve(_obj, _args) { - return new Promise((resolve, reject) => { - getData(url1).then((response) => { - resolve(response.description); - }, reject); - }); - }, - }, - address: { - type: new graphql.GraphQLObjectType({ - name: 'Address', - fields: { - country: { - type: graphql.GraphQLString, - resolve(obj, _args) { - return obj.country; - }, - }, - city: { - type: graphql.GraphQLString, - resolve(obj, _args) { - return obj.city; - }, - }, - }, - }), - resolve(obj, _args) { - return obj.address; - }, - }, - }, - }); - - const Book = new graphql.GraphQLObjectType({ - name: 'Book', - fields: { - id: { - type: graphql.GraphQLInt, - resolve(obj, _args) { - return obj.id; - }, - }, - name: { - type: graphql.GraphQLString, - resolve(obj, _args) { - return obj.name; - }, - }, - authors: { - type: new graphql.GraphQLList(Author), - resolve(obj, _args) { - return obj.authorIds.map((id) => authors[id]); - }, - }, - }, - }); - - const query = new graphql.GraphQLObjectType({ - name: 'Query', - fields: { - author: { - type: Author, - args: { - id: { type: graphql.GraphQLInt }, - }, - resolve(obj, args, _context) { - return Promise.resolve(getAuthor(args.id)); - }, - }, - authors: { - type: new graphql.GraphQLList(Author), - resolve(_obj, _args, _context) { - return Promise.resolve(authors); - }, - }, - book: { - type: Book, - args: { - id: { type: graphql.GraphQLInt }, - }, - resolve(obj, args, _context) { - return Promise.resolve(getBook(args.id)); - }, - }, - books: { - type: new graphql.GraphQLList(Book), - resolve(_obj, _args, _context) { - return Promise.resolve(books); - }, - }, - }, - }); - - const mutation = new graphql.GraphQLObjectType({ - name: 'Mutation', - fields: { - addBook: { - type: Book, - args: { - name: { type: new graphql.GraphQLNonNull(graphql.GraphQLString) }, - authorIds: { type: new graphql.GraphQLNonNull(graphql.GraphQLString) }, - }, - resolve(obj, args, _context) { - return Promise.resolve(addBook(args.name, args.authorIds)); - }, - }, - }, - }); - - const schema = new graphql.GraphQLSchema({ query, mutation }); - return schema; -}; diff --git a/examples/test/server-express.js b/examples/test/server-express.js deleted file mode 100644 index 6bba89f817..0000000000 --- a/examples/test/server-express.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -require('./tracer'); - -const express = require('express'); -const { graphqlHTTP } = require('express-graphql'); -const buildSchema = require('./schema'); - -const schema = buildSchema(); - -const app = express(); -app.use('/graphql', graphqlHTTP({ - schema, - graphiql: true, -})); - -app.listen(4000); - -console.log('Running a GraphQL API server at http://localhost:4000/graphql'); diff --git a/examples/test/tracer.js b/examples/test/tracer.js deleted file mode 100644 index 790ddb79f4..0000000000 --- a/examples/test/tracer.js +++ /dev/null @@ -1,89 +0,0 @@ -'use strict'; - -const { GraphQLInstrumentation } = require('@opentelemetry/instrumentation-graphql'); - -const { ConsoleSpanExporter, SimpleSpanProcessor } = require('@opentelemetry/tracing'); -const { NodeTracerProvider } = require('@opentelemetry/node'); -const { BasicTracerProvider } = require('@opentelemetry/tracing'); -const { CollectorTraceExporter } = require('@opentelemetry/exporter-collector'); -const { registerInstrumentations } = require('@opentelemetry/instrumentation'); -const httpPlugin = require('@opentelemetry/plugin-http').plugin; -const httpsPlugin = require('@opentelemetry/plugin-https').plugin; -const expressPlugin = require('@opentelemetry/plugin-express').plugin; -const { ConsoleLogger } = require('@opentelemetry/core'); -const { AsyncHooksContextManager, AsyncLocalStorageContextManager } = require('@opentelemetry/context-async-hooks'); - -const provider = new BasicTracerProvider(); - -// const provider = new NodeTracerProvider({ -// plugins: { -// http: { enabled: false }, -// https: { enabled: false }, -// express: { enabled: false }, -// }, -// }); - -const currentDir = __dirname; -registerInstrumentations({ - instrumentations: [ - // new GraphQLInstrumentation(), - GraphQLInstrumentation, - { - plugins: { - // http: { enabled: true, path: `${currentDir}/node_modules/@opentelemetry/plugin-http/build/src/` }, - https: { enabled: false, path: `${currentDir}/node_modules/@opentelemetry/plugin-https/build/src/` }, - express: { enabled: true, path: `${currentDir}/node_modules/@opentelemetry/plugin-express/build/src/` }, - - http: { enabled: true, plugin: httpPlugin }, - // https: { enabled: true, plugin: httpsPlugin }, - // express: { enabled: true, path: '@opentelemetry/plugin-express' }, - - // http: { enabled: false, path: '@opentelemetry/plugin-http' }, - // https: { enabled: false, path: '@opentelemetry/plugin-https' }, - // express: { enabled: true, plugin: expressPlugin }, - }, - }, - ], - tracerProvider: provider, - logger: new ConsoleLogger() -}); - -const exporter = new CollectorTraceExporter({ - serviceName: 'basic-service', -}); - -// const provider = new NodeTracerProvider({ -// plugins: { -// http: { enabled: false }, -// https: { enabled: false }, -// express: { enabled: false }, -// http: { enabled: true, path: `${currentDir}/node_modules/@opentelemetry/plugin-http/build/src/` }, -// https: { enabled: false, path: `${currentDir}/node_modules/@opentelemetry/plugin-https/build/src/` }, -// express: { enabled: true, path: `${currentDir}/node_modules/@opentelemetry/plugin-express/build/src/` }, -// }, -// }); - -// const graphQLInstrumentation = new GraphQLInstrumentation({ -// // allowAttributes: true, -// // depth: 2, -// // mergeItems: true, -// }); -// -// graphQLInstrumentation.setTracerProvider(provider); -// -// graphQLInstrumentation.enable(); -// -provider.addSpanProcessor(new SimpleSpanProcessor(exporter)); -provider.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); - // if (config.contextManager === undefined) { - // const ContextManager = semver.gte(process.version, '14.8.0') - // ? AsyncLocalStorageContextManager - // : AsyncHooksContextManager; - // config.contextManager = new ContextManager(); - // config.contextManager.enable(); - // } - -provider.register({ - contextManager: new AsyncLocalStorageContextManager(), - // contextManager: new AsyncHooksContextManager(), -}); From 7de7b81246d1d3a158074d6e829e9ae057f53a68 Mon Sep 17 00:00:00 2001 From: Bartlomiej Obecny Date: Fri, 18 Dec 2020 17:04:03 +0100 Subject: [PATCH 5/8] chore: reverting changes done temporary --- .../examples/xml-http-request/index.js | 37 ++++--------------- examples/tracer-web/webpack.config.js | 2 +- 2 files changed, 9 insertions(+), 30 deletions(-) diff --git a/examples/tracer-web/examples/xml-http-request/index.js b/examples/tracer-web/examples/xml-http-request/index.js index d3e08c6818..2532037bc9 100644 --- a/examples/tracer-web/examples/xml-http-request/index.js +++ b/examples/tracer-web/examples/xml-http-request/index.js @@ -4,37 +4,16 @@ import { XMLHttpRequestInstrumentation } from '@opentelemetry/instrumentation-xm import { ZoneContextManager } from '@opentelemetry/context-zone'; import { CollectorTraceExporter } from '@opentelemetry/exporter-collector'; import { B3Propagator } from '@opentelemetry/propagator-b3'; -import { registerInstrumentations } from '@opentelemetry/instrumentation'; -import { UserInteractionPlugin } from '@opentelemetry/plugin-user-interaction'; -import { DocumentLoad } from '@opentelemetry/plugin-document-load'; - const providerWithZone = new WebTracerProvider({ - // plugins: [ - // new XMLHttpRequestInstrumentation({ - // ignoreUrls: [/localhost:8090\/sockjs-node/], - // propagateTraceHeaderCorsUrls: [ - // 'https://httpbin.org/get', - // ], - // }), - // ], -}); - -registerInstrumentations({ - // instrumentations: [XMLHttpRequestInstrumentation], - instrumentations: [ - XMLHttpRequestInstrumentation, - // new XMLHttpRequestInstrumentation({ - // ignoreUrls: [/localhost:8090\/sockjs-node/], - // propagateTraceHeaderCorsUrls: [ - // 'https://httpbin.org/get', - // ], - // }), - new UserInteractionPlugin(), - new UserInteractionPlugin(), - new DocumentLoad(), + plugins: [ + new XMLHttpRequestInstrumentation({ + ignoreUrls: [/localhost:8090\/sockjs-node/], + propagateTraceHeaderCorsUrls: [ + 'https://httpbin.org/get', + ], + }), ], - tracerProvider: providerWithZone, }); providerWithZone.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter())); @@ -77,7 +56,7 @@ const prepareClickEvent = () => { getData(url1).then((_data) => { webTracerWithZone.getCurrentSpan().addEvent('fetching-span1-completed'); span1.end(); - }, () => { + }, ()=> { webTracerWithZone.getCurrentSpan().addEvent('fetching-error'); span1.end(); }); diff --git a/examples/tracer-web/webpack.config.js b/examples/tracer-web/webpack.config.js index 20db2ce403..00773db28b 100644 --- a/examples/tracer-web/webpack.config.js +++ b/examples/tracer-web/webpack.config.js @@ -17,7 +17,7 @@ const common = { output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js', - // sourceMapFilename: '[file].map', + sourceMapFilename: '[file].map', }, target: 'web', module: { From 6d785964e1ea02950b2bf43dc8a6af335c126f5b Mon Sep 17 00:00:00 2001 From: Bartlomiej Obecny Date: Fri, 18 Dec 2020 18:02:25 +0100 Subject: [PATCH 6/8] chore: linting --- packages/opentelemetry-instrumentation/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/opentelemetry-instrumentation/README.md b/packages/opentelemetry-instrumentation/README.md index 2ca9a1c6cb..cd9ef8798c 100644 --- a/packages/opentelemetry-instrumentation/README.md +++ b/packages/opentelemetry-instrumentation/README.md @@ -159,10 +159,12 @@ myPLugin.enable(); ``` ## AutoLoader + Successor of loading plugins through TracerProvider "plugins" option. It also supersedes PluginLoader for node. The old configurations usually looks like ### NODE - old way using TracerProvider + ```javascript const { NodeTracerProvider } = require('@opentelemetry/node'); const { B3Propagator } = require('@opentelemetry/propagator-b3'); @@ -177,6 +179,7 @@ provider.register({ ``` ### WEB - old way using TracerProvider + ```javascript const { WebTracerProvider } = require('@opentelemetry/web'); const { UserInteractionPlugin } = require('@opentelemetry/plugin-user-interaction'); @@ -202,6 +205,7 @@ After change it will look like this - mixing plugins and instrumentations togeth All plugins will be bound to TracerProvider as well as instrumentations ### NODE - Auto Loader + ```javascript const { B3Propagator } = require('@opentelemetry/propagator-b3'); const { registerInstrumentations } = require('@opentelemetry/instrumentation'); @@ -231,6 +235,7 @@ tracerProvider.register({ ``` ### WEB - Auto Loader + ```javascript const { B3Propagator } = require('@opentelemetry/propagator-b3'); const { registerInstrumentations } = require('@opentelemetry/instrumentation'); From babcea6e19a276f2aa87c1d1959440d6eaa7bfbd Mon Sep 17 00:00:00 2001 From: Bartlomiej Obecny Date: Fri, 18 Dec 2020 18:45:47 +0100 Subject: [PATCH 7/8] chore: updating submodule for opentelemetry-proto --- packages/opentelemetry-exporter-collector-proto/protos | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-exporter-collector-proto/protos b/packages/opentelemetry-exporter-collector-proto/protos index 313a868be2..59c488bfb8 160000 --- a/packages/opentelemetry-exporter-collector-proto/protos +++ b/packages/opentelemetry-exporter-collector-proto/protos @@ -1 +1 @@ -Subproject commit 313a868be259dce6c6516dd417d3ad5fd3321acf +Subproject commit 59c488bfb8fb6d0458ad6425758b70259ff4a2bd From dea07980b9120bda15ae35b756ce251d1df56051 Mon Sep 17 00:00:00 2001 From: Bartlomiej Obecny Date: Fri, 18 Dec 2020 18:46:36 +0100 Subject: [PATCH 8/8] chore: updating submodule for exporter-collector-grpc --- packages/opentelemetry-exporter-collector-grpc/protos | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opentelemetry-exporter-collector-grpc/protos b/packages/opentelemetry-exporter-collector-grpc/protos index 313a868be2..59c488bfb8 160000 --- a/packages/opentelemetry-exporter-collector-grpc/protos +++ b/packages/opentelemetry-exporter-collector-grpc/protos @@ -1 +1 @@ -Subproject commit 313a868be259dce6c6516dd417d3ad5fd3321acf +Subproject commit 59c488bfb8fb6d0458ad6425758b70259ff4a2bd