UNPKG

dd-trace

Version:

Datadog APM tracing client for JavaScript

208 lines (176 loc) 5.86 kB
'use strict' const ProducerPlugin = require('../../dd-trace/src/plugins/producer') const { DsmPathwayCodec, getMessageSize } = require('../../dd-trace/src/datastreams') class BaseBullmqProducerPlugin extends ProducerPlugin { static id = 'bullmq' asyncEnd (ctx) { ctx.currentStore?.span?.finish() } start (ctx) { if (!this.config.dsmEnabled) return const { span } = ctx.currentStore this.setProducerCheckpoint(span, ctx) } bindStart (ctx) { const { resource, meta } = this.getSpanData(ctx) const span = this.startSpan({ resource, meta: { component: 'bullmq', 'span.kind': 'producer', 'messaging.system': 'bullmq', 'messaging.operation': 'publish', ...meta, }, }, ctx) this.injectTraceContext(span, ctx) return ctx.currentStore } getSpanData (ctx) { throw new Error('getSpanData must be implemented by subclass') } injectTraceContext (span, ctx) { throw new Error('injectTraceContext must be implemented by subclass') } _injectIntoOpts (span, opts) { const carrier = {} this.tracer.inject(span, 'text_map', carrier) const existing = opts.telemetry?.metadata ? JSON.parse(opts.telemetry.metadata) : {} existing._datadog = carrier opts.telemetry = { metadata: JSON.stringify(existing), omitContext: true } } setProducerCheckpoint (span, ctx) { const { queueName, payloadSize, optsTarget } = this.getDsmData(ctx) const edgeTags = ['direction:out', `topic:${queueName}`, 'type:bullmq'] const dataStreamsContext = this.tracer.setCheckpoint(edgeTags, span, payloadSize) if (optsTarget && typeof optsTarget === 'object') { const existing = optsTarget.telemetry?.metadata ? JSON.parse(optsTarget.telemetry.metadata) : {} DsmPathwayCodec.encode(dataStreamsContext, existing._datadog || existing) if (!existing._datadog) existing._datadog = {} optsTarget.telemetry = { metadata: JSON.stringify(existing), omitContext: true } } } getDsmData (ctx) { throw new Error('getDsmData must be implemented by subclass') } } class QueueAddPlugin extends BaseBullmqProducerPlugin { static prefix = 'tracing:orchestrion:bullmq:Queue_add' getSpanData (ctx) { const queueName = ctx.self?.name || 'bullmq' return { resource: queueName, meta: { 'messaging.destination.name': ctx.self?.name, }, } } #ensureOpts (ctx) { let opts = ctx.arguments?.[2] if (!opts || typeof opts !== 'object') { opts = {} if (ctx.arguments.length <= 2) { Array.prototype.push.call(ctx.arguments, opts) } else { ctx.arguments[2] = opts } } return opts } injectTraceContext (span, ctx) { const opts = this.#ensureOpts(ctx) this._injectIntoOpts(span, opts) } getDsmData (ctx) { const data = ctx.arguments?.[1] return { queueName: ctx.self?.name || 'bullmq', payloadSize: data ? getMessageSize(data) : 0, optsTarget: this.#ensureOpts(ctx), } } } class QueueAddBulkPlugin extends BaseBullmqProducerPlugin { static prefix = 'tracing:orchestrion:bullmq:Queue_addBulk' operationName () { return 'bullmq.addBulk' } getSpanData (ctx) { const queueName = ctx.self?.name || 'bullmq' const jobs = ctx.arguments?.[0] return { resource: queueName, meta: { 'messaging.destination.name': ctx.self?.name, 'messaging.batch.message_count': Array.isArray(jobs) ? jobs.length : undefined, }, } } injectTraceContext (span, ctx) { const jobs = ctx.arguments?.[0] if (!Array.isArray(jobs)) return for (const job of jobs) { if (!job) continue job.opts = job.opts || {} this._injectIntoOpts(span, job.opts) } } getDsmData (ctx) { const jobs = ctx.arguments?.[0] || [] const payloadSize = jobs.reduce((total, job) => { return total + (job?.data ? getMessageSize(job.data) : 0) }, 0) return { queueName: ctx.self?.name || 'bullmq', payloadSize, optsTarget: jobs[0]?.opts, } } setProducerCheckpoint (span, ctx) { const jobs = ctx.arguments?.[0] || [] const queueName = ctx.self?.name || 'bullmq' const edgeTags = ['direction:out', `topic:${queueName}`, 'type:bullmq'] for (const job of jobs) { if (!job?.data) continue const payloadSize = getMessageSize(job.data) const dataStreamsContext = this.tracer.setCheckpoint(edgeTags, span, payloadSize) job.opts = job.opts || {} const existing = job.opts.telemetry?.metadata ? JSON.parse(job.opts.telemetry.metadata) : {} DsmPathwayCodec.encode(dataStreamsContext, existing._datadog || existing) if (!existing._datadog) existing._datadog = {} job.opts.telemetry = { metadata: JSON.stringify(existing), omitContext: true } } } } class FlowProducerAddPlugin extends BaseBullmqProducerPlugin { static prefix = 'tracing:orchestrion:bullmq:FlowProducer_add' getSpanData (ctx) { const flow = ctx.arguments?.[0] const queueName = flow?.queueName || 'bullmq' return { resource: queueName, meta: { 'messaging.destination.name': flow?.queueName, }, } } injectTraceContext (span, ctx) { const flow = ctx.arguments?.[0] if (!flow) return flow.opts = flow.opts || {} this._injectIntoOpts(span, flow.opts) } getDsmData (ctx) { const flow = ctx.arguments?.[0] if (!flow) { return { queueName: 'bullmq', payloadSize: 0, optsTarget: undefined } } flow.opts = flow.opts || {} return { queueName: flow.queueName || 'bullmq', payloadSize: flow.data ? getMessageSize(flow.data) : 0, optsTarget: flow.opts, } } } module.exports = [QueueAddPlugin, QueueAddBulkPlugin, FlowProducerAddPlugin]