npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

autotel-plugins

v0.6.6

Published

OpenTelemetry instrumentation for libraries without official support (Drizzle ORM)

Readme

Autotel Plugins

OpenTelemetry instrumentation for libraries without official support OR where the official support is fundamentally broken.

Philosophy

autotel-plugins only includes instrumentation that:

  1. Has NO official OpenTelemetry package (e.g., Drizzle ORM)
  2. Has BROKEN official instrumentation (e.g., Mongoose in ESM+tsx)
  3. Adds significant value beyond official packages

We do NOT include:

  • Re-exports of official packages
  • Wrappers that add no value
  • Duplicates of working official packages

Why This Approach?

With the --import pattern (Node.js 18.19+), using official OpenTelemetry packages when they work is simple:

// instrumentation.mjs
import { init } from 'autotel';
import { PgInstrumentation } from '@opentelemetry/instrumentation-pg';

init({
  service: 'my-app',
  instrumentations: [new PgInstrumentation()],
});
# Run with --import flag
tsx --import ./instrumentation.mjs src/index.ts

Benefits of official packages (when they work):

  • ✅ Always up-to-date (maintained by OpenTelemetry)
  • ✅ Complete feature coverage
  • ✅ Battle-tested in production
  • ✅ Zero maintenance burden
  • ✅ More discoverable and trustworthy

When to Use Official Packages

For databases/ORMs with working official instrumentation, use those directly:

Browse all official instrumentations →

⚠️ Mongoose ESM Exception

Note: @opentelemetry/instrumentation-mongoose is fundamentally broken in ESM+tsx environments due to module loading hook issues. It works in CommonJS, but if you're using ESM with tsx/ts-node, use our custom plugin below.

Installation

npm install autotel-plugins

Currently Supported

Mongoose

Instrument Mongoose database operations with OpenTelemetry tracing using runtime patching. Works in ESM+tsx unlike the official package. ✨ NEW: Automatic hook instrumentation - no manual trace() calls needed!

Why we provide this:

The official @opentelemetry/instrumentation-mongoose package is fundamentally broken in ESM+tsx environments:

  • Uses module loading hooks (import-in-the-middle) that fail with ESM import hoisting
  • Mongoose package lacks proper dual-mode exports (CJS only)
  • Works in CommonJS, but fails in modern ESM projects
  • No timeline for ESM support

Our implementation uses runtime patching instead of module loading hooks, so it works everywhere.

Basic Usage (with Automatic Hook Tracing)

import mongoose from 'mongoose';
import { init } from 'autotel';
import { instrumentMongoose } from 'autotel-plugins/mongoose';

// Initialize Autotel
init({ service: 'my-app' });

// IMPORTANT: Instrument BEFORE defining schemas to enable automatic hook tracing
instrumentMongoose(mongoose, {
  dbName: 'myapp',
  peerName: 'localhost',
  peerPort: 27017,
});

// NOW define schemas - hooks are automatically traced!
const userSchema = new mongoose.Schema({ name: String, email: String });

userSchema.pre('save', async function () {
  // ✨ This hook is AUTOMATICALLY traced - no manual trace() needed!
  this.email = this.email.toLowerCase();
});

const User = mongoose.model('User', userSchema);

// Connect to MongoDB
await mongoose.connect('mongodb://localhost:27017/myapp');

// All operations AND hooks are automatically traced
await User.create({ name: 'Alice', email: '[email protected]' });
// Creates spans: mongoose.users.create + mongoose.users.pre.save

What Gets Automatically Traced

1. Model Operations (all automatic):

  • create, insertMany, find, findOne, findById
  • findOneAndUpdate, findByIdAndUpdate, updateOne, updateMany
  • deleteOne, deleteMany, countDocuments, aggregate
  • Instance methods: save, remove, deleteOne

2. Schema Hooks (automatic - no manual code needed!):

  • Pre hooks: pre('save'), pre('findOneAndUpdate'), etc.
  • Post hooks: post('save'), post('remove'), etc.
  • Built-in hooks: post('init') (document hydration)

Hook Instrumentation Setup

For automatic hook tracing, call instrumentMongoose() before defining schemas. ESM import hoisting means you need a separate init file:

Pattern for ESM+tsx projects:

Create init-mongoose.ts:

import mongoose from 'mongoose';
import { instrumentMongoose } from 'autotel-plugins/mongoose';

instrumentMongoose(mongoose, { dbName: 'myapp' });

Import before schemas in index.ts:

import './init-mongoose'; // Import first!
import { User, Post } from './schema'; // Hooks auto-instrumented

Configuration

{
  dbName?: string                  // Database name
  captureCollectionName?: boolean  // Include collection in spans (default: true)
  peerName?: string                // MongoDB host
  peerPort?: number                // MongoDB port (default: 27017)
  tracerName?: string              // Custom tracer name
}

Span Attributes

Operation Spans (SpanKind.CLIENT):

  • db.system - "mongoose"
  • db.operation - create, find, update, etc.
  • db.mongodb.collection - Collection name
  • db.name - Database name
  • net.peer.name / net.peer.port - MongoDB server

Hook Spans (SpanKind.INTERNAL):

  • hook.type - "pre" or "post"
  • hook.operation - save, findOneAndUpdate, etc.
  • hook.model - Model name (User, Post, etc.)
  • db.mongodb.collection - Collection name
  • db.system - "mongoose"
  • db.name - Database name

Before vs After (70% Less Code!)

Before (Manual instrumentation):

import { trace } from 'autotel';

