mssql
Version:
Microsoft SQL Server client for Node.js.
154 lines (142 loc) • 5.64 kB
JavaScript
'use strict'
const dc = require('node:diagnostics_channel')
// TracingChannel names
const TRACE_QUERY = 'mssql:query'
const TRACE_BATCH = 'mssql:batch'
const TRACE_EXECUTE = 'mssql:execute'
const TRACE_BULK = 'mssql:bulk'
const TRACE_CONNECT = 'mssql:connect'
const TRACE_POOL_ACQUIRE = 'mssql:pool:acquire'
const TRACE_PREPARED_STATEMENT_PREPARE = 'mssql:prepared-statement:prepare'
const TRACE_PREPARED_STATEMENT_EXECUTE = 'mssql:prepared-statement:execute'
// Point-event channel names
const CONNECTION_ACQUIRE = 'mssql:connection:acquire'
const CONNECTION_RELEASE = 'mssql:connection:release'
const CONNECTION_CREATE = 'mssql:connection:create'
const CONNECTION_DESTROY = 'mssql:connection:destroy'
const POOL_CLOSE = 'mssql:pool:close'
const TRANSACTION_BEGIN = 'mssql:transaction:begin'
const TRANSACTION_COMMIT = 'mssql:transaction:commit'
const TRANSACTION_ROLLBACK = 'mssql:transaction:rollback'
const REQUEST_CANCEL = 'mssql:request:cancel'
const PREPARED_STATEMENT_UNPREPARE = 'mssql:prepared-statement:unprepare'
const CHANNELS = Object.freeze({
TRACE_QUERY,
TRACE_BATCH,
TRACE_EXECUTE,
TRACE_BULK,
TRACE_CONNECT,
TRACE_POOL_ACQUIRE,
TRACE_PREPARED_STATEMENT_PREPARE,
TRACE_PREPARED_STATEMENT_EXECUTE,
CONNECTION_ACQUIRE,
CONNECTION_RELEASE,
CONNECTION_CREATE,
CONNECTION_DESTROY,
POOL_CLOSE,
TRANSACTION_BEGIN,
TRANSACTION_COMMIT,
TRANSACTION_ROLLBACK,
REQUEST_CANCEL,
PREPARED_STATEMENT_UNPREPARE
})
// Pre-create TracingChannel instances at module load time
const tracingChannels = {
[TRACE_QUERY]: dc.tracingChannel(TRACE_QUERY),
[TRACE_BATCH]: dc.tracingChannel(TRACE_BATCH),
[TRACE_EXECUTE]: dc.tracingChannel(TRACE_EXECUTE),
[TRACE_BULK]: dc.tracingChannel(TRACE_BULK),
[TRACE_CONNECT]: dc.tracingChannel(TRACE_CONNECT),
[TRACE_POOL_ACQUIRE]: dc.tracingChannel(TRACE_POOL_ACQUIRE),
[TRACE_PREPARED_STATEMENT_PREPARE]: dc.tracingChannel(TRACE_PREPARED_STATEMENT_PREPARE),
[TRACE_PREPARED_STATEMENT_EXECUTE]: dc.tracingChannel(TRACE_PREPARED_STATEMENT_EXECUTE)
}
// Pre-create point-event channel instances at module load time
const pointChannels = {
[CONNECTION_ACQUIRE]: dc.channel(CONNECTION_ACQUIRE),
[CONNECTION_RELEASE]: dc.channel(CONNECTION_RELEASE),
[CONNECTION_CREATE]: dc.channel(CONNECTION_CREATE),
[CONNECTION_DESTROY]: dc.channel(CONNECTION_DESTROY),
[POOL_CLOSE]: dc.channel(POOL_CLOSE),
[TRANSACTION_BEGIN]: dc.channel(TRANSACTION_BEGIN),
[TRANSACTION_COMMIT]: dc.channel(TRANSACTION_COMMIT),
[TRANSACTION_ROLLBACK]: dc.channel(TRANSACTION_ROLLBACK),
[REQUEST_CANCEL]: dc.channel(REQUEST_CANCEL),
[PREPARED_STATEMENT_UNPREPARE]: dc.channel(PREPARED_STATEMENT_UNPREPARE)
}
// TracingChannel.hasSubscribers was added in Node 22. On 18.19 / 20 the
// aggregate property is undefined, so we fall back to checking the
// sub-channels directly (these have existed since the original
// diagnostics_channel API). Preserves the zero-cost fast path across all
// supported runtimes.
function tracingChannelHasSubscribers (tc) {
if (typeof tc.hasSubscribers === 'boolean') return tc.hasSubscribers
return tc.start.hasSubscribers ||
tc.end.hasSubscribers ||
tc.asyncStart.hasSubscribers ||
tc.asyncEnd.hasSubscribers ||
tc.error.hasSubscribers
}
/**
* Trace an async operation using a TracingChannel.
*
* When subscribers are active, wraps `fn` with TracingChannel.tracePromise().
* When no subscribers are active, calls `fn` directly with zero overhead
* (no context allocation).
*
* @param {string} name - TracingChannel name (one of CHANNELS.TRACE_*)
* @param {Function} fn - The function to trace (must return a Promise)
* @param {Function} contextFactory - Factory function returning the context object
* @returns {Promise} The return value of fn
*/
function tracePromise (name, fn, contextFactory) {
const channel = tracingChannels[name]
if (tracingChannelHasSubscribers(channel)) {
return channel.tracePromise(fn, contextFactory())
}
return fn()
}
/**
* Trace a callback-style async operation using a TracingChannel.
*
* When subscribers are active, delegates to TracingChannel.traceCallback,
* which replaces the callback at `position` in `args` with a wrapped
* version that publishes to start/end/asyncStart/asyncEnd/error. When
* no subscribers are active, calls `fn` directly with zero overhead.
*
* @param {string} name - TracingChannel name (one of CHANNELS.TRACE_*)
* @param {Function} fn - The function to call (receives callback at `position`)
* @param {number} position - Index of the callback within `args`
* @param {Function} contextFactory - Factory function returning the context object
* @param {*} thisArg - `this` binding for fn
* @param {Array} args - Arguments to pass to fn (includes the callback at `position`)
* @returns {*} The return value of fn
*/
function traceCallback (name, fn, position, contextFactory, thisArg, args) {
const channel = tracingChannels[name]
if (tracingChannelHasSubscribers(channel)) {
return channel.traceCallback(fn, position, contextFactory(), thisArg, ...args)
}
return fn.apply(thisArg, args)
}
/**
* Publish a point event on a named channel.
*
* Only allocates the message object when subscribers are active.
*
* @param {string} name - Point-event channel name (one of CHANNELS.*)
* @param {Function} factory - Factory function that returns the message object
*/
function publish (name, factory) {
const channel = pointChannels[name]
if (channel.hasSubscribers) {
channel.publish(factory())
}
}
module.exports = {
CHANNELS,
tracingChannels,
tracePromise,
traceCallback,
publish
}