An automatic instrumentation tool, complimentary to OpenTelemetry to enable tracing in your business logic without messing up your codebase.
A TypeScript transformer that automatically instruments your business logic (class-level function calls) with OpenTelemetry spans at compile time through AST weaving, achieving true "application-level transparency" as described in Google's Dapper paper.
- Zero-touch: No code changes required in your business logic
 - Deep Tracing: Automatically traces all function calls, including private methods
 - Minimal Runtime Overhead: Instead of runtime monkey-patch → compile-time patching
 
Design Philosophy This transformer uses a strategically designed AST visitor that specifically targets class-level method declarations to achieve optimal performance and precision in business logic tracing.
Principles:
- Precision over Volume: Focuses on business logic methods in classes where most application logic resides
 - Performance Optimization: Avoids instrumenting every methods to prevent span explosion and performance degradation
 - Strategic Targeting: Selectively instruments 
ts.isMethodDeclarationnodes which typically contain core business operations - Noise Reduction: Excludes utility functions, helpers, and nested functions that would create excessive telemetry noise
 
This intentional scope limitation ensures you get high-value tracing data without overwhelming your observability infrastructure or impacting application performance.
OpenTelemetry Compatible: This package follows OpenTelemetry Semantic Conventions and integrates seamlessly with the OpenTelemetry ecosystem.
# Install as development dependency (compile-time only)
npm install @waitingliou/tsc-otel-instrumentation --save-devImportant
This package is a compile-time TypeScript transformer that modifies your code during the build process. The generated JavaScript code has no runtime dependency on this package - only on @opentelemetry/api.
Add the transformer to your project's tsconfig.json:
{
  "compilerOptions": {
    "plugins": [
      {
        "transform": "@waitingliou/tsc-otel-instrumentation/transformer",
        "include": [
          "**/*Service.ts",
          "**/*Repository.ts"
        ],
        "exclude": [
          "**/*.test.ts",
          "**/*.spec.ts"
        ],
        "instrumentPrivateMethods": true,
        "spanNamePrefix": "myapp",
        "autoInjectTracer": true
      }
    ]
  }
}ts-patch patches your local TypeScript installation so the official tsc can load custom transformers from compilerOptions.plugins.
# Install ts-patch as development dependency  
npm install ts-patch --save-dev
npx ts-patch install -sI recommend adding a postinstall step to your npm scripts to ensure teammates automatically set up ts-patch after installing dependencies:
"scripts": {
    "build": "tsc",
    "dev": "tsc --watch",
    "test": "jest",
    "postinstall": "ts-patch install -s"
},Install OpenTelemetry API as a production dependency (required at runtime) in your project:
# Runtime dependency for generated instrumentation code
npm install @opentelemetry/api{
  "compilerOptions": {
    "plugins": [
      {
        "transform": "@waitingliou/tsc-otel-instrumentation/transformer",
        "include": [
          "**/*Service.ts",
          "**/*Repository.ts",
          "src/business/**/*.ts"
        ],
        "exclude": [
          "**/*.test.ts",
          "**/*.spec.ts",
          "**/node_modules/**"
        ],
        "instrumentPrivateMethods": true,
        "spanNamePrefix": "myapp",
        "autoInjectTracer": true,
        "commonAttributes": {
          "service.name": "user-management-service",
          "service.version": "1.2.0",
          "deployment.environment": "production"
        },
        "includeMethods": ["create*", "updateUser", "get?ser"],
        "excludeMethods": ["toString", "test*", "deprecated"],
        "debug": false,
        "logLevel": "warn",
        "maxMethodsPerFile": 50
      }
    ]
  }
}| Option | Type | Required | Default | Description | 
|---|---|---|---|---|
include | 
string[] | 
✅ | - | Glob patterns for files to instrument. Supports standard glob syntax: **/*Service.ts, src/business/**/*.ts | 
exclude | 
string[] | 
❌ | ["**/*.test.ts", "**/*.spec.ts", "**/node_modules/**"] | 
Glob patterns for files to skip. Takes priority over include | 
instrumentPrivateMethods | 
boolean | 
❌ | false | 
Include methods starting with _ (underscore). Example: _privateMethod | 
spanNamePrefix | 
string | 
❌ | "ts-otel-weaver" | 
Prefix for all span names. Final span: {prefix}.{ClassName}.{methodName} | 
autoInjectTracer | 
boolean | 
❌ | true | 
Automatically add import { trace } from "@opentelemetry/api" to instrumented files | 
commonAttributes | 
Record<string, string> | 
❌ | {} | 
Key-value pairs added to ALL spans. Use for service metadata, environment info, etc. | 
includeMethods | 
string[] | 
❌ | [] | 
Method name patterns to instrument. Supports exact names and glob patterns (*, ?). Highest priority - only these methods will be instrumented if specified | 
excludeMethods | 
string[] | 
❌ | ["constructor", "toString", "valueOf", "toJSON", "inspect"] | 
Method name patterns to skip. Supports exact names and glob patterns (*, ?). Ignored if includeMethods is set | 
debug | 
boolean | 
❌ | false | 
Enable detailed transformation logs during compilation | 
logLevel | 
'none' | 'error' | 'warn' | 'info' | 'debug' | 
❌ | 'warn' | 
Control console output verbosity | 
maxMethodsPerFile | 
number | 
❌ | 100 | 
Safety limit to prevent accidentally instrumenting too many methods | 
This transformer automatically detects and properly instruments various TypeScript method types based on the design principles outlined in the Specification section.
| Function Type | Status | Scope | Sync | Async | Generator | Example | 
|---|---|---|---|---|---|---|
| Class Methods | ✅ Supported | Class | ✅ | ✅ | ✅ | getUser(id: string): User | 
| Private Methods | ✅ Supported | Class | ✅ | ✅ | ✅ | private _validateUser(user: User) | 
| Static Methods | ✅ Supported | Class | ✅ | ✅ | ✅ | static getInstance(): UserService | 
| Async Methods | ✅ Supported | Class | ❌ | ✅ | ✅ | async createUser(data: UserData) | 
| Generator Methods | ✅ Supported | Class | ✅ | ✅ | ✅ | *generateSequence(count: number) | 
| Async Generators | ✅ Supported | Class | ❌ | ✅ | ✅ | async *processUsers() | 
| Arrow Function Properties | 🎯 Excluded by Design | Class | ❌ | ❌ | ❌ | getUser = (id: string) => {} | 
| Function Expressions | 🎯 Excluded by Design | Class | ❌ | ❌ | ❌ | getUser: () => User = function() {} | 
| Standalone Functions | 🎯 Excluded by Design | Global | ❌ | ❌ | ❌ | function getUserById(id: string) | 
| Arrow Functions | 🎯 Excluded by Design | Global | ❌ | ❌ | ❌ | const processUser = (user) => {} | 
| Nested Functions | 🎯 Excluded by Design | Function | ❌ | ❌ | ❌ | Functions inside other functions | 
| Object Methods | 🎯 Excluded by Design | Object | ❌ | ❌ | ❌ | const obj = { method() {} } | 
When you call a method, it automatically generates a tracing structure like:
myapp.UserService.getUser
├──  myapp.UserService.createUser
│   ├── myapp.CacheService.get
│   ├── myapp.UserService._validateUserData (private method)
│   ├── myapp.UserRepository.save
│   │   ├── myapp.UserRepository._validateUser
│   │   └── myapp.UserRepository._persistUser  
├── myapp.UserService._processUserData
│   ├── myapp.ValidationService.validate
│   └── myapp.TransformService.transform
└── myapp.NotificationService.sendWelcome
| Name | Description | 
|---|---|
| Node.js & Express.js | A comprehensive example showcasing how to integrate Express.js + Nodejs runtime projects with @waitingliou/tsc-otel-instrumentation, and demo a real-world tracing sceanario with Grafana Cloud. | 
| Bun & Hono.js | A comprehensive example showcasing how to integrate Hono.js + Bun runtime projects with @waitingliou/tsc-otel-instrumentation, and demo a real-world tracing sceanario with Grafana Cloud. | 
Compile and check:
npm run build
# check import
head -5 dist/your-service.js
# should see:
# import { trace, SpanStatusCode, SpanKind } from "@opentelemetry/api";
# const tracer = trace.getTracer("@waitingliou/tsc-otel-instrumentation");As a compile-time transformer, this package adds instrumentation during the TypeScript compilation phase. Below is a performance benchmark measured on a production-scale codebase to help you understand the expected overhead.
Test Subject:
- Project: Core package of app.pathors.com
 - Nature: Production-grade enterprise application
 - Purpose: Real-world performance evaluation on actual production codebase
 
