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
Create and attach a transaction to each request:
Copied
const Sentry = require("@sentry/node");
const {
extractTraceparentData,
Span,
stripUrlQueryAndFragment,
} = 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",
// We recommend adjusting this value in production, or using tracesSampler
// for finer control
tracesSampleRate: 1.0,
});
// 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) => {
const reqMethod = (ctx.method || "").toUpperCase();
const reqUrl = ctx.url && stripUrlQueryAndFragment(ctx.url);
// connect to trace of upstream app
let traceparentData;
if (ctx.request.get("sentry-trace")) {
traceparentData = extractTraceparentData(ctx.request.get("sentry-trace"));
}
const transaction = Sentry.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
Sentry.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();
};
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 Custom 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();
};
- Package:
- npm:@sentry/node
- Version:
- 8.24.0
- Repository:
- https://github.com/getsentry/sentry-javascript