userSchema.pre('save', async function () {
  await trace((ctx) => async () => {
    ctx.setAttribute('hook.type', 'pre');
    ctx.setAttribute('hook.operation', 'save');
    // ... lots of boilerplate
    this.email = this.email.toLowerCase();
  })();
});

After (Automatic instrumentation):

// NO trace() imports needed!
userSchema.pre('save', async function () {
  // Automatically traced with all attributes!
  this.email = this.email.toLowerCase();
});

Drizzle ORM

Instrument Drizzle database operations with OpenTelemetry tracing. Drizzle doesn't have official instrumentation, so we provide it here.

import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import { instrumentDrizzleClient } from 'autotel-plugins/drizzle';

const queryClient = postgres(process.env.DATABASE_URL!);
const db = drizzle({ client: queryClient });

// Instrument the database instance
instrumentDrizzleClient(db, {
  dbSystem: 'postgresql',
  dbName: 'myapp',
  peerName: 'db.example.com',
  peerPort: 5432,
  captureQueryText: true,
});

// All queries are now traced
await db.select().from(users).where(eq(users.id, 123));

Supported databases:

  • PostgreSQL (node-postgres, postgres.js)
  • MySQL (mysql2)
  • SQLite (better-sqlite3, LibSQL/Turso)

Functions:

  • instrumentDrizzle(client, config) - Instrument a database client/pool
  • instrumentDrizzleClient(db, config) - Instrument a Drizzle database instance

Configuration:

{
  dbSystem?: string           // Database type (postgresql, mysql, sqlite)
  dbName?: string            // Database name
  captureQueryText?: boolean // Capture SQL in spans (default: true)
  maxQueryTextLength?: number // Max SQL length (default: 1000)
  peerName?: string          // Database host
  peerPort?: number          // Database port
}

Span Attributes:

  • db.system - Database type (postgresql, mysql, sqlite)
  • db.operation - Operation name (SELECT, INSERT, UPDATE, DELETE)
  • db.name - Database name
  • db.statement - SQL query text (if captureQueryText: true)
  • net.peer.name - Database host
  • net.peer.port - Database port

Usage with Autotel

Drizzle instrumentation works seamlessly with Autotel:

import { init } from 'autotel';
import { instrumentDrizzleClient } from 'autotel-plugins/drizzle';
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';

// Initialize Autotel
init({
  service: 'my-service',
  endpoint: 'http://localhost:4318',
});

// Instrument your database
const client = postgres(process.env.DATABASE_URL!);
const db = drizzle({ client });
instrumentDrizzleClient(db, { dbSystem: 'postgresql' });

// Traces will be sent to your OTLP endpoint
await db.select().from(users);

Combining with Official Packages

Mix autotel-plugins with official OpenTelemetry instrumentations:

import { init } from 'autotel';
import { instrumentDrizzleClient } from 'autotel-plugins/drizzle';
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express';
import { PgInstrumentation } from '@opentelemetry/instrumentation-pg';

init({
  service: 'my-service',
  instrumentations: [
    new HttpInstrumentation(),
    new ExpressInstrumentation(),
    new PgInstrumentation(), // Official packages
  ],
});

// Drizzle (no official package available)
const db = drizzle({ client: postgres(process.env.DATABASE_URL!) });
instrumentDrizzleClient(db, { dbSystem: 'postgresql' });

Security Considerations

Query Text Capture

By default, Drizzle instrumentation captures SQL text which may contain sensitive data:

// Disable SQL capture to prevent PII leakage
instrumentDrizzleClient(db, {
  captureQueryText: false,
});

Examples

See the example-drizzle directory for a complete working example.

TypeScript

Full type safety with TypeScript:

import type { InstrumentDrizzleConfig } from 'autotel-plugins';

const config: InstrumentDrizzleConfig = {
  dbSystem: 'postgresql',
  captureQueryText: true,
  maxQueryTextLength: 1000,
};

Future

When official OpenTelemetry instrumentation becomes available for Drizzle ORM, we will announce deprecation and provide a migration guide.

Creating Your Own Instrumentation

Don't see your library here? Autotel makes it easy to create custom instrumentation for any library using simple, well-tested utilities.

Quick Example

import { trace, SpanKind } from '@opentelemetry/api';
import { runWithSpan, finalizeSpan } from 'autotel/trace-helpers';

const INSTRUMENTED_FLAG = Symbol('instrumented');

export function instrumentMyLibrary(client) {
  if (client[INSTRUMENTED_FLAG]) return client;

  const tracer = trace.getTracer('my-library');
  const originalMethod = client.someMethod.bind(client);

  client.someMethod = async function (...args) {
    const span = tracer.startSpan('operation', { kind: SpanKind.CLIENT });
    span.setAttribute('operation.param', args[0]);

    try {
      const result = await runWithSpan(span, () => originalMethod(...args));
      finalizeSpan(span);
      return result;
    } catch (error) {
      finalizeSpan(span, error);
      throw error;
    }
  };

  client[INSTRUMENTED_FLAG] = true;
  return client;
}

Full Guide

For a comprehensive guide including:

  • Step-by-step tutorial with real examples
  • Best practices for security and idempotency
  • Complete utilities reference
  • Ready-to-use template code

See: Creating Custom Instrumentation in the main autotel docs.

You can also check INSTRUMENTATION_TEMPLATE.ts for a fully commented, copy-paste-ready template.

Contributing

Found a database/ORM without official OpenTelemetry instrumentation? Please open an issue to discuss adding it.

License

MIT