Codebase Scale:
- Files: 1,652
 - Lines of Library: 10,247
 - Lines of Definitions: 274,130
 - Lines of TypeScript: 9,971
 
Test Environment:
- OS: macOS 14.2 (Darwin 23.2.0)
 - CPU: Apple Silicon (M-3)
 - Memory: 16GB
 - Node.js: v20.x
 - TypeScript: v5.x
 - ts-patch: v3.x
 
Measurement Method:
- Tool: 
/usr/bin/time -l(memory & time) +tsc --extendedDiagnostics(compilation phases) - Runs: 10 iterations, averaged for statistical reliability
 - Variants: Without transformer (Baseline) vs. With transformer (Instrumented)
 
Results (mean of 10 runs)
| Metric | Baseline | With Instrumentation | Overhead | 
|---|---|---|---|
| Real Time | 3.783s | 4.449s | +17.6% | 
| Total Compile Time | 3.173s | 3.571s | +12.6% | 
| Max RSS | 469.22 MB | 488.35 MB | +4.1% | 
| TSC Memory | 351.22 MB | 387.31 MB | +10.3% | 
| Parse Time | 1.342s | 1.455s | +8.4% | 
| Bind Time | 0.416s | 0.410s | −1.4% | 
| Check Time | 1.080s | 1.207s | +11.8% | 
| Emit Time | 0.337s | 0.499s | +48.1% | 
Key Observations:
- Overall Impact: +17.6% increase in real compile time, +12.6% in total compilation time
 - Memory Overhead: Modest increase of ~4-10% in memory consumption
 - Emit Phase: The most significant impact (+48.1%) occurs during code emission, which is expected as the transformer injects tracing code into method bodies
 - Type Checking: Minor impact (+11.8%) on type checking phase
 - Parse/Bind: Minimal impact on parsing (+8.4%) and binding phases
 
Trade-off Note: This is a one-time compile-time cost that enables zero-runtime overhead tracing. Unlike runtime instrumentation libraries, there's no performance penalty during application execution.
- Node.js >= 18.0.0
 - TypeScript >= 4.5.0
 - ts-patch for transformer integration
 
- @opentelemetry/api >= 1.9.0 (for generated instrumentation code)
 
Apache-2.0 License - see LICENSE file for details.
- Inspired by runtime monkey-patching of OpenTelemetry Instrumentation libraries
 - Thanks to @Pathors. As a intern in @Pathors, I was inspired with the idea of developing this tool during the process of integrating OTel.
 - Leverages TypeScript Compiler API for AST transformations
 

