Koa

Add @sentry/node as a dependency:

Copied
$ npm install --save @sentry/node

Initialize the Sentry SDK and install the on error hook:

Copied
import Koa from "koa";
import * as Sentry from "@sentry/node";

// or using CommonJS
// const Koa = require('koa');
// const Sentry = require('@sentry/node');

const app = new Koa();

Sentry.init({ dsn: "https://examplePublicKey@o0.ingest.sentry.io/0" });

app.on("error", (err, ctx) => {
  Sentry.withScope(function(scope) {
    scope.addEventProcessor(function(event) {
      return Sentry.Handlers.parseRequest(event, ctx.request);
    });
    Sentry.captureException(err);
  });
});

app.listen(3000);

Monitor Performance

Copied
$ npm install --save @sentry/node @sentry/tracing

Creates and attaches a transaction to each context

Copied
const Sentry = require("@sentry/node");
const { Span } = require("@sentry/tracing");
const Koa = require("koa");
const app = new Koa();
const domain = require("domain");

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  tracesSampleRate: 1.0, // Be sure to lower this in production
});

// not mandatory, but adding domains does help a lot with breadcrumbs
const requestHandler = (ctx, next) => {
  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 () => {
      Sentry.getCurrentHub().configureScope(scope =>
        scope.addEventProcessor(event =>
          Sentry.Handlers.parseRequest(event, ctx.request, { user: false })
        )
      );
      await next();
      resolve();
    });
  });
};

// this tracing middleware creates a transaction per request
const tracingMiddleWare = async (ctx, next) => {
  // captures span of upstream app
  const sentryTraceId = ctx.request.get("sentry-trace");
  let traceId;
  let parentSpanId;
  let sampled;
  if (sentryTraceId) {
    const span = Span.fromTraceparent(sentryTraceId);
    if (span) {
      traceId = span.traceId;
      parentSpanId = span.parentSpanId;
      sampled = span.sampled;
    }
  }
  const transaction = Sentry.startTransaction({
    name: `${ctx.method} ${ctx.url}`,
    op: "http.server",
    parentSpanId,
    traceId,
    sampled,
  });
  ctx.__sentry_transaction = transaction;
  await next();

  // if using koa router, a nicer way to capture transaction using the matched route
  if (ctx._matchedRoute) {
    const mountPath = ctx.mountPath || "";
    transaction.setName(`${ctx.method} ${mountPath}${ctx._matchedRoute}`);
  }
  transaction.setHttpStatus(ctx.status);
  transaction.finish();
};

app.use(requestHandler);
app.use(tracingMiddleWare);

// usual error handler
app.on("error", (err, ctx) => {
  Sentry.withScope(scope => {
    scope.addEventProcessor(event => {
      return Sentry.Handlers.parseRequest(event, ctx.request);
    });
    Sentry.captureException(err);
  });
});
// the rest of your app

Subsequent Manual Child Spans

The following example creates a span for a part of the code that contains an expensive operation and sends the result to Sentry. You will need to use the transaction stored in the context.

Copied
const myMiddleware = async (ctx, next) => {
  let span;
  const transaction = ctx.__sentry_transaction;
  if (transaction) {
    span = transaction.startChild({
      description: route,
      op: "myMiddleware",
    });
  }
  await myExpensiveOperation();
  if (span) {
    span.finish();
  }
  return next();
};
You can edit this page on GitHub.