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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@8monkey/opentelemetry-instrumentation-bun-sql

v0.2.0

Published

OpenTelemetry instrumentation for `Bun.SQL` database client for PostgreSQL / MySQL / SQLite

Downloads

657

Readme

OpenTelemetry Bun.SQL Instrumentation

OpenTelemetry instrumentation for Bun.SQL, the built-in database client in Bun supporting PostgreSQL, MySQL, and SQLite.

Installation

bun add @8monkey/opentelemetry-instrumentation-bun-sql

Supported Versions

  • Bun >= 1.2

Usage

import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
import { SimpleSpanProcessor, ConsoleSpanExporter } from "@opentelemetry/sdk-trace-base";
import { registerInstrumentations } from "@opentelemetry/instrumentation";
import { BunSqlInstrumentation } from "@8monkey/opentelemetry-instrumentation-bun-sql";

// 1. Register instrumentation before creating any SQL instances.
//    Bun built-ins bypass Node.js module hooks, so the instrumentation patches
//    require("bun").SQL at enable time — instances created before this are not traced.
const provider = new NodeTracerProvider({
  spanProcessors: [new SimpleSpanProcessor(new ConsoleSpanExporter())],
});
provider.register();

registerInstrumentations({
  instrumentations: [new BunSqlInstrumentation()],
});

// 2. Create instances via require("bun"), not `import { SQL } from "bun"`.
//    Static import bindings are resolved at module load time (before step 1 runs)
//    and therefore always capture the original, unpatched constructor.
const sql = new (require("bun") as typeof Bun).SQL({ adapter: "sqlite" });

await sql`SELECT 1`;
await sql.close();

Semantic Conventions

This instrumentation follows the OpenTelemetry Database Semantic Conventions.

Attributes

| Attribute | Description | | --------------------------- | ------------------------------------------------------------------------------------- | | db.system.name | Database system: postgresql, mysql, or sqlite | | db.operation.name | SQL operation: SELECT, INSERT, UPDATE, DELETE, etc. | | db.query.text | The SQL query text (parameterized for tagged templates, sanitized for unsafe queries) | | db.namespace | Database name or SQLite filename | | db.response.returned_rows | Number of rows returned | | server.address | Server hostname (PostgreSQL/MySQL) | | server.port | Server port (PostgreSQL/MySQL) | | error.type | Error class name (e.g., SQLiteError, PostgresError) | | db.response.status_code | Database-specific error code |

Span names

Span names follow the OTel convention priority:

  1. {db.operation.name} {db.namespace} (e.g., SELECT mydb)
  2. {db.operation.name} (e.g., SELECT)
  3. {db.namespace} (e.g., mydb)
  4. {db.system.name} (e.g., postgresql)

Metrics

This instrumentation emits the following metric per the OTel DB metrics semantic conventions:

db.client.operation.duration

| Property | Value | | ----------- | -------------------------------------------------- | | Type | Histogram | | Unit | s (seconds) | | Description | Duration of database client operations | | Buckets | 0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10 |

Metric attributes:

| Attribute | Requirement | Description | | ------------------------- | --------------------- | ------------------------------------------- | | db.system.name | Required | Database system identifier | | db.operation.name | Conditionally Required | Database operation (e.g., SELECT) | | db.namespace | Conditionally Required | Database name or SQLite filename | | error.type | Conditionally Required | Error class name (only on failure) | | db.response.status_code | Conditionally Required | Database error code (only on failure) | | server.address | Recommended | Server hostname | | server.port | Conditionally Required | Server port (when non-default) |

db.query.text is intentionally excluded from metric attributes to avoid high-cardinality issues and PII exposure.

Configuration

| Option | Type | Default | Description | | --------------------------- | --------------------------- | --------------- | -------------------------------------------------------------------------------------------------- | | requireParentSpan | boolean | false | Only create spans when a parent span exists in context | | enhancedDatabaseReporting | boolean | false | Include query parameters (db.query.parameter.<n>) and result data in spans | | ignoreConnectionSpans | boolean | false | Suppress spans for CLOSE and RESERVE operations | | maskStatement | boolean | true | Replace integer literals and quoted strings with ? in non-parameterized queries (sql.unsafe()) | | maskStatementHook | (query: string) => string | Built-in masker | Custom masking function for non-parameterized queries | | addSqlCommenterComment | boolean | false | Append SQL commenter traceparent comments to queries | | requestHook | (span, info) => void | - | Called before query execution to customize span attributes | | responseHook | (span, info) => void | - | Called after query execution with response metadata |

Example with hooks

import type {
  BunSqlRequestHookInformation,
  BunSqlResponseHookInformation,
} from "@8monkey/opentelemetry-instrumentation-bun-sql";

new BunSqlInstrumentation({
  requestHook: (span, info: BunSqlRequestHookInformation) => {
    span.setAttribute("custom.query", info.query);
  },
  responseHook: (span, info: BunSqlResponseHookInformation) => {
    if (info.rowCount !== undefined) {
      span.setAttribute("custom.row_count", info.rowCount);
    }
  },
});

What gets instrumented

  • Tagged template queries: sql`SELECT * FROM users WHERE id = ${id}`
  • Unsafe queries: sql.unsafe("SELECT * FROM users")
  • Queries inside transactions: queries run inside sql.begin(tx => ...), tx.savepoint(sp => ...), etc. are individually traced; no span is emitted for the transaction boundary itself
  • Connection management: sql.close(), sql.reserve()
  • Chaining methods: .values(), .raw(), .simple(), .execute()

Query text handling

| Query type | db.query.text behavior | | ------------------------------------------ | ---------------------------------------------------------- | | Tagged template | Parameterized: SELECT * FROM users WHERE id = $1 | | sql.unsafe() | Sanitized by default: SELECT * FROM users WHERE name = ? | | sql.unsafe() with maskStatement: false | Raw text preserved |