feat: add TracingChannel support for native APM instrumentation

Merged
LO
7mo ago
+1134-19

Summary

  • Add first-class TracingChannel (node:diagnostics_channel) support so APM tools (OTEL, Datadog, Sentry) can instrument mysql2 without monkey-patching via IITM/RITM
  • Four tracing channels: mysql2:query, mysql2:execute, mysql2:connect, mysql2:pool:connect
  • Context fields map to OTEL semantic conventions (db.query.text, db.namespace, server.address, server.port)
  • Zero-cost when no subscribers — hasSubscribers checked before any context allocation
  • Graceful degradation on Node versions without TracingChannel (e.g. Node 18)

Motivation

Current APM instrumentations use IITM (import-in-the-middle) for ESM and RITM (require-in-the-middle) for CJS to monkey-patch Connection.prototype.query and Connection.prototype.execute. This has fragility concerns:

  • Runtime lock-in: RITM/IITM rely on Node.js-specific module loader internals — they don't work on Bun or Deno
  • ESM fragility: IITM is built on Node.js's evolving module customization hooks, a persistent source of breakage in the OTEL JS ecosystem
  • Initialization ordering: instrumentation must be set up before mysql2 is first imported — get the order wrong and instrumentation silently does nothing
  • Bundling: users must externalize instrumented modules, increasingly difficult with server-side bundling

With native TracingChannel support, instrumentation libraries become subscribers, not patches. Each tool listens independently with no ordering concerns, no clobbering, and no internal API dependency.

Channels

TracingChannelTracksContext fields
mysql2:queryconnection.query() executionquery, values, database, serverAddress, serverPort
mysql2:executeconnection.execute() prepared statementsquery, values, database, serverAddress, serverPort
mysql2:connectClient connection lifecycledatabase, serverAddress, serverPort, user
mysql2:pool:connectPool connection acquisitiondatabase, serverAddress, serverPort

Implementation

  • New lib/tracing.js + lib/tracing.d.ts — channel setup, context types, tracePromise wrappers
  • lib/base/connection.js — instrumented query(), execute(), and constructor (connect)
  • lib/base/pool.js — instrumented getConnection()
  • typings/mysql/index.d.ts — re-exports context types for APM consumers
  • test/integration/tracing-channel.test.mts — integration tests for all 4 channels

Uses tracePromise with promise wrapping (same pattern as node-redis, ioredis, pg implementations). Promise wrapper in lib/promise/ delegates to the base layer, so promise API gets tracing for free.

Prior art

Follows the same pattern adopted by:

closes #4174