Base class for node which OpenTelemetry instrumentation modules extend
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
The module definition system allows instrumentations to specify which modules and files to instrument, their supported versions, and how to patch/unpatch them.
Defines instrumentation for an entire Node.js module with optional file-level patching.
/**
* Implementation of InstrumentationModuleDefinition for Node.js modules
*/
class InstrumentationNodeModuleDefinition implements InstrumentationModuleDefinition {
files: InstrumentationModuleFile[];
constructor(
/** Module name or path */
public name: string,
/** Supported version ranges using semver syntax */
public supportedVersions: string[],
/** Optional module-level patch function */
public patch?: (exports: any, moduleVersion?: string) => any,
/** Optional module-level unpatch function */
public unpatch?: (exports: any, moduleVersion?: string) => void,
/** Optional file-level instrumentation definitions */
files?: InstrumentationModuleFile[]
);
}Usage Example:
import { InstrumentationNodeModuleDefinition } from "@opentelemetry/instrumentation";
// Simple module-level instrumentation
const httpDefinition = new InstrumentationNodeModuleDefinition(
"http",
["*"], // Support all versions
(moduleExports, moduleVersion) => {
// Patch http.request and http.get
shimmer.wrap(moduleExports, 'request', wrapRequest);
shimmer.wrap(moduleExports, 'get', wrapGet);
return moduleExports;
},
(moduleExports, moduleVersion) => {
// Unpatch http.request and http.get
shimmer.unwrap(moduleExports, 'request');
shimmer.unwrap(moduleExports, 'get');
}
);
// Module with version constraints
const expressDefinition = new InstrumentationNodeModuleDefinition(
"express",
[">=4.0.0 <6.0.0"], // Support Express 4.x and 5.x
(moduleExports, moduleVersion) => {
// Patch Express application
return moduleExports;
}
);Defines instrumentation for specific files within a module.
/**
* Implementation of InstrumentationModuleFile for Node.js module files
*/
class InstrumentationNodeModuleFile implements InstrumentationModuleFile {
public name: string;
constructor(
/** File name with relative path */
name: string,
/** Supported version ranges using semver syntax */
public supportedVersions: string[],
/** File-level patch function */
public patch: (moduleExports: any, moduleVersion?: string) => any,
/** File-level unpatch function */
public unpatch: (moduleExports?: any, moduleVersion?: string) => void
);
}Usage Example:
import { InstrumentationNodeModuleFile } from "@opentelemetry/instrumentation";
// File-level instrumentation
const routerFile = new InstrumentationNodeModuleFile(
"lib/router/index.js",
[">=4.0.0 <6.0.0"],
(moduleExports, moduleVersion) => {
// Patch specific router methods
shimmer.wrap(moduleExports.prototype, 'use', wrapUse);
return moduleExports;
},
(moduleExports, moduleVersion) => {
// Unpatch router methods
shimmer.unwrap(moduleExports.prototype, 'use');
}
);
// Combined module with file-level instrumentation
const expressWithFiles = new InstrumentationNodeModuleDefinition(
"express",
[">=4.0.0 <6.0.0"],
undefined, // No module-level patch
undefined, // No module-level unpatch
[routerFile] // File-level instrumentation
);Core interfaces for module and file instrumentation definitions.
/**
* Interface for module instrumentation definition
*/
interface InstrumentationModuleDefinition {
/** Module name or path */
name: string;
/** Module exports (set at runtime) */
moduleExports?: any;
/** Module version (set at runtime) */
moduleVersion?: string;
/** Supported version ranges */
supportedVersions: string[];
/** File-level instrumentation definitions */
files: InstrumentationModuleFile[];
/** Include prerelease versions in semver check */
includePrerelease?: boolean;
/** Optional module-level patch function */
patch?: (moduleExports: any, moduleVersion?: string) => any;
/** Optional module-level unpatch function */
unpatch?: (moduleExports: any, moduleVersion?: string) => void;
}
/**
* Interface for file instrumentation definition
*/
interface InstrumentationModuleFile {
/** File name with relative path */
name: string;
/** Module exports for this file (set at runtime) */
moduleExports?: unknown;
/** Supported version ranges */
supportedVersions: string[];
/** File-level patch function */
patch(moduleExports: unknown, moduleVersion?: string): unknown;
/** File-level unpatch function */
unpatch(moduleExports?: unknown, moduleVersion?: string): void;
}Common patterns for specifying supported module versions using semver ranges.
Usage Examples:
// Support all versions
new InstrumentationNodeModuleDefinition("lodash", ["*"]);
// Support specific major version
new InstrumentationNodeModuleDefinition("express", [">=4.0.0 <5.0.0"]);
// Support multiple major versions
new InstrumentationNodeModuleDefinition("mongoose", [">=5.0.0 <6.0.0", ">=6.0.0 <8.0.0"]);
// Support from specific version onwards
new InstrumentationNodeModuleDefinition("redis", [">=2.6.0"]);
// Support exact version
new InstrumentationNodeModuleDefinition("mysql", ["2.18.1"]);
// Support prerelease versions
const definition = new InstrumentationNodeModuleDefinition("beta-package", [">=1.0.0-beta"]);
definition.includePrerelease = true;Advanced example showing module and file-level instrumentation together.
Usage Example:
import {
InstrumentationNodeModuleDefinition,
InstrumentationNodeModuleFile
} from "@opentelemetry/instrumentation";
class ComplexInstrumentation extends InstrumentationBase {
protected init() {
// File-level instrumentations
const applicationFile = new InstrumentationNodeModuleFile(
"lib/application.js",
[">=4.0.0 <6.0.0"],
(moduleExports, moduleVersion) => {
shimmer.wrap(moduleExports, 'listen', this._patchListen.bind(this));
return moduleExports;
},
(moduleExports) => {
shimmer.unwrap(moduleExports, 'listen');
}
);
const routerFile = new InstrumentationNodeModuleFile(
"lib/router/index.js",
[">=4.0.0 <6.0.0"],
(moduleExports, moduleVersion) => {
shimmer.wrap(moduleExports.prototype, 'use', this._patchUse.bind(this));
return moduleExports;
},
(moduleExports) => {
shimmer.unwrap(moduleExports.prototype, 'use');
}
);
// Module definition with both module and file-level instrumentation
return new InstrumentationNodeModuleDefinition(
"express",
[">=4.0.0 <6.0.0"],
(moduleExports, moduleVersion) => {
// Module-level patches
shimmer.wrap(moduleExports, 'Router', this._patchRouter.bind(this));
return moduleExports;
},
(moduleExports) => {
// Module-level unpatches
shimmer.unwrap(moduleExports, 'Router');
},
[applicationFile, routerFile] // File-level instrumentation
);
}
private _patchListen(original: Function) {
return function(this: any, ...args: any[]) {
// Add server startup tracing
return original.apply(this, args);
};
}
private _patchUse(original: Function) {
return function(this: any, ...args: any[]) {
// Add middleware tracing
return original.apply(this, args);
};
}
private _patchRouter(original: Function) {
return function(this: any, ...args: any[]) {
// Add router creation tracing
return original.apply(this, args);
};
}
}Module definitions should handle version mismatches and missing modules gracefully.
Usage Example:
class RobustInstrumentation extends InstrumentationBase {
protected init() {
return new InstrumentationNodeModuleDefinition(
"optional-module",
[">=1.0.0 <3.0.0"],
(moduleExports, moduleVersion) => {
try {
// Check if the expected API exists
if (typeof moduleExports.methodToWrap !== 'function') {
this._diag.warn('Expected method not found, skipping instrumentation');
return moduleExports;
}
shimmer.wrap(moduleExports, 'methodToWrap', this._wrapMethod.bind(this));
return moduleExports;
} catch (error) {
this._diag.error('Failed to patch module', error);
return moduleExports;
}
},
(moduleExports) => {
try {
if (moduleExports && typeof moduleExports.methodToWrap === 'function') {
shimmer.unwrap(moduleExports, 'methodToWrap');
}
} catch (error) {
this._diag.error('Failed to unpatch module', error);
}
}
);
}
}Install with Tessl CLI
npx tessl i tessl/npm-opentelemetry--instrumentation