CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-opentelemetry--instrumentation

Base class for node which OpenTelemetry instrumentation modules extend

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

module-definitions.mddocs/

Module Definitions

The module definition system allows instrumentations to specify which modules and files to instrument, their supported versions, and how to patch/unpatch them.

Capabilities

InstrumentationNodeModuleDefinition

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;
  }
);

InstrumentationNodeModuleFile

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
);

Module Definition Interfaces

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;
}

Version Constraint Patterns

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;

Complex Module Definition

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);
    };
  }
}

Error Handling

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

docs

auto-registration.md

base-instrumentation.md

index.md

module-definitions.md

semconv-stability.md

utilities.md

tile.json