Skip to content

Instantly share code, notes, and snippets.

@Antoine-lb
Last active January 12, 2022 17:51
Show Gist options
  • Save Antoine-lb/4cdb6ecb1dc04fb5ddd1380fe33c467d to your computer and use it in GitHub Desktop.
Save Antoine-lb/4cdb6ecb1dc04fb5ddd1380fe33c467d to your computer and use it in GitHub Desktop.
Add Sentry tracing/Performance to Strapi

strapi-plugin-sentry does not include the tracing/performance functionality, but this can be added by adding a middelware with the code provided in the Strapi Koa documentation (https://docs.sentry.io/platforms/node/guides/koa/#monitor-performance).

Before beginning make sure that you installed strapi-plugin-sentry and that it works (i.e. it logs the errors inside of the Sentry dashboard).

Create a middelware, but don't call it sentry, because there may be a problem with the plugin. I called mine stracing.

Inside of ./middlewares/stracing/index.js:

const {
  extractTraceparentData,
  Span,
  stripUrlQueryAndFragment,
} = require("@sentry/tracing");
const domain = require("domain");

// not mandatory, but adding domains does help a lot with breadcrumbs
const requestHandler = (ctx, next) => {
  const sentryInstance = strapi.plugins.sentry.services.sentry.getInstance();
  return new Promise((resolve, _) => {
    const local = domain.create();
    local.add(ctx);
    local.on("error", (err) => {
      ctx.status = err.status || 500;
      ctx.body = err.message;
      ctx.app.emit("error", err, ctx);
    });
    local.run(async () => {
      sentryInstance.getCurrentHub().configureScope((scope) =>
        scope.addEventProcessor((event) =>
          sentryInstance.Handlers.parseRequest(event, ctx.request, {
            user: false,
          })
        )
      );
      await next();
      resolve();
    });
  });
};

// this tracing middleware creates a transaction per request
const tracingMiddleWare = async (ctx, next) => {
  console.log("tracingMiddleWare called");
  const sentryInstance = strapi.plugins.sentry.services.sentry.getInstance();
  const reqMethod = (ctx.method || "").toUpperCase();
  const reqUrl = ctx.url && stripUrlQueryAndFragment(ctx.url);

  // this can be used to see if the middleware is getting the right DSN
  // console.log(
  //   "sentryInstance.getCurrentHub()._stack[0].client",
  //   sentryInstance.getCurrentHub()._stack[0].client
  // );

  // connect to trace of upstream app
  let traceparentData;
  if (ctx.request.get("sentry-trace")) {
    traceparentData = extractTraceparentData(ctx.request.get("sentry-trace"));
  }

  const transaction = sentryInstance.startTransaction({
    name: `${reqMethod} ${reqUrl}`,
    op: "http.server",
    ...traceparentData,
  });

  ctx.__sentry_transaction = transaction;
  // We put the transaction on the scope so users can attach children to it
  sentryInstance.getCurrentHub().configureScope((scope) => {
    scope.setSpan(transaction);
  });

  ctx.res.on("finish", () => {
    // Push `transaction.finish` to the next event loop so open spans have a chance to finish before the transaction closes
    setImmediate(() => {
      // if using koa router, a nicer way to capture transaction using the matched route
      if (ctx._matchedRoute) {
        const mountPath = ctx.mountPath || "";
        transaction.setName(`${reqMethod} ${mountPath}${ctx._matchedRoute}`);
      }
      transaction.setHttpStatus(ctx.status);
      transaction.finish();
    });
  });

  await next();
};

module.exports = (strapi) => {
  return {
    initialize() {
      strapi.app.use(requestHandler);
      strapi.app.use(tracingMiddleWare);
    },
  };
};

This is basically the same as the documentation but using strapi.plugins.sentry.services.sentry.getInstance(); to get the Strapi instance.

Then in the configuration of the sentry plugin add init.tracesSampleRate (if not it will not record without giving an error) so the config should look something like this:

  sentry: {
    dsn: env("SENTRY_DSN"),
    init: {
      tracesSampleRate: 1.0,
    },
  },

And make sure that the middelware is active in the ./config/middleware.js (it should look something like this):

module.exports = ({ env }) => ({
  load: {
    before: [
      "stracing",
      ...
    ],
  },
  settings: {
    stracing: {
      enabled: true,
    },
  },
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment