-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathapp.js
124 lines (104 loc) · 3.13 KB
/
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
require('dotenv').config()
const Koa = require('koa');
const cors = require('@koa/cors');
const bodyParser = require('koa-bodyparser');
const serve = require('koa-static');
const v1Routes = require('./routes/v1');
const app = new Koa();
const domain = require("domain");
const {
extractTraceparentData,
stripUrlQueryAndFragment,
} = require("@sentry/tracing");
// Configure Sentry
const Sentry = require('@sentry/node');
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV
});
// 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);
app.on("error", (err, ctx) => {
Sentry.withScope((scope) => {
scope.addEventProcessor((event) => {
return Sentry.Handlers.parseRequest(event, ctx.request);
});
Sentry.captureException(err, {
tags: {
environment: process.env.NODE_ENV,
},
});
});
});
app.use(bodyParser());
app.use(cors({
origin: '*',
}));
app.use(serve('public'));
// better error handling
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
console.log(err);
ctx.status = err.status || 500;
ctx.body = { errors: [err] };
ctx.app.emit('error', err, ctx);
}
});
app.use(v1Routes.routes());
module.exports = app.listen(process.env.PORT || 3000);