Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BullMQ plugin support #3577

Closed
jdjkelly opened this issue Aug 24, 2023 · 2 comments
Closed

BullMQ plugin support #3577

jdjkelly opened this issue Aug 24, 2023 · 2 comments

Comments

@jdjkelly
Copy link

jdjkelly commented Aug 24, 2023

Similar but not a dupe of #1704, BullMQ is a popular NodeJS messaging queue with ~500,000 weekly downloads on NPM. It would be great to have support for this library.

For anyone looking for support, here's a simple approach that involves patching processJob on the Worker class and adding some custom tags:

import { Worker } from 'bullmq';

const originalProcessJob = Worker.prototype.processJob;
Worker.prototype.processJob = async function (job, ...args) {
  return await tracer.trace(`queue.${job.queueName}`, async (span) => {
    const { name, data, opts, id } = job;
    span?.addTags({ job: { name, id, ...data, ...opts } });

    return originalProcessJob.apply(this, [job, ...args]);
  });
};
@theodormarcu
Copy link

theodormarcu commented Oct 24, 2023

Is there really no better way than this? We resorted to writing this tracer wrapper to get recursive flame charts in datadog.

We have to wrap every function call in it, which is not fun and feels insane from a developer experience perspective.

cc @suhjohn, who's the original author of the script

// tracer.ts
import tracer, { Span } from 'dd-trace';
import path from 'path';

tracer.init({
  logInjection: true, // correlates traces with logs
}); // initialized in a different file to avoid hoisting.

/**
 * trace
 *
 * A wrapper around dd-trace's tracer.trace function that automatically
 * creates a span with the name of the function that called it.
 *
 * For any function, wrap it in a trace call to automatically create a span.
 * Example: 
   export const removeKey = async (lockKey: string) => {
    const result: number = await client.del(lockKey);
    return result;
  };

  becomes: 
  
  export const removeKey = async (lockKey: string) => {
  await trace(async () => {
    const result: number = await client.del(lockKey);
    return result;
  });
};
 */
export function trace<T>(
  cb: (span?: Span | undefined) => Promise<T>,
): Promise<T> {
  // Logic to compute the span name
  const { stack } = new Error();
  const lines = stack !== undefined ? stack.split('\n') : [];
  const callerLine = lines[2] || ''; // The caller of the caller (the outer function)

  const matchFilePath = callerLine.match(/\(([^:]+):\d+:\d+\)$/);
  const callerFilename = matchFilePath ? matchFilePath[1] : '';
  const matchFunctionName = callerLine.match(/at (\S+)(?: \()/);

  const dirParts = path.dirname(callerFilename).split(path.sep);
  const srcIndex = dirParts.indexOf('build');
  const dirsAfterSrc =
    srcIndex !== -1 ? dirParts.slice(srcIndex + 1) : dirParts;
  const dirNameStr = `${dirsAfterSrc.join('.')}.`;

  let baseName = path.basename(callerFilename, '.ts');
  baseName = baseName.replace(/\.js$/, '');
  const functionName = matchFunctionName ? `.${matchFunctionName[1]}` : '';

  const spanName = `${dirNameStr}${baseName}${functionName}`;
  // Tracing logic
  return new Promise((resolve, reject) => {
    void tracer.trace(spanName, async (span) => {
      try {
        const result = await cb(span);
        resolve(result);
      } catch (error) {
        span?.setTag('error', error);
        reject(error);
      } finally {
        span?.finish();
      }
    });
  });
}

export default tracer;

@tlhunter
Copy link
Member

I'm going to close this as a dupe of #1704 for now. I added a link to this issue from the other issue so folks can see your workaround.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants