DataDog Tracer on Meteor, has anyone done it before?

Hello I was wondering if anyone has done this to track their meteor apps https://docs.datadoghq.com/tracing/setup/nodejs/ I tried adding it following this guide but haven’t succed as Im not getting anything on the dd instance.

Thanks!

You could start the production build with -r argument, like:

“node -r trace.js bundle/main.js”

and in trace.js:

require(‘dd-trace’).init()

1 Like

Thanks, Im just lost on how to add this if Im building my app on aws pipelines, I have a staged step where I call meteor build build/ --allow-superuser --directory --architecture os.linux.x86_64 after that I build a docker image with this build docker build --build-arg NODE_VERSION=8.11.0 -t $REPOSITORY_URI:latest .
So on those steps where should I add this node -r trace.js bundle/main.js call?

Thanks again!

In the Dockerfile itself where COMMAND or ENTRYPOINT is defined. I don’t know if it’s exactly that line of code and I don’t know if it’s going to work, it’s just the concept using that command line option to preload datadog:

https://nodejs.org/api/cli.html#cli_r_require_module

So please post here once you were successful :wink:

1 Like

Great! thanks again for the help @kaufmae! and sure, as soon as I get it going will post back.

I embarked on this journey last week and have had partial success.

I created my own server-side instrumentation around meteor methods using the opentracing api and it works fine in isolation. Trace id correlation, though, does not, and I suspect that it has something to do with fibers because I seem to get trace ids from the dd-trace-patched mongodb driver mixed up with the trace ids from my manual instrumentation in my dd-trace-patched winston logging calls.

This is my call chain:

registered meteor method invocation
  tracer.startSpan
     error logging wrapper
         code that does actual work, including mongodb queries
     return to logging wrapper
  span.finish()

What I noticed was that the error logging wrapper which uses winston to log json to aws cloudwatch would get the wrong traceid when logging a caught and re-thrown error, in one instance one that belonged to a mongodb span instead of the span that was opened around it.

This made the datadog feature to see log entries for a specific trace not work

If I figure this out I will let you know.

Here’s my instrumentation code in case it helps:

export const outputWithTraceId = (span: opentracing.Span, message: string) => {
  return; // no-op for now
  const context = span.context();
  const traceId = context.toTraceId();
  process.stdout.write(`traceId: ${traceId} - ${message}\n`);
};

const handleError = (span: opentracing.Span, name: string, e: any) => {
  outputWithTraceId(span, `handling error for ${name}`);
  const errorMessage = getErrorMessage(e);
  span.log({
    event: "error",
    message: errorMessage,
    error: e
  }); // span.log does NOT work with datadog so this line does nothing
  span.setTag(opentracing.Tags.ERROR, true);
  span.setTag("errorMessage", errorMessage);
  span.finish();
};

const wrap = <T extends (...args: any[]) => any>(fn: T, name: string) =>
  function wrappedMethod(...args: Parameters<T>) {
    const tracer = opentracing.globalTracer();

    // https://docs.datadoghq.com/tracing/trace_search_and_analytics/?tab=nodejs
    const tags: {
      [key: string]: string | boolean;
    } = {
      [ResourceNameTag]: name,
      [AnalyticsTag]: true
    };
    if (this.userId) {
      tags["user.id"] = this.userId;
    }
    const userSpan = tracer.startSpan("meteorCall", {
      tags
    });

    outputWithTraceId(userSpan, `new span for ${name}`);

    try {
      const result: ReturnType<T> = fn.bind(this)(...args);
      userSpan.log({
        event: "request_end"
      });
      outputWithTraceId(userSpan, `success - finishing for ${name}`);
      userSpan.finish();

      return result;
    } catch (e) {
      handleError(userSpan, name, e);
      throw e;
    }
  };

function wrapAllExistingMethods() {
  const meteor = Meteor as any;
  const defaultServer = meteor.default_server;
  const handlers: { [k: string]: (...args: any[]) => any } =
    defaultServer.method_handlers;
  Object.keys(handlers).forEach(key => {
    const fn = handlers[key];
    const wrapped = wrap(fn, key);
    handlers[key] = wrapped;
  });
}

// First time in, wrap existing methods
wrapAllExistingMethods();

export const wrapMethodsForTracing = <
  T extends { [k in keyof T]: (...args: any[]) => any }
>(
  methods: T
): T => {
  const wrappedMethods = { ...methods };
  for (const name of Object.keys(methods)) {
    const method = methods[name as keyof T];
    wrappedMethods[name] = wrap(method, name);
  }
  return wrappedMethods;
};

1 Like

Did you have any luck with implementing this?

I’ve imported the tracer early on in server code and it catches certain services, but I believe it’s missing quite a bit. Wondering where you wound up on this one.

Hey @typ no luck, tried a lot of stuff and I just gave up, nothing really worked, sorry I don’t have a solution for you.