dd-trace
Version:
Datadog APM tracing client for JavaScript
147 lines (131 loc) • 4.94 kB
JavaScript
const {
channel,
addHook
} = require('./helpers/instrument')
const kebabCase = require('../../datadog-core/src/utils/src/kebabcase')
const shimmer = require('../../datadog-shimmer')
const { NODE_MAJOR, NODE_MINOR } = require('../../../version')
const MIN_VERSION = ((NODE_MAJOR > 22) || (NODE_MAJOR === 22 && NODE_MINOR >= 2)) ? '>=0.5.3' : '>=0.5.0'
const commandStartCh = channel('apm:amqplib:command:start')
const commandFinishCh = channel('apm:amqplib:command:finish')
const commandErrorCh = channel('apm:amqplib:command:error')
const consumeStartCh = channel('apm:amqplib:consume:start')
const consumeFinishCh = channel('apm:amqplib:consume:finish')
const publishStartCh = channel('apm:amqplib:publish:start')
const publishFinishCh = channel('apm:amqplib:publish:finish')
const publishErrorCh = channel('apm:amqplib:publish:error')
const methods = {}
addHook({ name: 'amqplib', file: 'lib/defs.js', versions: [MIN_VERSION] }, defs => {
for (const [key, value] of Object.entries(defs)) {
if (Number.isInteger(value) && isCamelCase(key)) {
methods[value] = kebabCase(key).replaceAll('-', '.')
}
}
return defs
})
addHook({ name: 'amqplib', file: 'lib/channel_model.js', versions: [MIN_VERSION] }, x => {
shimmer.wrap(x.Channel.prototype, 'get', getMessage => function (queue, options) {
return getMessage.apply(this, arguments).then(message => {
if (message === null) {
return message
}
const ctx = { method: 'basic.get', message, fields: message.fields, queue }
consumeStartCh.runStores(ctx, () => {
// finish right away
consumeFinishCh.publish(ctx)
})
return message
})
})
shimmer.wrap(x.Channel.prototype, 'consume', consume => function (queue, callback, options) {
if (!consumeStartCh.hasSubscribers) {
return consume.apply(this, arguments)
}
arguments[1] = (message, ...args) => {
if (message === null) {
return callback(message, ...args)
}
const ctx = { method: 'basic.deliver', message, fields: message.fields, queue }
return consumeStartCh.runStores(ctx, () => {
// finish right away
const result = callback(message, ...args)
consumeFinishCh.publish(ctx)
return result
})
}
return consume.apply(this, arguments)
})
return x
})
addHook({ name: 'amqplib', file: 'lib/callback_model.js', versions: [MIN_VERSION] }, channel => {
shimmer.wrap(channel.Channel.prototype, 'get', getMessage => function (queue, options, callback) {
if (!commandStartCh.hasSubscribers) {
return getMessage.apply(this, arguments)
}
arguments[2] = (error, message, ...args) => {
if (error !== null || message === null) {
return callback(error, message, ...args)
}
const ctx = { method: 'basic.get', message, fields: message.fields, queue }
return consumeStartCh.runStores(ctx, () => {
const result = callback(error, message, ...args)
consumeFinishCh.publish(ctx)
return result
})
}
return getMessage.apply(this, arguments)
})
shimmer.wrap(channel.Channel.prototype, 'consume', consume => function (queue, callback) {
if (!consumeStartCh.hasSubscribers) {
return consume.apply(this, arguments)
}
arguments[1] = (message, ...args) => {
if (message === null) {
return callback(message, ...args)
}
const ctx = { method: 'basic.deliver', message, fields: message.fields, queue }
return consumeStartCh.runStores(ctx, () => {
const result = callback(message, ...args)
consumeFinishCh.publish(ctx)
return result
})
}
return consume.apply(this, arguments)
})
return channel
})
addHook({ name: 'amqplib', file: 'lib/channel.js', versions: [MIN_VERSION] }, channel => {
shimmer.wrap(channel.Channel.prototype, 'sendImmediately', sendImmediately => function (method, fields) {
return instrument(
sendImmediately, this, arguments, methods[method], fields, null, commandStartCh, commandFinishCh, commandErrorCh
)
})
shimmer.wrap(channel.Channel.prototype, 'sendMessage', sendMessage => function (fields) {
return instrument(
sendMessage, this, arguments, 'basic.publish', fields, arguments[2],
publishStartCh, publishFinishCh, publishErrorCh
)
})
return channel
})
function instrument (send, channel, args, method, fields, message, startCh, finishCh, errorCh) {
if (!startCh.hasSubscribers || method === 'basic.get') {
return send.apply(channel, args)
}
const ctx = { channel, method, fields, message }
return startCh.runStores(ctx, () => {
try {
return send.apply(channel, args)
} catch (err) {
ctx.error = err
errorCh.publish(ctx)
throw err
} finally {
finishCh.publish(ctx)
}
})
}
function isCamelCase (str) {
return /([A-Z][a-z0-9]+)+/.test(str)
}